xref: /rk3399_ARM-atf/lib/psci/psci_main.c (revision 606b7430077c15695a5b3bcfbad4975f00c9bf95)
1532ed618SSoby Mathew /*
2b41b0824SJayanth Dodderi Chidanand  * Copyright (c) 2013-2022, Arm Limited and Contributors. All rights reserved.
3532ed618SSoby Mathew  *
482cb2c1aSdp-arm  * SPDX-License-Identifier: BSD-3-Clause
5532ed618SSoby Mathew  */
6532ed618SSoby Mathew 
709d40e0eSAntonio Nino Diaz #include <assert.h>
809d40e0eSAntonio Nino Diaz #include <string.h>
909d40e0eSAntonio Nino Diaz 
10532ed618SSoby Mathew #include <arch.h>
11532ed618SSoby Mathew #include <arch_helpers.h>
1209d40e0eSAntonio Nino Diaz #include <common/debug.h>
1309d40e0eSAntonio Nino Diaz #include <lib/pmf/pmf.h>
1409d40e0eSAntonio Nino Diaz #include <lib/runtime_instr.h>
1509d40e0eSAntonio Nino Diaz #include <lib/smccc.h>
1609d40e0eSAntonio Nino Diaz #include <plat/common/platform.h>
1709d40e0eSAntonio Nino Diaz #include <services/arm_arch_svc.h>
1809d40e0eSAntonio Nino Diaz 
19532ed618SSoby Mathew #include "psci_private.h"
20532ed618SSoby Mathew 
21532ed618SSoby Mathew /*******************************************************************************
22532ed618SSoby Mathew  * PSCI frontend api for servicing SMCs. Described in the PSCI spec.
23532ed618SSoby Mathew  ******************************************************************************/
24532ed618SSoby Mathew int psci_cpu_on(u_register_t target_cpu,
25532ed618SSoby Mathew 		uintptr_t entrypoint,
26532ed618SSoby Mathew 		u_register_t context_id)
27532ed618SSoby Mathew 
28532ed618SSoby Mathew {
29532ed618SSoby Mathew 	int rc;
30532ed618SSoby Mathew 	entry_point_info_t ep;
31532ed618SSoby Mathew 
32532ed618SSoby Mathew 	/* Determine if the cpu exists of not */
33532ed618SSoby Mathew 	rc = psci_validate_mpidr(target_cpu);
34532ed618SSoby Mathew 	if (rc != PSCI_E_SUCCESS)
35532ed618SSoby Mathew 		return PSCI_E_INVALID_PARAMS;
36532ed618SSoby Mathew 
37532ed618SSoby Mathew 	/* Validate the entry point and get the entry_point_info */
38532ed618SSoby Mathew 	rc = psci_validate_entry_point(&ep, entrypoint, context_id);
39532ed618SSoby Mathew 	if (rc != PSCI_E_SUCCESS)
40532ed618SSoby Mathew 		return rc;
41532ed618SSoby Mathew 
42532ed618SSoby Mathew 	/*
43532ed618SSoby Mathew 	 * To turn this cpu on, specify which power
44532ed618SSoby Mathew 	 * levels need to be turned on
45532ed618SSoby Mathew 	 */
46532ed618SSoby Mathew 	return psci_cpu_on_start(target_cpu, &ep);
47532ed618SSoby Mathew }
48532ed618SSoby Mathew 
49532ed618SSoby Mathew unsigned int psci_version(void)
50532ed618SSoby Mathew {
51532ed618SSoby Mathew 	return PSCI_MAJOR_VER | PSCI_MINOR_VER;
52532ed618SSoby Mathew }
53532ed618SSoby Mathew 
54532ed618SSoby Mathew int psci_cpu_suspend(unsigned int power_state,
55532ed618SSoby Mathew 		     uintptr_t entrypoint,
56532ed618SSoby Mathew 		     u_register_t context_id)
57532ed618SSoby Mathew {
58532ed618SSoby Mathew 	int rc;
59532ed618SSoby Mathew 	unsigned int target_pwrlvl, is_power_down_state;
60532ed618SSoby Mathew 	entry_point_info_t ep;
61532ed618SSoby Mathew 	psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} };
62532ed618SSoby Mathew 	plat_local_state_t cpu_pd_state;
63*606b7430SWing Li #if PSCI_OS_INIT_MODE
64*606b7430SWing Li 	unsigned int cpu_idx = plat_my_core_pos();
65*606b7430SWing Li 	plat_local_state_t prev[PLAT_MAX_PWR_LVL];
66*606b7430SWing Li #endif
67532ed618SSoby Mathew 
68532ed618SSoby Mathew 	/* Validate the power_state parameter */
69532ed618SSoby Mathew 	rc = psci_validate_power_state(power_state, &state_info);
70532ed618SSoby Mathew 	if (rc != PSCI_E_SUCCESS) {
71532ed618SSoby Mathew 		assert(rc == PSCI_E_INVALID_PARAMS);
72532ed618SSoby Mathew 		return rc;
73532ed618SSoby Mathew 	}
74532ed618SSoby Mathew 
75532ed618SSoby Mathew 	/*
76532ed618SSoby Mathew 	 * Get the value of the state type bit from the power state parameter.
77532ed618SSoby Mathew 	 */
78532ed618SSoby Mathew 	is_power_down_state = psci_get_pstate_type(power_state);
79532ed618SSoby Mathew 
80532ed618SSoby Mathew 	/* Sanity check the requested suspend levels */
81532ed618SSoby Mathew 	assert(psci_validate_suspend_req(&state_info, is_power_down_state)
82532ed618SSoby Mathew 			== PSCI_E_SUCCESS);
83532ed618SSoby Mathew 
84532ed618SSoby Mathew 	target_pwrlvl = psci_find_target_suspend_lvl(&state_info);
85a1c3faa6SSandrine Bailleux 	if (target_pwrlvl == PSCI_INVALID_PWR_LVL) {
86a1c3faa6SSandrine Bailleux 		ERROR("Invalid target power level for suspend operation\n");
87a1c3faa6SSandrine Bailleux 		panic();
88a1c3faa6SSandrine Bailleux 	}
89532ed618SSoby Mathew 
90532ed618SSoby Mathew 	/* Fast path for CPU standby.*/
91362030bfSAntonio Nino Diaz 	if (is_cpu_standby_req(is_power_down_state, target_pwrlvl)) {
926b7b0f36SAntonio Nino Diaz 		if  (psci_plat_pm_ops->cpu_standby == NULL)
93532ed618SSoby Mathew 			return PSCI_E_INVALID_PARAMS;
94532ed618SSoby Mathew 
95532ed618SSoby Mathew 		/*
96532ed618SSoby Mathew 		 * Set the state of the CPU power domain to the platform
97532ed618SSoby Mathew 		 * specific retention state and enter the standby state.
98532ed618SSoby Mathew 		 */
99532ed618SSoby Mathew 		cpu_pd_state = state_info.pwr_domain_state[PSCI_CPU_PWR_LVL];
100532ed618SSoby Mathew 		psci_set_cpu_local_state(cpu_pd_state);
101532ed618SSoby Mathew 
102*606b7430SWing Li #if PSCI_OS_INIT_MODE
103*606b7430SWing Li 		/*
104*606b7430SWing Li 		 * If in OS-initiated mode, save a copy of the previous
105*606b7430SWing Li 		 * requested local power states and update the new requested
106*606b7430SWing Li 		 * local power states for this CPU.
107*606b7430SWing Li 		 */
108*606b7430SWing Li 		if (psci_suspend_mode == OS_INIT) {
109*606b7430SWing Li 			psci_update_req_local_pwr_states(target_pwrlvl, cpu_idx,
110*606b7430SWing Li 							 &state_info, prev);
111*606b7430SWing Li 		}
112*606b7430SWing Li #endif
113*606b7430SWing Li 
114532ed618SSoby Mathew #if ENABLE_PSCI_STAT
11504c1db1eSdp-arm 		plat_psci_stat_accounting_start(&state_info);
116532ed618SSoby Mathew #endif
117532ed618SSoby Mathew 
118872be88aSdp-arm #if ENABLE_RUNTIME_INSTRUMENTATION
119872be88aSdp-arm 		PMF_CAPTURE_TIMESTAMP(rt_instr_svc,
120872be88aSdp-arm 		    RT_INSTR_ENTER_HW_LOW_PWR,
121872be88aSdp-arm 		    PMF_NO_CACHE_MAINT);
122872be88aSdp-arm #endif
123872be88aSdp-arm 
124532ed618SSoby Mathew 		psci_plat_pm_ops->cpu_standby(cpu_pd_state);
125532ed618SSoby Mathew 
126532ed618SSoby Mathew 		/* Upon exit from standby, set the state back to RUN. */
127532ed618SSoby Mathew 		psci_set_cpu_local_state(PSCI_LOCAL_STATE_RUN);
128532ed618SSoby Mathew 
129*606b7430SWing Li #if PSCI_OS_INIT_MODE
130*606b7430SWing Li 		/*
131*606b7430SWing Li 		 * If in OS-initiated mode, restore the previous requested
132*606b7430SWing Li 		 * local power states for this CPU.
133*606b7430SWing Li 		 */
134*606b7430SWing Li 		if (psci_suspend_mode == OS_INIT) {
135*606b7430SWing Li 			psci_restore_req_local_pwr_states(cpu_idx, prev);
136*606b7430SWing Li 		}
137*606b7430SWing Li #endif
138*606b7430SWing Li 
139872be88aSdp-arm #if ENABLE_RUNTIME_INSTRUMENTATION
140872be88aSdp-arm 		PMF_CAPTURE_TIMESTAMP(rt_instr_svc,
141872be88aSdp-arm 		    RT_INSTR_EXIT_HW_LOW_PWR,
142872be88aSdp-arm 		    PMF_NO_CACHE_MAINT);
143872be88aSdp-arm #endif
144872be88aSdp-arm 
145532ed618SSoby Mathew #if ENABLE_PSCI_STAT
14604c1db1eSdp-arm 		plat_psci_stat_accounting_stop(&state_info);
147532ed618SSoby Mathew 
148532ed618SSoby Mathew 		/* Update PSCI stats */
14904c1db1eSdp-arm 		psci_stats_update_pwr_up(PSCI_CPU_PWR_LVL, &state_info);
150532ed618SSoby Mathew #endif
151532ed618SSoby Mathew 
152532ed618SSoby Mathew 		return PSCI_E_SUCCESS;
153532ed618SSoby Mathew 	}
154532ed618SSoby Mathew 
155532ed618SSoby Mathew 	/*
156532ed618SSoby Mathew 	 * If a power down state has been requested, we need to verify entry
157532ed618SSoby Mathew 	 * point and program entry information.
158532ed618SSoby Mathew 	 */
1596b7b0f36SAntonio Nino Diaz 	if (is_power_down_state != 0U) {
160532ed618SSoby Mathew 		rc = psci_validate_entry_point(&ep, entrypoint, context_id);
161532ed618SSoby Mathew 		if (rc != PSCI_E_SUCCESS)
162532ed618SSoby Mathew 			return rc;
163532ed618SSoby Mathew 	}
164532ed618SSoby Mathew 
165532ed618SSoby Mathew 	/*
166532ed618SSoby Mathew 	 * Do what is needed to enter the power down state. Upon success,
167532ed618SSoby Mathew 	 * enter the final wfi which will power down this CPU. This function
168532ed618SSoby Mathew 	 * might return if the power down was abandoned for any reason, e.g.
169532ed618SSoby Mathew 	 * arrival of an interrupt
170532ed618SSoby Mathew 	 */
171*606b7430SWing Li 	rc = psci_cpu_suspend_start(&ep,
172532ed618SSoby Mathew 				    target_pwrlvl,
173532ed618SSoby Mathew 				    &state_info,
174532ed618SSoby Mathew 				    is_power_down_state);
175532ed618SSoby Mathew 
176*606b7430SWing Li 	return rc;
177532ed618SSoby Mathew }
178532ed618SSoby Mathew 
179532ed618SSoby Mathew 
180532ed618SSoby Mathew int psci_system_suspend(uintptr_t entrypoint, u_register_t context_id)
181532ed618SSoby Mathew {
182532ed618SSoby Mathew 	int rc;
183532ed618SSoby Mathew 	psci_power_state_t state_info;
184532ed618SSoby Mathew 	entry_point_info_t ep;
185532ed618SSoby Mathew 
186532ed618SSoby Mathew 	/* Check if the current CPU is the last ON CPU in the system */
187b41b0824SJayanth Dodderi Chidanand 	if (!psci_is_last_on_cpu())
188532ed618SSoby Mathew 		return PSCI_E_DENIED;
189532ed618SSoby Mathew 
190532ed618SSoby Mathew 	/* Validate the entry point and get the entry_point_info */
191532ed618SSoby Mathew 	rc = psci_validate_entry_point(&ep, entrypoint, context_id);
192532ed618SSoby Mathew 	if (rc != PSCI_E_SUCCESS)
193532ed618SSoby Mathew 		return rc;
194532ed618SSoby Mathew 
195532ed618SSoby Mathew 	/* Query the psci_power_state for system suspend */
196532ed618SSoby Mathew 	psci_query_sys_suspend_pwrstate(&state_info);
197532ed618SSoby Mathew 
198a4065abdSldts 	/*
199a4065abdSldts 	 * Check if platform allows suspend to Highest power level
200a4065abdSldts 	 * (System level)
201a4065abdSldts 	 */
202a4065abdSldts 	if (psci_find_target_suspend_lvl(&state_info) < PLAT_MAX_PWR_LVL)
203a4065abdSldts 		return PSCI_E_DENIED;
204a4065abdSldts 
205532ed618SSoby Mathew 	/* Ensure that the psci_power_state makes sense */
206532ed618SSoby Mathew 	assert(psci_validate_suspend_req(&state_info, PSTATE_TYPE_POWERDOWN)
207532ed618SSoby Mathew 						== PSCI_E_SUCCESS);
2086b7b0f36SAntonio Nino Diaz 	assert(is_local_state_off(
2096b7b0f36SAntonio Nino Diaz 			state_info.pwr_domain_state[PLAT_MAX_PWR_LVL]) != 0);
210532ed618SSoby Mathew 
211532ed618SSoby Mathew 	/*
212532ed618SSoby Mathew 	 * Do what is needed to enter the system suspend state. This function
213532ed618SSoby Mathew 	 * might return if the power down was abandoned for any reason, e.g.
214532ed618SSoby Mathew 	 * arrival of an interrupt
215532ed618SSoby Mathew 	 */
216*606b7430SWing Li 	rc = psci_cpu_suspend_start(&ep,
217532ed618SSoby Mathew 				    PLAT_MAX_PWR_LVL,
218532ed618SSoby Mathew 				    &state_info,
219532ed618SSoby Mathew 				    PSTATE_TYPE_POWERDOWN);
220532ed618SSoby Mathew 
221*606b7430SWing Li 	return rc;
222532ed618SSoby Mathew }
223532ed618SSoby Mathew 
224532ed618SSoby Mathew int psci_cpu_off(void)
225532ed618SSoby Mathew {
226532ed618SSoby Mathew 	int rc;
227532ed618SSoby Mathew 	unsigned int target_pwrlvl = PLAT_MAX_PWR_LVL;
228532ed618SSoby Mathew 
229532ed618SSoby Mathew 	/*
230532ed618SSoby Mathew 	 * Do what is needed to power off this CPU and possible higher power
231532ed618SSoby Mathew 	 * levels if it able to do so. Upon success, enter the final wfi
232532ed618SSoby Mathew 	 * which will power down this CPU.
233532ed618SSoby Mathew 	 */
234532ed618SSoby Mathew 	rc = psci_do_cpu_off(target_pwrlvl);
235532ed618SSoby Mathew 
236532ed618SSoby Mathew 	/*
237532ed618SSoby Mathew 	 * The only error cpu_off can return is E_DENIED. So check if that's
238532ed618SSoby Mathew 	 * indeed the case.
239532ed618SSoby Mathew 	 */
240532ed618SSoby Mathew 	assert(rc == PSCI_E_DENIED);
241532ed618SSoby Mathew 
242532ed618SSoby Mathew 	return rc;
243532ed618SSoby Mathew }
244532ed618SSoby Mathew 
245532ed618SSoby Mathew int psci_affinity_info(u_register_t target_affinity,
246532ed618SSoby Mathew 		       unsigned int lowest_affinity_level)
247532ed618SSoby Mathew {
2485b33ad17SDeepika Bhavnani 	int ret;
2495b33ad17SDeepika Bhavnani 	unsigned int target_idx;
250532ed618SSoby Mathew 
251532ed618SSoby Mathew 	/* We dont support level higher than PSCI_CPU_PWR_LVL */
252532ed618SSoby Mathew 	if (lowest_affinity_level > PSCI_CPU_PWR_LVL)
253532ed618SSoby Mathew 		return PSCI_E_INVALID_PARAMS;
254532ed618SSoby Mathew 
255532ed618SSoby Mathew 	/* Calculate the cpu index of the target */
2565b33ad17SDeepika Bhavnani 	ret = plat_core_pos_by_mpidr(target_affinity);
2575b33ad17SDeepika Bhavnani 	if (ret == -1) {
258532ed618SSoby Mathew 		return PSCI_E_INVALID_PARAMS;
2595b33ad17SDeepika Bhavnani 	}
2605b33ad17SDeepika Bhavnani 	target_idx = (unsigned int)ret;
261532ed618SSoby Mathew 
2628fd307ffSRoberto Vargas 	/*
2638fd307ffSRoberto Vargas 	 * Generic management:
2648fd307ffSRoberto Vargas 	 * Perform cache maintanence ahead of reading the target CPU state to
2658fd307ffSRoberto Vargas 	 * ensure that the data is not stale.
2668fd307ffSRoberto Vargas 	 * There is a theoretical edge case where the cache may contain stale
2678fd307ffSRoberto Vargas 	 * data for the target CPU data - this can occur under the following
2688fd307ffSRoberto Vargas 	 * conditions:
2698fd307ffSRoberto Vargas 	 * - the target CPU is in another cluster from the current
2708fd307ffSRoberto Vargas 	 * - the target CPU was the last CPU to shutdown on its cluster
2718fd307ffSRoberto Vargas 	 * - the cluster was removed from coherency as part of the CPU shutdown
2728fd307ffSRoberto Vargas 	 *
2738fd307ffSRoberto Vargas 	 * In this case the cache maintenace that was performed as part of the
2748fd307ffSRoberto Vargas 	 * target CPUs shutdown was not seen by the current CPU's cluster. And
2758fd307ffSRoberto Vargas 	 * so the cache may contain stale data for the target CPU.
2768fd307ffSRoberto Vargas 	 */
2775b33ad17SDeepika Bhavnani 	flush_cpu_data_by_index(target_idx,
2786b7b0f36SAntonio Nino Diaz 				psci_svc_cpu_data.aff_info_state);
2798fd307ffSRoberto Vargas 
280532ed618SSoby Mathew 	return psci_get_aff_info_state_by_idx(target_idx);
281532ed618SSoby Mathew }
282532ed618SSoby Mathew 
283532ed618SSoby Mathew int psci_migrate(u_register_t target_cpu)
284532ed618SSoby Mathew {
285532ed618SSoby Mathew 	int rc;
286532ed618SSoby Mathew 	u_register_t resident_cpu_mpidr;
287532ed618SSoby Mathew 
288532ed618SSoby Mathew 	rc = psci_spd_migrate_info(&resident_cpu_mpidr);
289532ed618SSoby Mathew 	if (rc != PSCI_TOS_UP_MIG_CAP)
290532ed618SSoby Mathew 		return (rc == PSCI_TOS_NOT_UP_MIG_CAP) ?
291532ed618SSoby Mathew 			  PSCI_E_DENIED : PSCI_E_NOT_SUPPORTED;
292532ed618SSoby Mathew 
293532ed618SSoby Mathew 	/*
294532ed618SSoby Mathew 	 * Migrate should only be invoked on the CPU where
295532ed618SSoby Mathew 	 * the Secure OS is resident.
296532ed618SSoby Mathew 	 */
297532ed618SSoby Mathew 	if (resident_cpu_mpidr != read_mpidr_el1())
298532ed618SSoby Mathew 		return PSCI_E_NOT_PRESENT;
299532ed618SSoby Mathew 
300532ed618SSoby Mathew 	/* Check the validity of the specified target cpu */
301532ed618SSoby Mathew 	rc = psci_validate_mpidr(target_cpu);
302532ed618SSoby Mathew 	if (rc != PSCI_E_SUCCESS)
303532ed618SSoby Mathew 		return PSCI_E_INVALID_PARAMS;
304532ed618SSoby Mathew 
3056b7b0f36SAntonio Nino Diaz 	assert((psci_spd_pm != NULL) && (psci_spd_pm->svc_migrate != NULL));
306532ed618SSoby Mathew 
307532ed618SSoby Mathew 	rc = psci_spd_pm->svc_migrate(read_mpidr_el1(), target_cpu);
3086b7b0f36SAntonio Nino Diaz 	assert((rc == PSCI_E_SUCCESS) || (rc == PSCI_E_INTERN_FAIL));
309532ed618SSoby Mathew 
310532ed618SSoby Mathew 	return rc;
311532ed618SSoby Mathew }
312532ed618SSoby Mathew 
313532ed618SSoby Mathew int psci_migrate_info_type(void)
314532ed618SSoby Mathew {
315532ed618SSoby Mathew 	u_register_t resident_cpu_mpidr;
316532ed618SSoby Mathew 
317532ed618SSoby Mathew 	return psci_spd_migrate_info(&resident_cpu_mpidr);
318532ed618SSoby Mathew }
319532ed618SSoby Mathew 
3206b7b0f36SAntonio Nino Diaz u_register_t psci_migrate_info_up_cpu(void)
321532ed618SSoby Mathew {
322532ed618SSoby Mathew 	u_register_t resident_cpu_mpidr;
323532ed618SSoby Mathew 	int rc;
324532ed618SSoby Mathew 
325532ed618SSoby Mathew 	/*
326532ed618SSoby Mathew 	 * Return value of this depends upon what
327532ed618SSoby Mathew 	 * psci_spd_migrate_info() returns.
328532ed618SSoby Mathew 	 */
329532ed618SSoby Mathew 	rc = psci_spd_migrate_info(&resident_cpu_mpidr);
3306b7b0f36SAntonio Nino Diaz 	if ((rc != PSCI_TOS_NOT_UP_MIG_CAP) && (rc != PSCI_TOS_UP_MIG_CAP))
3316b7b0f36SAntonio Nino Diaz 		return (u_register_t)(register_t) PSCI_E_INVALID_PARAMS;
332532ed618SSoby Mathew 
333532ed618SSoby Mathew 	return resident_cpu_mpidr;
334532ed618SSoby Mathew }
335532ed618SSoby Mathew 
33628d3d614SJeenu Viswambharan int psci_node_hw_state(u_register_t target_cpu,
33728d3d614SJeenu Viswambharan 		       unsigned int power_level)
33828d3d614SJeenu Viswambharan {
33928d3d614SJeenu Viswambharan 	int rc;
34028d3d614SJeenu Viswambharan 
34128d3d614SJeenu Viswambharan 	/* Validate target_cpu */
34228d3d614SJeenu Viswambharan 	rc = psci_validate_mpidr(target_cpu);
34328d3d614SJeenu Viswambharan 	if (rc != PSCI_E_SUCCESS)
34428d3d614SJeenu Viswambharan 		return PSCI_E_INVALID_PARAMS;
34528d3d614SJeenu Viswambharan 
34628d3d614SJeenu Viswambharan 	/* Validate power_level against PLAT_MAX_PWR_LVL */
34728d3d614SJeenu Viswambharan 	if (power_level > PLAT_MAX_PWR_LVL)
34828d3d614SJeenu Viswambharan 		return PSCI_E_INVALID_PARAMS;
34928d3d614SJeenu Viswambharan 
35028d3d614SJeenu Viswambharan 	/*
35128d3d614SJeenu Viswambharan 	 * Dispatch this call to platform to query power controller, and pass on
35228d3d614SJeenu Viswambharan 	 * to the caller what it returns
35328d3d614SJeenu Viswambharan 	 */
3546b7b0f36SAntonio Nino Diaz 	assert(psci_plat_pm_ops->get_node_hw_state != NULL);
35528d3d614SJeenu Viswambharan 	rc = psci_plat_pm_ops->get_node_hw_state(target_cpu, power_level);
3566b7b0f36SAntonio Nino Diaz 	assert(((rc >= HW_ON) && (rc <= HW_STANDBY))
3576b7b0f36SAntonio Nino Diaz 		|| (rc == PSCI_E_NOT_SUPPORTED)
3586b7b0f36SAntonio Nino Diaz 		|| (rc == PSCI_E_INVALID_PARAMS));
35928d3d614SJeenu Viswambharan 	return rc;
36028d3d614SJeenu Viswambharan }
36128d3d614SJeenu Viswambharan 
362532ed618SSoby Mathew int psci_features(unsigned int psci_fid)
363532ed618SSoby Mathew {
364532ed618SSoby Mathew 	unsigned int local_caps = psci_caps;
365532ed618SSoby Mathew 
3666eabbb07SDimitris Papastamos 	if (psci_fid == SMCCC_VERSION)
3676eabbb07SDimitris Papastamos 		return PSCI_E_SUCCESS;
3686eabbb07SDimitris Papastamos 
369532ed618SSoby Mathew 	/* Check if it is a 64 bit function */
370532ed618SSoby Mathew 	if (((psci_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_64)
371532ed618SSoby Mathew 		local_caps &= PSCI_CAP_64BIT_MASK;
372532ed618SSoby Mathew 
373532ed618SSoby Mathew 	/* Check for invalid fid */
374532ed618SSoby Mathew 	if (!(is_std_svc_call(psci_fid) && is_valid_fast_smc(psci_fid)
375532ed618SSoby Mathew 			&& is_psci_fid(psci_fid)))
376532ed618SSoby Mathew 		return PSCI_E_NOT_SUPPORTED;
377532ed618SSoby Mathew 
378532ed618SSoby Mathew 
379532ed618SSoby Mathew 	/* Check if the psci fid is supported or not */
3806b7b0f36SAntonio Nino Diaz 	if ((local_caps & define_psci_cap(psci_fid)) == 0U)
381532ed618SSoby Mathew 		return PSCI_E_NOT_SUPPORTED;
382532ed618SSoby Mathew 
383532ed618SSoby Mathew 	/* Format the feature flags */
3846b7b0f36SAntonio Nino Diaz 	if ((psci_fid == PSCI_CPU_SUSPEND_AARCH32) ||
3856b7b0f36SAntonio Nino Diaz 	    (psci_fid == PSCI_CPU_SUSPEND_AARCH64)) {
386532ed618SSoby Mathew 		/*
387532ed618SSoby Mathew 		 * The trusted firmware does not support OS Initiated Mode.
388532ed618SSoby Mathew 		 */
3896b7b0f36SAntonio Nino Diaz 		unsigned int ret = ((FF_PSTATE << FF_PSTATE_SHIFT) |
3906b7b0f36SAntonio Nino Diaz 			(((FF_SUPPORTS_OS_INIT_MODE == 1U) ? 0U : 1U)
3916b7b0f36SAntonio Nino Diaz 				<< FF_MODE_SUPPORT_SHIFT));
3926b7b0f36SAntonio Nino Diaz 		return (int) ret;
393532ed618SSoby Mathew 	}
394532ed618SSoby Mathew 
395532ed618SSoby Mathew 	/* Return 0 for all other fid's */
396532ed618SSoby Mathew 	return PSCI_E_SUCCESS;
397532ed618SSoby Mathew }
398532ed618SSoby Mathew 
399b88a4416SWing Li #if PSCI_OS_INIT_MODE
400b88a4416SWing Li int psci_set_suspend_mode(unsigned int mode)
401b88a4416SWing Li {
402b88a4416SWing Li 	if (psci_suspend_mode == mode) {
403b88a4416SWing Li 		return PSCI_E_SUCCESS;
404b88a4416SWing Li 	}
405b88a4416SWing Li 
406b88a4416SWing Li 	if (mode == PLAT_COORD) {
407b88a4416SWing Li 		/* Check if the current CPU is the last ON CPU in the system */
408b88a4416SWing Li 		if (!psci_is_last_on_cpu_safe()) {
409b88a4416SWing Li 			return PSCI_E_DENIED;
410b88a4416SWing Li 		}
411b88a4416SWing Li 	}
412b88a4416SWing Li 
413b88a4416SWing Li 	if (mode == OS_INIT) {
414b88a4416SWing Li 		/*
415b88a4416SWing Li 		 * Check if all CPUs in the system are ON or if the current
416b88a4416SWing Li 		 * CPU is the last ON CPU in the system.
417b88a4416SWing Li 		 */
418b88a4416SWing Li 		if (!(psci_are_all_cpus_on_safe() ||
419b88a4416SWing Li 		      psci_is_last_on_cpu_safe())) {
420b88a4416SWing Li 			return PSCI_E_DENIED;
421b88a4416SWing Li 		}
422b88a4416SWing Li 	}
423b88a4416SWing Li 
424b88a4416SWing Li 	psci_suspend_mode = mode;
425b88a4416SWing Li 	psci_flush_dcache_range((uintptr_t)&psci_suspend_mode,
426b88a4416SWing Li 				sizeof(psci_suspend_mode));
427b88a4416SWing Li 
428b88a4416SWing Li 	return PSCI_E_SUCCESS;
429b88a4416SWing Li }
430b88a4416SWing Li #endif
431b88a4416SWing Li 
432532ed618SSoby Mathew /*******************************************************************************
433532ed618SSoby Mathew  * PSCI top level handler for servicing SMCs.
434532ed618SSoby Mathew  ******************************************************************************/
435cf0b1492SSoby Mathew u_register_t psci_smc_handler(uint32_t smc_fid,
436532ed618SSoby Mathew 			  u_register_t x1,
437532ed618SSoby Mathew 			  u_register_t x2,
438532ed618SSoby Mathew 			  u_register_t x3,
439532ed618SSoby Mathew 			  u_register_t x4,
440532ed618SSoby Mathew 			  void *cookie,
441532ed618SSoby Mathew 			  void *handle,
442532ed618SSoby Mathew 			  u_register_t flags)
443532ed618SSoby Mathew {
4446b7b0f36SAntonio Nino Diaz 	u_register_t ret;
4456b7b0f36SAntonio Nino Diaz 
446532ed618SSoby Mathew 	if (is_caller_secure(flags))
4476b7b0f36SAntonio Nino Diaz 		return (u_register_t)SMC_UNK;
448532ed618SSoby Mathew 
449532ed618SSoby Mathew 	/* Check the fid against the capabilities */
4506b7b0f36SAntonio Nino Diaz 	if ((psci_caps & define_psci_cap(smc_fid)) == 0U)
4516b7b0f36SAntonio Nino Diaz 		return (u_register_t)SMC_UNK;
452532ed618SSoby Mathew 
453532ed618SSoby Mathew 	if (((smc_fid >> FUNCID_CC_SHIFT) & FUNCID_CC_MASK) == SMC_32) {
454532ed618SSoby Mathew 		/* 32-bit PSCI function, clear top parameter bits */
455532ed618SSoby Mathew 
4566b7b0f36SAntonio Nino Diaz 		uint32_t r1 = (uint32_t)x1;
4576b7b0f36SAntonio Nino Diaz 		uint32_t r2 = (uint32_t)x2;
4586b7b0f36SAntonio Nino Diaz 		uint32_t r3 = (uint32_t)x3;
459532ed618SSoby Mathew 
460532ed618SSoby Mathew 		switch (smc_fid) {
461532ed618SSoby Mathew 		case PSCI_VERSION:
4626b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)psci_version();
4636b7b0f36SAntonio Nino Diaz 			break;
464532ed618SSoby Mathew 
465532ed618SSoby Mathew 		case PSCI_CPU_OFF:
4666b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)psci_cpu_off();
4676b7b0f36SAntonio Nino Diaz 			break;
468532ed618SSoby Mathew 
469532ed618SSoby Mathew 		case PSCI_CPU_SUSPEND_AARCH32:
4706b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)psci_cpu_suspend(r1, r2, r3);
4716b7b0f36SAntonio Nino Diaz 			break;
472532ed618SSoby Mathew 
473532ed618SSoby Mathew 		case PSCI_CPU_ON_AARCH32:
4746b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)psci_cpu_on(r1, r2, r3);
4756b7b0f36SAntonio Nino Diaz 			break;
476532ed618SSoby Mathew 
477532ed618SSoby Mathew 		case PSCI_AFFINITY_INFO_AARCH32:
4786b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)psci_affinity_info(r1, r2);
4796b7b0f36SAntonio Nino Diaz 			break;
480532ed618SSoby Mathew 
481532ed618SSoby Mathew 		case PSCI_MIG_AARCH32:
4826b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)psci_migrate(r1);
4836b7b0f36SAntonio Nino Diaz 			break;
484532ed618SSoby Mathew 
485532ed618SSoby Mathew 		case PSCI_MIG_INFO_TYPE:
4866b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)psci_migrate_info_type();
4876b7b0f36SAntonio Nino Diaz 			break;
488532ed618SSoby Mathew 
489532ed618SSoby Mathew 		case PSCI_MIG_INFO_UP_CPU_AARCH32:
4906b7b0f36SAntonio Nino Diaz 			ret = psci_migrate_info_up_cpu();
4916b7b0f36SAntonio Nino Diaz 			break;
492532ed618SSoby Mathew 
49328d3d614SJeenu Viswambharan 		case PSCI_NODE_HW_STATE_AARCH32:
4946b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)psci_node_hw_state(r1, r2);
4956b7b0f36SAntonio Nino Diaz 			break;
49628d3d614SJeenu Viswambharan 
497532ed618SSoby Mathew 		case PSCI_SYSTEM_SUSPEND_AARCH32:
4986b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)psci_system_suspend(r1, r2);
4996b7b0f36SAntonio Nino Diaz 			break;
500532ed618SSoby Mathew 
501532ed618SSoby Mathew 		case PSCI_SYSTEM_OFF:
502532ed618SSoby Mathew 			psci_system_off();
503532ed618SSoby Mathew 			/* We should never return from psci_system_off() */
5043eacacc0SJonathan Wright 			break;
505532ed618SSoby Mathew 
506532ed618SSoby Mathew 		case PSCI_SYSTEM_RESET:
507532ed618SSoby Mathew 			psci_system_reset();
508532ed618SSoby Mathew 			/* We should never return from psci_system_reset() */
5093eacacc0SJonathan Wright 			break;
510532ed618SSoby Mathew 
511532ed618SSoby Mathew 		case PSCI_FEATURES:
5126b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)psci_features(r1);
5136b7b0f36SAntonio Nino Diaz 			break;
514532ed618SSoby Mathew 
515b88a4416SWing Li #if PSCI_OS_INIT_MODE
516b88a4416SWing Li 		case PSCI_SET_SUSPEND_MODE:
517b88a4416SWing Li 			ret = (u_register_t)psci_set_suspend_mode(r1);
518b88a4416SWing Li 			break;
519b88a4416SWing Li #endif
520b88a4416SWing Li 
521532ed618SSoby Mathew #if ENABLE_PSCI_STAT
522532ed618SSoby Mathew 		case PSCI_STAT_RESIDENCY_AARCH32:
5236b7b0f36SAntonio Nino Diaz 			ret = psci_stat_residency(r1, r2);
5246b7b0f36SAntonio Nino Diaz 			break;
525532ed618SSoby Mathew 
526532ed618SSoby Mathew 		case PSCI_STAT_COUNT_AARCH32:
5276b7b0f36SAntonio Nino Diaz 			ret = psci_stat_count(r1, r2);
5286b7b0f36SAntonio Nino Diaz 			break;
529532ed618SSoby Mathew #endif
530d4c596beSRoberto Vargas 		case PSCI_MEM_PROTECT:
5316b7b0f36SAntonio Nino Diaz 			ret = psci_mem_protect(r1);
5326b7b0f36SAntonio Nino Diaz 			break;
533d4c596beSRoberto Vargas 
534d4c596beSRoberto Vargas 		case PSCI_MEM_CHK_RANGE_AARCH32:
5356b7b0f36SAntonio Nino Diaz 			ret = psci_mem_chk_range(r1, r2);
5366b7b0f36SAntonio Nino Diaz 			break;
537532ed618SSoby Mathew 
53836a8f8fdSRoberto Vargas 		case PSCI_SYSTEM_RESET2_AARCH32:
53936a8f8fdSRoberto Vargas 			/* We should never return from psci_system_reset2() */
5406b7b0f36SAntonio Nino Diaz 			ret = psci_system_reset2(r1, r2);
5416b7b0f36SAntonio Nino Diaz 			break;
54236a8f8fdSRoberto Vargas 
543532ed618SSoby Mathew 		default:
5446b7b0f36SAntonio Nino Diaz 			WARN("Unimplemented PSCI Call: 0x%x\n", smc_fid);
5456b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)SMC_UNK;
546532ed618SSoby Mathew 			break;
547532ed618SSoby Mathew 		}
548532ed618SSoby Mathew 	} else {
549532ed618SSoby Mathew 		/* 64-bit PSCI function */
550532ed618SSoby Mathew 
551532ed618SSoby Mathew 		switch (smc_fid) {
552532ed618SSoby Mathew 		case PSCI_CPU_SUSPEND_AARCH64:
5536b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)
5546b7b0f36SAntonio Nino Diaz 				psci_cpu_suspend((unsigned int)x1, x2, x3);
5556b7b0f36SAntonio Nino Diaz 			break;
556532ed618SSoby Mathew 
557532ed618SSoby Mathew 		case PSCI_CPU_ON_AARCH64:
5586b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)psci_cpu_on(x1, x2, x3);
5596b7b0f36SAntonio Nino Diaz 			break;
560532ed618SSoby Mathew 
561532ed618SSoby Mathew 		case PSCI_AFFINITY_INFO_AARCH64:
5626b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)
5636b7b0f36SAntonio Nino Diaz 				psci_affinity_info(x1, (unsigned int)x2);
5646b7b0f36SAntonio Nino Diaz 			break;
565532ed618SSoby Mathew 
566532ed618SSoby Mathew 		case PSCI_MIG_AARCH64:
5676b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)psci_migrate(x1);
5686b7b0f36SAntonio Nino Diaz 			break;
569532ed618SSoby Mathew 
570532ed618SSoby Mathew 		case PSCI_MIG_INFO_UP_CPU_AARCH64:
5716b7b0f36SAntonio Nino Diaz 			ret = psci_migrate_info_up_cpu();
5726b7b0f36SAntonio Nino Diaz 			break;
573532ed618SSoby Mathew 
57428d3d614SJeenu Viswambharan 		case PSCI_NODE_HW_STATE_AARCH64:
5756b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)psci_node_hw_state(
5766b7b0f36SAntonio Nino Diaz 					x1, (unsigned int) x2);
5776b7b0f36SAntonio Nino Diaz 			break;
57828d3d614SJeenu Viswambharan 
579532ed618SSoby Mathew 		case PSCI_SYSTEM_SUSPEND_AARCH64:
5806b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)psci_system_suspend(x1, x2);
5816b7b0f36SAntonio Nino Diaz 			break;
582532ed618SSoby Mathew 
583b88a4416SWing Li #if PSCI_OS_INIT_MODE
584b88a4416SWing Li 		case PSCI_SET_SUSPEND_MODE:
585b88a4416SWing Li 			ret = (u_register_t)psci_set_suspend_mode(x1);
586b88a4416SWing Li 			break;
587b88a4416SWing Li #endif
588b88a4416SWing Li 
589532ed618SSoby Mathew #if ENABLE_PSCI_STAT
590532ed618SSoby Mathew 		case PSCI_STAT_RESIDENCY_AARCH64:
5916b7b0f36SAntonio Nino Diaz 			ret = psci_stat_residency(x1, (unsigned int) x2);
5926b7b0f36SAntonio Nino Diaz 			break;
593532ed618SSoby Mathew 
594532ed618SSoby Mathew 		case PSCI_STAT_COUNT_AARCH64:
5956b7b0f36SAntonio Nino Diaz 			ret = psci_stat_count(x1, (unsigned int) x2);
5966b7b0f36SAntonio Nino Diaz 			break;
597532ed618SSoby Mathew #endif
598532ed618SSoby Mathew 
599d4c596beSRoberto Vargas 		case PSCI_MEM_CHK_RANGE_AARCH64:
6006b7b0f36SAntonio Nino Diaz 			ret = psci_mem_chk_range(x1, x2);
6016b7b0f36SAntonio Nino Diaz 			break;
602d4c596beSRoberto Vargas 
60336a8f8fdSRoberto Vargas 		case PSCI_SYSTEM_RESET2_AARCH64:
60436a8f8fdSRoberto Vargas 			/* We should never return from psci_system_reset2() */
6056b7b0f36SAntonio Nino Diaz 			ret = psci_system_reset2((uint32_t) x1, x2);
6066b7b0f36SAntonio Nino Diaz 			break;
607d4c596beSRoberto Vargas 
608532ed618SSoby Mathew 		default:
6096b7b0f36SAntonio Nino Diaz 			WARN("Unimplemented PSCI Call: 0x%x\n", smc_fid);
6106b7b0f36SAntonio Nino Diaz 			ret = (u_register_t)SMC_UNK;
611532ed618SSoby Mathew 			break;
612532ed618SSoby Mathew 		}
613532ed618SSoby Mathew 	}
614532ed618SSoby Mathew 
6156b7b0f36SAntonio Nino Diaz 	return ret;
616532ed618SSoby Mathew }
617