blob: f51bb5586ecd243fad5e176b59cddb31cffcdb84 [file] [log] [blame]
Soby Mathew49473e42015-06-10 13:49:59 +01001/*
2 * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are met:
6 *
7 * Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * Redistributions in binary form must reproduce the above copyright notice,
11 * this list of conditions and the following disclaimer in the documentation
12 * and/or other materials provided with the distribution.
13 *
14 * Neither the name of ARM nor the names of its contributors may be used
15 * to endorse or promote products derived from this software without specific
16 * prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28 * POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include <arch_helpers.h>
32#include <assert.h>
33#include <errno.h>
34#include <platform.h>
35#include <psci.h>
36
37/*
38 * The platform hooks exported by the platform using the earlier version of
39 * platform interface
40 */
41const plat_pm_ops_t *pm_ops;
42
43/*
44 * The hooks exported by the compatibility layer
45 */
46static plat_psci_ops_t compat_psci_ops;
47
48/*
49 * The secure entry point to be used on warm reset.
50 */
51static unsigned long secure_entrypoint;
52
53/*
54 * This array stores the 'power_state' requests of each CPU during
55 * CPU_SUSPEND and SYSTEM_SUSPEND to support querying of state-ID
56 * by the platform.
57 */
58unsigned int psci_power_state_compat[PLATFORM_CORE_COUNT];
59
60/*******************************************************************************
61 * The PSCI compatibility helper to parse the power state and populate the
62 * 'pwr_domain_state' for each power level. It is assumed that, when in
63 * compatibility mode, the PSCI generic layer need to know only whether the
64 * affinity level will be OFF or in RETENTION and if the platform supports
65 * multiple power down and retention states, it will be taken care within
66 * the platform layer.
67 ******************************************************************************/
68static int parse_power_state(unsigned int power_state,
69 psci_power_state_t *req_state)
70{
71 int i;
72 int pstate = psci_get_pstate_type(power_state);
73 int aff_lvl = psci_get_pstate_pwrlvl(power_state);
74
75 if (aff_lvl > PLATFORM_MAX_AFFLVL)
76 return PSCI_E_INVALID_PARAMS;
77
78 /* Sanity check the requested state */
79 if (pstate == PSTATE_TYPE_STANDBY) {
80 /*
81 * Set the CPU local state as retention and ignore the higher
82 * levels. This allows the generic PSCI layer to invoke
83 * plat_psci_ops 'cpu_standby' hook and the compatibility
84 * layer invokes the 'affinst_standby' handler with the
85 * correct power_state parameter thus preserving the correct
86 * behavior.
87 */
88 req_state->pwr_domain_state[0] =
89 PLAT_MAX_RET_STATE;
90 } else {
91 for (i = 0; i <= aff_lvl; i++)
92 req_state->pwr_domain_state[i] =
93 PLAT_MAX_OFF_STATE;
94 }
95
96 return PSCI_E_SUCCESS;
97}
98
99/*******************************************************************************
100 * The PSCI compatibility helper to set the 'power_state' in
101 * psci_power_state_compat[] at index corresponding to the current core.
102 ******************************************************************************/
103static void set_psci_power_state_compat(unsigned int power_state)
104{
105 unsigned int my_core_pos = plat_my_core_pos();
106
107 psci_power_state_compat[my_core_pos] = power_state;
108 flush_dcache_range((uintptr_t) &psci_power_state_compat[my_core_pos],
109 sizeof(psci_power_state_compat[my_core_pos]));
110}
111
112/*******************************************************************************
113 * The PSCI compatibility helper for plat_pm_ops_t 'validate_power_state'
114 * hook.
115 ******************************************************************************/
116static int validate_power_state_compat(unsigned int power_state,
117 psci_power_state_t *req_state)
118{
119 int rc;
120 assert(req_state);
121
122 if (pm_ops->validate_power_state) {
123 rc = pm_ops->validate_power_state(power_state);
124 if (rc != PSCI_E_SUCCESS)
125 return rc;
126 }
127
128 /* Store the 'power_state' parameter for the current CPU. */
129 set_psci_power_state_compat(power_state);
130
131 return parse_power_state(power_state, req_state);
132}
133
134/*******************************************************************************
135 * The PSCI compatibility helper for plat_pm_ops_t
136 * 'get_sys_suspend_power_state' hook.
137 ******************************************************************************/
138void get_sys_suspend_power_state_compat(psci_power_state_t *req_state)
139{
140 unsigned int power_state;
141 assert(req_state);
142
143 power_state = pm_ops->get_sys_suspend_power_state();
144
145 /* Store the 'power_state' parameter for the current CPU. */
146 set_psci_power_state_compat(power_state);
147
148 if (parse_power_state(power_state, req_state) != PSCI_E_SUCCESS)
149 assert(0);
150}
151
152/*******************************************************************************
153 * The PSCI compatibility helper for plat_pm_ops_t 'validate_ns_entrypoint'
154 * hook.
155 ******************************************************************************/
156static int validate_ns_entrypoint_compat(uintptr_t ns_entrypoint)
157{
158 return pm_ops->validate_ns_entrypoint(ns_entrypoint);
159}
160
161/*******************************************************************************
162 * The PSCI compatibility helper for plat_pm_ops_t 'affinst_standby' hook.
163 ******************************************************************************/
164static void cpu_standby_compat(plat_local_state_t cpu_state)
165{
166 unsigned int powerstate = psci_get_suspend_powerstate();
167
168 assert(powerstate != PSCI_INVALID_DATA);
169
170 pm_ops->affinst_standby(powerstate);
171}
172
173/*******************************************************************************
174 * The PSCI compatibility helper for plat_pm_ops_t 'affinst_on' hook.
175 ******************************************************************************/
176static int pwr_domain_on_compat(u_register_t mpidr)
177{
178 int level, rc;
179
180 /*
181 * The new PSCI framework does not hold the locks for higher level
182 * power domain nodes when this hook is invoked. Hence figuring out the
183 * target state of the parent power domains does not make much sense.
184 * Hence we hard-code the state as PSCI_STATE_OFF for all the levels.
185 * We expect the platform to perform the necessary CPU_ON operations
186 * when the 'affinst_on' is invoked only for level 0.
187 */
188 for (level = PLATFORM_MAX_AFFLVL; level >= 0; level--) {
189 rc = pm_ops->affinst_on((unsigned long)mpidr, secure_entrypoint,
190 level, PSCI_STATE_OFF);
191 if (rc != PSCI_E_SUCCESS)
192 break;
193 }
194
195 return rc;
196}
197
198/*******************************************************************************
199 * The PSCI compatibility helper for plat_pm_ops_t 'affinst_off' hook.
200 ******************************************************************************/
201static void pwr_domain_off_compat(const psci_power_state_t *target_state)
202{
203 int level;
204 unsigned int plat_state;
205
206 for (level = 0; level <= PLATFORM_MAX_AFFLVL; level++) {
207 plat_state = (is_local_state_run(
208 target_state->pwr_domain_state[level]) ?
209 PSCI_STATE_ON : PSCI_STATE_OFF);
210 pm_ops->affinst_off(level, plat_state);
211 }
212}
213
214/*******************************************************************************
215 * The PSCI compatibility helper for plat_pm_ops_t 'affinst_suspend' hook.
216 ******************************************************************************/
217static void pwr_domain_suspend_compat(const psci_power_state_t *target_state)
218{
219 int level;
220 unsigned int plat_state;
221
222 for (level = 0; level <= psci_get_suspend_afflvl(); level++) {
223 plat_state = (is_local_state_run(
224 target_state->pwr_domain_state[level]) ?
225 PSCI_STATE_ON : PSCI_STATE_OFF);
226 pm_ops->affinst_suspend(secure_entrypoint, level, plat_state);
227 }
228}
229
230/*******************************************************************************
231 * The PSCI compatibility helper for plat_pm_ops_t 'affinst_on_finish'
232 * hook.
233 ******************************************************************************/
234static void pwr_domain_on_finish_compat(const psci_power_state_t *target_state)
235{
236 int level;
237 unsigned int plat_state;
238
239 for (level = PLATFORM_MAX_AFFLVL; level >= 0; level--) {
240 plat_state = (is_local_state_run(
241 target_state->pwr_domain_state[level]) ?
242 PSCI_STATE_ON : PSCI_STATE_OFF);
243 pm_ops->affinst_on_finish(level, plat_state);
244 }
245}
246
247/*******************************************************************************
248 * The PSCI compatibility helper for plat_pm_ops_t
249 * 'affinst_suspend_finish' hook.
250 ******************************************************************************/
251static void pwr_domain_suspend_finish_compat(
252 const psci_power_state_t *target_state)
253{
254 int level;
255 unsigned int plat_state;
256
257 for (level = psci_get_suspend_afflvl(); level >= 0; level--) {
258 plat_state = (is_local_state_run(
259 target_state->pwr_domain_state[level]) ?
260 PSCI_STATE_ON : PSCI_STATE_OFF);
261 pm_ops->affinst_suspend_finish(level, plat_state);
262 }
263}
264
265/*******************************************************************************
266 * The PSCI compatibility helper for plat_pm_ops_t 'system_off' hook.
267 ******************************************************************************/
268static void __dead2 system_off_compat(void)
269{
270 pm_ops->system_off();
271}
272
273/*******************************************************************************
274 * The PSCI compatibility helper for plat_pm_ops_t 'system_reset' hook.
275 ******************************************************************************/
276static void __dead2 system_reset_compat(void)
277{
278 pm_ops->system_reset();
279}
280
281/*******************************************************************************
282 * Export the compatibility compat_psci_ops. The assumption made is that the
283 * power domains correspond to affinity instances on the platform.
284 ******************************************************************************/
285int plat_setup_psci_ops(uintptr_t sec_entrypoint,
286 const plat_psci_ops_t **psci_ops)
287{
288 platform_setup_pm(&pm_ops);
289
290 secure_entrypoint = (unsigned long) sec_entrypoint;
291
292 /*
293 * It is compulsory for the platform ports using the new porting
294 * interface to export a hook to validate the power state parameter
295 */
296 compat_psci_ops.validate_power_state = validate_power_state_compat;
297
298 /*
299 * Populate the compatibility plat_psci_ops_t hooks if available
300 */
301 if (pm_ops->validate_ns_entrypoint)
302 compat_psci_ops.validate_ns_entrypoint =
303 validate_ns_entrypoint_compat;
304
305 if (pm_ops->affinst_standby)
306 compat_psci_ops.cpu_standby = cpu_standby_compat;
307
308 if (pm_ops->affinst_on)
309 compat_psci_ops.pwr_domain_on = pwr_domain_on_compat;
310
311 if (pm_ops->affinst_off)
312 compat_psci_ops.pwr_domain_off = pwr_domain_off_compat;
313
314 if (pm_ops->affinst_suspend)
315 compat_psci_ops.pwr_domain_suspend = pwr_domain_suspend_compat;
316
317 if (pm_ops->affinst_on_finish)
318 compat_psci_ops.pwr_domain_on_finish =
319 pwr_domain_on_finish_compat;
320
321 if (pm_ops->affinst_suspend_finish)
322 compat_psci_ops.pwr_domain_suspend_finish =
323 pwr_domain_suspend_finish_compat;
324
325 if (pm_ops->system_off)
326 compat_psci_ops.system_off = system_off_compat;
327
328 if (pm_ops->system_reset)
329 compat_psci_ops.system_reset = system_reset_compat;
330
331 if (pm_ops->get_sys_suspend_power_state)
332 compat_psci_ops.get_sys_suspend_power_state =
333 get_sys_suspend_power_state_compat;
334
335 *psci_ops = &compat_psci_ops;
336 return 0;
337}