blob: 6e40ad637acd707921793c96b238345b20f554c6 [file] [log] [blame]
Soby Mathew49473e42015-06-10 13:49:59 +01001/*
2 * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
3 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Soby Mathew49473e42015-06-10 13:49:59 +01005 */
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 */
17const plat_pm_ops_t *pm_ops;
18
19/*
20 * The hooks exported by the compatibility layer
21 */
22static plat_psci_ops_t compat_psci_ops;
23
24/*
25 * The secure entry point to be used on warm reset.
26 */
27static 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 */
34unsigned 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 ******************************************************************************/
44static 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 ******************************************************************************/
79static 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 ******************************************************************************/
92static 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 ******************************************************************************/
114void 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 ******************************************************************************/
132static 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 ******************************************************************************/
140static 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 ******************************************************************************/
152static 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 ******************************************************************************/
177static 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 ******************************************************************************/
193static 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 ******************************************************************************/
210static 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 ******************************************************************************/
227static 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 ******************************************************************************/
244static 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 ******************************************************************************/
252static 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 ******************************************************************************/
261int 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}