blob: b6291bbf085db30b54deb69fb625be00bae85962 [file] [log] [blame]
Tony Xief6118cc2016-01-15 17:17:32 +08001/*
2 * Copyright (c) 2013-2016, 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 <console.h>
34#include <errno.h>
35#include <debug.h>
36#include <psci.h>
37#include <delay_timer.h>
38#include <platform_def.h>
39#include <plat_private.h>
40
41/* Macros to read the rk power domain state */
42#define RK_CORE_PWR_STATE(state) \
43 ((state)->pwr_domain_state[MPIDR_AFFLVL0])
44#define RK_CLUSTER_PWR_STATE(state) \
45 ((state)->pwr_domain_state[MPIDR_AFFLVL1])
46#define RK_SYSTEM_PWR_STATE(state) \
47 ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
48
49static uintptr_t rockchip_sec_entrypoint;
50
51static struct rockchip_pm_ops_cb *rockchip_ops;
52
Tony Xief6118cc2016-01-15 17:17:32 +080053/*******************************************************************************
54 * Rockchip standard platform handler called to check the validity of the power
55 * state parameter.
56 ******************************************************************************/
57int rockchip_validate_power_state(unsigned int power_state,
58 psci_power_state_t *req_state)
59{
60 int pstate = psci_get_pstate_type(power_state);
61 int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
62 int i;
63
64 assert(req_state);
65
66 if (pwr_lvl > PLAT_MAX_PWR_LVL)
67 return PSCI_E_INVALID_PARAMS;
68
69 /* Sanity check the requested state */
70 if (pstate == PSTATE_TYPE_STANDBY) {
71 /*
72 * It's probably to enter standby only on power level 0
73 * ignore any other power level.
74 */
75 if (pwr_lvl != MPIDR_AFFLVL0)
76 return PSCI_E_INVALID_PARAMS;
77
78 req_state->pwr_domain_state[MPIDR_AFFLVL0] =
79 PLAT_MAX_RET_STATE;
80 } else {
81 for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++)
82 req_state->pwr_domain_state[i] =
83 PLAT_MAX_OFF_STATE;
Tony Xie42e113e2016-07-16 11:16:51 +080084
85 for (i = (pwr_lvl + 1); i <= PLAT_MAX_PWR_LVL; i++)
86 req_state->pwr_domain_state[i] =
87 PLAT_MAX_RET_STATE;
Tony Xief6118cc2016-01-15 17:17:32 +080088 }
89
90 /* We expect the 'state id' to be zero */
91 if (psci_get_pstate_id(power_state))
92 return PSCI_E_INVALID_PARAMS;
93
94 return PSCI_E_SUCCESS;
95}
96
97void rockchip_get_sys_suspend_power_state(psci_power_state_t *req_state)
98{
99 int i;
100
101 for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++)
102 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
103}
104
105/*******************************************************************************
106 * RockChip handler called when a CPU is about to enter standby.
107 ******************************************************************************/
108void rockchip_cpu_standby(plat_local_state_t cpu_state)
109{
110 unsigned int scr;
111
112 assert(cpu_state == PLAT_MAX_RET_STATE);
113
114 scr = read_scr_el3();
115 /* Enable PhysicalIRQ bit for NS world to wake the CPU */
116 write_scr_el3(scr | SCR_IRQ_BIT);
117 isb();
118 dsb();
119 wfi();
120
121 /*
122 * Restore SCR to the original value, synchronisation of scr_el3 is
123 * done by eret while el3_exit to save some execution cycles.
124 */
125 write_scr_el3(scr);
126}
127
128/*******************************************************************************
129 * RockChip handler called when a power domain is about to be turned on. The
130 * mpidr determines the CPU to be turned on.
131 ******************************************************************************/
132int rockchip_pwr_domain_on(u_register_t mpidr)
133{
134 if (rockchip_ops && rockchip_ops->cores_pwr_dm_on)
135 rockchip_ops->cores_pwr_dm_on(mpidr, rockchip_sec_entrypoint);
136
137 return PSCI_E_SUCCESS;
138}
139
140/*******************************************************************************
141 * RockChip handler called when a power domain is about to be turned off. The
142 * target_state encodes the power state that each level should transition to.
143 ******************************************************************************/
144void rockchip_pwr_domain_off(const psci_power_state_t *target_state)
145{
Tony Xie42e113e2016-07-16 11:16:51 +0800146 uint32_t lvl;
147 plat_local_state_t lvl_state;
148
Tony Xief6118cc2016-01-15 17:17:32 +0800149 assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
150
151 plat_rockchip_gic_cpuif_disable();
152
153 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
154 plat_cci_disable();
Tony Xie42e113e2016-07-16 11:16:51 +0800155
156 if (!rockchip_ops || !rockchip_ops->cores_pwr_dm_off)
157 return;
158
159 rockchip_ops->cores_pwr_dm_off();
160
161 if (!rockchip_ops->hlvl_pwr_dm_off)
162 return;
163
164 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
165 lvl_state = target_state->pwr_domain_state[lvl];
166 rockchip_ops->hlvl_pwr_dm_off(lvl, lvl_state);
167 }
Tony Xief6118cc2016-01-15 17:17:32 +0800168}
169
170/*******************************************************************************
171 * RockChip handler called when a power domain is about to be suspended. The
172 * target_state encodes the power state that each level should transition to.
173 ******************************************************************************/
174void rockchip_pwr_domain_suspend(const psci_power_state_t *target_state)
175{
Tony Xie42e113e2016-07-16 11:16:51 +0800176 uint32_t lvl;
177 plat_local_state_t lvl_state;
Tony Xief6118cc2016-01-15 17:17:32 +0800178
Tony Xie42e113e2016-07-16 11:16:51 +0800179 if (RK_CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
180 return;
Tony Xief6118cc2016-01-15 17:17:32 +0800181
Tony Xie42e113e2016-07-16 11:16:51 +0800182 if (rockchip_ops) {
183 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE &&
184 rockchip_ops->sys_pwr_dm_suspend) {
Tony Xief6118cc2016-01-15 17:17:32 +0800185 rockchip_ops->sys_pwr_dm_suspend();
Tony Xie42e113e2016-07-16 11:16:51 +0800186 } else if (rockchip_ops->cores_pwr_dm_suspend) {
Tony Xief6118cc2016-01-15 17:17:32 +0800187 rockchip_ops->cores_pwr_dm_suspend();
Tony Xie42e113e2016-07-16 11:16:51 +0800188 }
Tony Xief6118cc2016-01-15 17:17:32 +0800189 }
190
191 /* Prevent interrupts from spuriously waking up this cpu */
192 plat_rockchip_gic_cpuif_disable();
193
194 /* Perform the common cluster specific operations */
195 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
196 plat_cci_disable();
Tony Xie42e113e2016-07-16 11:16:51 +0800197
198 if (!rockchip_ops || !rockchip_ops->hlvl_pwr_dm_suspend)
199 return;
200
201 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
202 lvl_state = target_state->pwr_domain_state[lvl];
203 rockchip_ops->hlvl_pwr_dm_suspend(lvl, lvl_state);
204 }
Tony Xief6118cc2016-01-15 17:17:32 +0800205}
206
207/*******************************************************************************
208 * RockChip handler called when a power domain has just been powered on after
209 * being turned off earlier. The target_state encodes the low power state that
210 * each level has woken up from.
211 ******************************************************************************/
212void rockchip_pwr_domain_on_finish(const psci_power_state_t *target_state)
213{
Tony Xie42e113e2016-07-16 11:16:51 +0800214 uint32_t lvl;
215 plat_local_state_t lvl_state;
216
Tony Xief6118cc2016-01-15 17:17:32 +0800217 assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
218
Tony Xie42e113e2016-07-16 11:16:51 +0800219 if (!rockchip_ops)
220 goto comm_finish;
221
222 if (rockchip_ops->hlvl_pwr_dm_on_finish) {
223 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
224 lvl_state = target_state->pwr_domain_state[lvl];
225 rockchip_ops->hlvl_pwr_dm_on_finish(lvl, lvl_state);
226 }
227 }
228
229 if (rockchip_ops->cores_pwr_dm_on_finish)
Tony Xief6118cc2016-01-15 17:17:32 +0800230 rockchip_ops->cores_pwr_dm_on_finish();
Tony Xie42e113e2016-07-16 11:16:51 +0800231comm_finish:
Tony Xief6118cc2016-01-15 17:17:32 +0800232
233 /* Perform the common cluster specific operations */
234 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
235 /* Enable coherency if this cluster was off */
236 plat_cci_enable();
237 }
238
239 /* Enable the gic cpu interface */
240 plat_rockchip_gic_pcpu_init();
241
242 /* Program the gic per-cpu distributor or re-distributor interface */
243 plat_rockchip_gic_cpuif_enable();
244}
245
246/*******************************************************************************
247 * RockChip handler called when a power domain has just been powered on after
248 * having been suspended earlier. The target_state encodes the low power state
249 * that each level has woken up from.
250 * TODO: At the moment we reuse the on finisher and reinitialize the secure
251 * context. Need to implement a separate suspend finisher.
252 ******************************************************************************/
253void rockchip_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
254{
Tony Xie42e113e2016-07-16 11:16:51 +0800255 uint32_t lvl;
256 plat_local_state_t lvl_state;
257
Tony Xief6118cc2016-01-15 17:17:32 +0800258 /* Nothing to be done on waking up from retention from CPU level */
Tony Xie42e113e2016-07-16 11:16:51 +0800259 if (RK_CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
Tony Xief6118cc2016-01-15 17:17:32 +0800260 return;
261
262 /* Perform system domain restore if woken up from system suspend */
Tony Xie42e113e2016-07-16 11:16:51 +0800263 if (!rockchip_ops)
264 goto comm_finish;
265
266 if (rockchip_ops->hlvl_pwr_dm_resume) {
267 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
268 lvl_state = target_state->pwr_domain_state[lvl];
269 rockchip_ops->hlvl_pwr_dm_resume(lvl, lvl_state);
270 }
271 }
272
273 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE &&
274 rockchip_ops->sys_pwr_dm_resume) {
275 rockchip_ops->sys_pwr_dm_resume();
276 } else if (rockchip_ops->cores_pwr_dm_resume) {
277 rockchip_ops->cores_pwr_dm_resume();
278 }
279
280comm_finish:
281 /*
282 * Program the gic per-cpu distributor
283 * or re-distributor interface
284 */
285 plat_rockchip_gic_cpuif_enable();
Tony Xief6118cc2016-01-15 17:17:32 +0800286
287 /* Perform the common cluster specific operations */
288 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
289 /* Enable coherency if this cluster was off */
290 plat_cci_enable();
291 }
292}
293
294/*******************************************************************************
295 * RockChip handlers to reboot the system
296 ******************************************************************************/
297static void __dead2 rockchip_system_reset(void)
298{
299 assert(rockchip_ops && rockchip_ops->sys_gbl_soft_reset);
300
301 rockchip_ops->sys_gbl_soft_reset();
302}
303
304/*******************************************************************************
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800305 * RockChip handlers to power off the system
306 ******************************************************************************/
307static void __dead2 rockchip_system_poweroff(void)
308{
309 assert(rockchip_ops && rockchip_ops->system_off);
310
311 rockchip_ops->system_off();
312}
313
314/*******************************************************************************
Tony Xief6118cc2016-01-15 17:17:32 +0800315 * Export the platform handlers via plat_rockchip_psci_pm_ops. The rockchip
316 * standard
317 * platform layer will take care of registering the handlers with PSCI.
318 ******************************************************************************/
319const plat_psci_ops_t plat_rockchip_psci_pm_ops = {
320 .cpu_standby = rockchip_cpu_standby,
321 .pwr_domain_on = rockchip_pwr_domain_on,
322 .pwr_domain_off = rockchip_pwr_domain_off,
323 .pwr_domain_suspend = rockchip_pwr_domain_suspend,
324 .pwr_domain_on_finish = rockchip_pwr_domain_on_finish,
325 .pwr_domain_suspend_finish = rockchip_pwr_domain_suspend_finish,
326 .system_reset = rockchip_system_reset,
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800327 .system_off = rockchip_system_poweroff,
Tony Xief6118cc2016-01-15 17:17:32 +0800328 .validate_power_state = rockchip_validate_power_state,
329 .get_sys_suspend_power_state = rockchip_get_sys_suspend_power_state
330};
331
332int plat_setup_psci_ops(uintptr_t sec_entrypoint,
333 const plat_psci_ops_t **psci_ops)
334{
335 *psci_ops = &plat_rockchip_psci_pm_ops;
336 rockchip_sec_entrypoint = sec_entrypoint;
337 return 0;
338}
339
Tony Xie42e113e2016-07-16 11:16:51 +0800340uintptr_t plat_get_sec_entrypoint(void)
341{
342 assert(rockchip_sec_entrypoint);
343 return rockchip_sec_entrypoint;
344}
345
Tony Xief6118cc2016-01-15 17:17:32 +0800346void plat_setup_rockchip_pm_ops(struct rockchip_pm_ops_cb *ops)
347{
348 rockchip_ops = ops;
349}