• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #include <arch_helpers.h>
8 #include <assert.h>
9 #include <errno.h>
10 #include <platform.h>
11 #include <psci.h>
12 
13 /*
14  * The platform hooks exported by the platform using the earlier version of
15  * platform interface
16  */
17 const plat_pm_ops_t *pm_ops;
18 
19 /*
20  * The hooks exported by the compatibility layer
21  */
22 static plat_psci_ops_t compat_psci_ops;
23 
24 /*
25  * The secure entry point to be used on warm reset.
26  */
27 static unsigned long secure_entrypoint;
28 
29 /*
30  * This array stores the 'power_state' requests of each CPU during
31  * CPU_SUSPEND and SYSTEM_SUSPEND to support querying of state-ID
32  * by the platform.
33  */
34 unsigned int psci_power_state_compat[PLATFORM_CORE_COUNT];
35 
36 /*******************************************************************************
37  * The PSCI compatibility helper to parse the power state and populate the
38  * 'pwr_domain_state' for each power level. It is assumed that, when in
39  * compatibility mode, the PSCI generic layer need to know only whether the
40  * affinity level will be OFF or in RETENTION and if the platform supports
41  * multiple power down and retention states, it will be taken care within
42  * the platform layer.
43  ******************************************************************************/
parse_power_state(unsigned int power_state,psci_power_state_t * req_state)44 static int parse_power_state(unsigned int power_state,
45 		    psci_power_state_t *req_state)
46 {
47 	int i;
48 	int pstate = psci_get_pstate_type(power_state);
49 	int aff_lvl = psci_get_pstate_pwrlvl(power_state);
50 
51 	if (aff_lvl > PLATFORM_MAX_AFFLVL)
52 		return PSCI_E_INVALID_PARAMS;
53 
54 	/* Sanity check the requested state */
55 	if (pstate == PSTATE_TYPE_STANDBY) {
56 		/*
57 		 * Set the CPU local state as retention and ignore the higher
58 		 * levels. This allows the generic PSCI layer to invoke
59 		 * plat_psci_ops 'cpu_standby' hook and the compatibility
60 		 * layer invokes the 'affinst_standby' handler with the
61 		 * correct power_state parameter thus preserving the correct
62 		 * behavior.
63 		 */
64 		req_state->pwr_domain_state[0] =
65 					PLAT_MAX_RET_STATE;
66 	} else {
67 		for (i = 0; i <= aff_lvl; i++)
68 			req_state->pwr_domain_state[i] =
69 					PLAT_MAX_OFF_STATE;
70 	}
71 
72 	return PSCI_E_SUCCESS;
73 }
74 
75 /*******************************************************************************
76  * The PSCI compatibility helper to set the 'power_state' in
77  * psci_power_state_compat[] at index corresponding to the current core.
78  ******************************************************************************/
set_psci_power_state_compat(unsigned int power_state)79 static void set_psci_power_state_compat(unsigned int power_state)
80 {
81 	unsigned int my_core_pos = plat_my_core_pos();
82 
83 	psci_power_state_compat[my_core_pos] = power_state;
84 	flush_dcache_range((uintptr_t) &psci_power_state_compat[my_core_pos],
85 			sizeof(psci_power_state_compat[my_core_pos]));
86 }
87 
88 /*******************************************************************************
89  * The PSCI compatibility helper for plat_pm_ops_t 'validate_power_state'
90  * hook.
91  ******************************************************************************/
validate_power_state_compat(unsigned int power_state,psci_power_state_t * req_state)92 static int validate_power_state_compat(unsigned int power_state,
93 			    psci_power_state_t *req_state)
94 {
95 	int rc;
96 	assert(req_state);
97 
98 	if (pm_ops->validate_power_state) {
99 		rc = pm_ops->validate_power_state(power_state);
100 		if (rc != PSCI_E_SUCCESS)
101 			return rc;
102 	}
103 
104 	/* Store the 'power_state' parameter for the current CPU. */
105 	set_psci_power_state_compat(power_state);
106 
107 	return parse_power_state(power_state, req_state);
108 }
109 
110 /*******************************************************************************
111  * The PSCI compatibility helper for plat_pm_ops_t
112  * 'get_sys_suspend_power_state' hook.
113  ******************************************************************************/
get_sys_suspend_power_state_compat(psci_power_state_t * req_state)114 void get_sys_suspend_power_state_compat(psci_power_state_t *req_state)
115 {
116 	unsigned int power_state;
117 	assert(req_state);
118 
119 	power_state = pm_ops->get_sys_suspend_power_state();
120 
121 	/* Store the 'power_state' parameter for the current CPU. */
122 	set_psci_power_state_compat(power_state);
123 
124 	if (parse_power_state(power_state, req_state) != PSCI_E_SUCCESS)
125 		assert(0);
126 }
127 
128 /*******************************************************************************
129  * The PSCI compatibility helper for plat_pm_ops_t 'validate_ns_entrypoint'
130  * hook.
131  ******************************************************************************/
validate_ns_entrypoint_compat(uintptr_t ns_entrypoint)132 static int validate_ns_entrypoint_compat(uintptr_t ns_entrypoint)
133 {
134 	return pm_ops->validate_ns_entrypoint(ns_entrypoint);
135 }
136 
137 /*******************************************************************************
138  * The PSCI compatibility helper for plat_pm_ops_t 'affinst_standby' hook.
139  ******************************************************************************/
cpu_standby_compat(plat_local_state_t cpu_state)140 static void cpu_standby_compat(plat_local_state_t cpu_state)
141 {
142 	unsigned int powerstate = psci_get_suspend_powerstate();
143 
144 	assert(powerstate != PSCI_INVALID_DATA);
145 
146 	pm_ops->affinst_standby(powerstate);
147 }
148 
149 /*******************************************************************************
150  * The PSCI compatibility helper for plat_pm_ops_t 'affinst_on' hook.
151  ******************************************************************************/
pwr_domain_on_compat(u_register_t mpidr)152 static int pwr_domain_on_compat(u_register_t mpidr)
153 {
154 	int level, rc;
155 
156 	/*
157 	 * The new PSCI framework does not hold the locks for higher level
158 	 * power domain nodes when this hook is invoked. Hence figuring out the
159 	 * target state of the parent power domains does not make much sense.
160 	 * Hence we hard-code the state as PSCI_STATE_OFF for all the levels.
161 	 * We expect the platform to perform the necessary CPU_ON operations
162 	 * when the 'affinst_on' is invoked only for level 0.
163 	 */
164 	for (level = PLATFORM_MAX_AFFLVL; level >= 0; level--) {
165 		rc = pm_ops->affinst_on((unsigned long)mpidr, secure_entrypoint,
166 					level, PSCI_STATE_OFF);
167 		if (rc != PSCI_E_SUCCESS)
168 			break;
169 	}
170 
171 	return rc;
172 }
173 
174 /*******************************************************************************
175  * The PSCI compatibility helper for plat_pm_ops_t 'affinst_off' hook.
176  ******************************************************************************/
pwr_domain_off_compat(const psci_power_state_t * target_state)177 static void pwr_domain_off_compat(const psci_power_state_t *target_state)
178 {
179 	int level;
180 	unsigned int plat_state;
181 
182 	for (level = 0; level <= PLATFORM_MAX_AFFLVL; level++) {
183 		plat_state = (is_local_state_run(
184 				target_state->pwr_domain_state[level]) ?
185 				PSCI_STATE_ON : PSCI_STATE_OFF);
186 		pm_ops->affinst_off(level, plat_state);
187 	}
188 }
189 
190 /*******************************************************************************
191  * The PSCI compatibility helper for plat_pm_ops_t 'affinst_suspend' hook.
192  ******************************************************************************/
pwr_domain_suspend_compat(const psci_power_state_t * target_state)193 static void pwr_domain_suspend_compat(const psci_power_state_t *target_state)
194 {
195 	int level;
196 	unsigned int plat_state;
197 
198 	for (level = 0; level <= psci_get_suspend_afflvl(); level++) {
199 		plat_state = (is_local_state_run(
200 				target_state->pwr_domain_state[level]) ?
201 				PSCI_STATE_ON : PSCI_STATE_OFF);
202 		pm_ops->affinst_suspend(secure_entrypoint, level, plat_state);
203 	}
204 }
205 
206 /*******************************************************************************
207  * The PSCI compatibility helper for plat_pm_ops_t 'affinst_on_finish'
208  * hook.
209  ******************************************************************************/
pwr_domain_on_finish_compat(const psci_power_state_t * target_state)210 static void pwr_domain_on_finish_compat(const psci_power_state_t *target_state)
211 {
212 	int level;
213 	unsigned int plat_state;
214 
215 	for (level = PLATFORM_MAX_AFFLVL; level >= 0; level--) {
216 		plat_state = (is_local_state_run(
217 				target_state->pwr_domain_state[level]) ?
218 				PSCI_STATE_ON : PSCI_STATE_OFF);
219 		pm_ops->affinst_on_finish(level, plat_state);
220 	}
221 }
222 
223 /*******************************************************************************
224  * The PSCI compatibility helper for plat_pm_ops_t
225  * 'affinst_suspend_finish' hook.
226  ******************************************************************************/
pwr_domain_suspend_finish_compat(const psci_power_state_t * target_state)227 static void pwr_domain_suspend_finish_compat(
228 				const psci_power_state_t *target_state)
229 {
230 	int level;
231 	unsigned int plat_state;
232 
233 	for (level = psci_get_suspend_afflvl(); level >= 0; level--) {
234 		plat_state = (is_local_state_run(
235 				target_state->pwr_domain_state[level]) ?
236 				PSCI_STATE_ON : PSCI_STATE_OFF);
237 		pm_ops->affinst_suspend_finish(level, plat_state);
238 	}
239 }
240 
241 /*******************************************************************************
242  * The PSCI compatibility helper for plat_pm_ops_t 'system_off' hook.
243  ******************************************************************************/
system_off_compat(void)244 static void __dead2 system_off_compat(void)
245 {
246 	pm_ops->system_off();
247 }
248 
249 /*******************************************************************************
250  * The PSCI compatibility helper for plat_pm_ops_t 'system_reset' hook.
251  ******************************************************************************/
system_reset_compat(void)252 static void __dead2 system_reset_compat(void)
253 {
254 	pm_ops->system_reset();
255 }
256 
257 /*******************************************************************************
258  * Export the compatibility compat_psci_ops. The assumption made is that the
259  * power domains correspond to affinity instances on the platform.
260  ******************************************************************************/
plat_setup_psci_ops(uintptr_t sec_entrypoint,const plat_psci_ops_t ** psci_ops)261 int plat_setup_psci_ops(uintptr_t sec_entrypoint,
262 				const plat_psci_ops_t **psci_ops)
263 {
264 	platform_setup_pm(&pm_ops);
265 
266 	secure_entrypoint = (unsigned long) sec_entrypoint;
267 
268 	/*
269 	 * It is compulsory for the platform ports using the new porting
270 	 * interface to export a hook to validate the power state parameter
271 	 */
272 	compat_psci_ops.validate_power_state = validate_power_state_compat;
273 
274 	/*
275 	 * Populate the compatibility plat_psci_ops_t hooks if available
276 	 */
277 	if (pm_ops->validate_ns_entrypoint)
278 		compat_psci_ops.validate_ns_entrypoint =
279 				validate_ns_entrypoint_compat;
280 
281 	if (pm_ops->affinst_standby)
282 		compat_psci_ops.cpu_standby = cpu_standby_compat;
283 
284 	if (pm_ops->affinst_on)
285 		compat_psci_ops.pwr_domain_on = pwr_domain_on_compat;
286 
287 	if (pm_ops->affinst_off)
288 		compat_psci_ops.pwr_domain_off = pwr_domain_off_compat;
289 
290 	if (pm_ops->affinst_suspend)
291 		compat_psci_ops.pwr_domain_suspend = pwr_domain_suspend_compat;
292 
293 	if (pm_ops->affinst_on_finish)
294 		compat_psci_ops.pwr_domain_on_finish =
295 				pwr_domain_on_finish_compat;
296 
297 	if (pm_ops->affinst_suspend_finish)
298 		compat_psci_ops.pwr_domain_suspend_finish =
299 				pwr_domain_suspend_finish_compat;
300 
301 	if (pm_ops->system_off)
302 		compat_psci_ops.system_off = system_off_compat;
303 
304 	if (pm_ops->system_reset)
305 		compat_psci_ops.system_reset = system_reset_compat;
306 
307 	if (pm_ops->get_sys_suspend_power_state)
308 		compat_psci_ops.get_sys_suspend_power_state =
309 				get_sys_suspend_power_state_compat;
310 
311 	*psci_ops = &compat_psci_ops;
312 	return 0;
313 }
314