blob: 4133fd04f1dafa2bea43c2e4e83fdac90fa370cc [file] [log] [blame]
Tony Xief6118cc2016-01-15 17:17:32 +08001/*
2 * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
3 *
dp-armfa3cf0b2017-05-03 09:38:09 +01004 * SPDX-License-Identifier: BSD-3-Clause
Tony Xief6118cc2016-01-15 17:17:32 +08005 */
6
7#include <arch_helpers.h>
8#include <assert.h>
9#include <console.h>
10#include <errno.h>
11#include <debug.h>
12#include <psci.h>
13#include <delay_timer.h>
14#include <platform_def.h>
15#include <plat_private.h>
16
17/* Macros to read the rk power domain state */
18#define RK_CORE_PWR_STATE(state) \
19 ((state)->pwr_domain_state[MPIDR_AFFLVL0])
20#define RK_CLUSTER_PWR_STATE(state) \
21 ((state)->pwr_domain_state[MPIDR_AFFLVL1])
22#define RK_SYSTEM_PWR_STATE(state) \
23 ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
24
25static uintptr_t rockchip_sec_entrypoint;
26
tony.xie422d51c2017-03-01 11:05:17 +080027#pragma weak rockchip_soc_cores_pwr_dm_on
28#pragma weak rockchip_soc_hlvl_pwr_dm_off
29#pragma weak rockchip_soc_cores_pwr_dm_off
30#pragma weak rockchip_soc_sys_pwr_dm_suspend
31#pragma weak rockchip_soc_cores_pwr_dm_suspend
32#pragma weak rockchip_soc_hlvl_pwr_dm_suspend
33#pragma weak rockchip_soc_hlvl_pwr_dm_on_finish
34#pragma weak rockchip_soc_cores_pwr_dm_on_finish
35#pragma weak rockchip_soc_sys_pwr_dm_resume
36#pragma weak rockchip_soc_hlvl_pwr_dm_resume
37#pragma weak rockchip_soc_cores_pwr_dm_resume
38#pragma weak rockchip_soc_soft_reset
39#pragma weak rockchip_soc_system_off
40#pragma weak rockchip_soc_sys_pd_pwr_dn_wfi
41#pragma weak rockchip_soc_cores_pd_pwr_dn_wfi
42
43int rockchip_soc_cores_pwr_dm_on(unsigned long mpidr, uint64_t entrypoint)
44{
45 return PSCI_E_NOT_SUPPORTED;
46}
47
48int rockchip_soc_hlvl_pwr_dm_off(uint32_t lvl,
49 plat_local_state_t lvl_state)
50{
51 return PSCI_E_NOT_SUPPORTED;
52}
53
54int rockchip_soc_cores_pwr_dm_off(void)
55{
56 return PSCI_E_NOT_SUPPORTED;
57}
58
59int rockchip_soc_sys_pwr_dm_suspend(void)
60{
61 return PSCI_E_NOT_SUPPORTED;
62}
63
64int rockchip_soc_cores_pwr_dm_suspend(void)
65{
66 return PSCI_E_NOT_SUPPORTED;
67}
68
69int rockchip_soc_hlvl_pwr_dm_suspend(uint32_t lvl,
70 plat_local_state_t lvl_state)
71{
72 return PSCI_E_NOT_SUPPORTED;
73}
74
75int rockchip_soc_hlvl_pwr_dm_on_finish(uint32_t lvl,
76 plat_local_state_t lvl_state)
77{
78 return PSCI_E_NOT_SUPPORTED;
79}
80
81int rockchip_soc_cores_pwr_dm_on_finish(void)
82{
83 return PSCI_E_NOT_SUPPORTED;
84}
85
86int rockchip_soc_sys_pwr_dm_resume(void)
87{
88 return PSCI_E_NOT_SUPPORTED;
89}
90
91int rockchip_soc_hlvl_pwr_dm_resume(uint32_t lvl,
92 plat_local_state_t lvl_state)
93{
94 return PSCI_E_NOT_SUPPORTED;
95}
96
97int rockchip_soc_cores_pwr_dm_resume(void)
98{
99 return PSCI_E_NOT_SUPPORTED;
100}
101
102void __dead2 rockchip_soc_soft_reset(void)
103{
104 while (1)
105 ;
106}
107
108void __dead2 rockchip_soc_system_off(void)
109{
110 while (1)
111 ;
112}
113
114void __dead2 rockchip_soc_cores_pd_pwr_dn_wfi(
115 const psci_power_state_t *target_state)
116{
117 psci_power_down_wfi();
118}
119
120void __dead2 rockchip_soc_sys_pd_pwr_dn_wfi(void)
121{
122 psci_power_down_wfi();
123}
Tony Xief6118cc2016-01-15 17:17:32 +0800124
Tony Xief6118cc2016-01-15 17:17:32 +0800125/*******************************************************************************
126 * Rockchip standard platform handler called to check the validity of the power
127 * state parameter.
128 ******************************************************************************/
129int rockchip_validate_power_state(unsigned int power_state,
130 psci_power_state_t *req_state)
131{
132 int pstate = psci_get_pstate_type(power_state);
133 int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
134 int i;
135
136 assert(req_state);
137
138 if (pwr_lvl > PLAT_MAX_PWR_LVL)
139 return PSCI_E_INVALID_PARAMS;
140
141 /* Sanity check the requested state */
142 if (pstate == PSTATE_TYPE_STANDBY) {
143 /*
144 * It's probably to enter standby only on power level 0
145 * ignore any other power level.
146 */
147 if (pwr_lvl != MPIDR_AFFLVL0)
148 return PSCI_E_INVALID_PARAMS;
149
150 req_state->pwr_domain_state[MPIDR_AFFLVL0] =
151 PLAT_MAX_RET_STATE;
152 } else {
153 for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++)
154 req_state->pwr_domain_state[i] =
155 PLAT_MAX_OFF_STATE;
Tony Xie42e113e2016-07-16 11:16:51 +0800156
157 for (i = (pwr_lvl + 1); i <= PLAT_MAX_PWR_LVL; i++)
158 req_state->pwr_domain_state[i] =
159 PLAT_MAX_RET_STATE;
Tony Xief6118cc2016-01-15 17:17:32 +0800160 }
161
162 /* We expect the 'state id' to be zero */
163 if (psci_get_pstate_id(power_state))
164 return PSCI_E_INVALID_PARAMS;
165
166 return PSCI_E_SUCCESS;
167}
168
169void rockchip_get_sys_suspend_power_state(psci_power_state_t *req_state)
170{
171 int i;
172
173 for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++)
174 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
175}
176
177/*******************************************************************************
178 * RockChip handler called when a CPU is about to enter standby.
179 ******************************************************************************/
180void rockchip_cpu_standby(plat_local_state_t cpu_state)
181{
182 unsigned int scr;
183
184 assert(cpu_state == PLAT_MAX_RET_STATE);
185
186 scr = read_scr_el3();
187 /* Enable PhysicalIRQ bit for NS world to wake the CPU */
188 write_scr_el3(scr | SCR_IRQ_BIT);
189 isb();
190 dsb();
191 wfi();
192
193 /*
194 * Restore SCR to the original value, synchronisation of scr_el3 is
195 * done by eret while el3_exit to save some execution cycles.
196 */
197 write_scr_el3(scr);
198}
199
200/*******************************************************************************
201 * RockChip handler called when a power domain is about to be turned on. The
202 * mpidr determines the CPU to be turned on.
203 ******************************************************************************/
204int rockchip_pwr_domain_on(u_register_t mpidr)
205{
tony.xie422d51c2017-03-01 11:05:17 +0800206 return rockchip_soc_cores_pwr_dm_on(mpidr, rockchip_sec_entrypoint);
Tony Xief6118cc2016-01-15 17:17:32 +0800207}
208
209/*******************************************************************************
210 * RockChip handler called when a power domain is about to be turned off. The
211 * target_state encodes the power state that each level should transition to.
212 ******************************************************************************/
213void rockchip_pwr_domain_off(const psci_power_state_t *target_state)
214{
Tony Xie42e113e2016-07-16 11:16:51 +0800215 uint32_t lvl;
216 plat_local_state_t lvl_state;
tony.xie422d51c2017-03-01 11:05:17 +0800217 int ret;
Tony Xie42e113e2016-07-16 11:16:51 +0800218
Tony Xief6118cc2016-01-15 17:17:32 +0800219 assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
220
221 plat_rockchip_gic_cpuif_disable();
222
223 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
224 plat_cci_disable();
Tony Xie42e113e2016-07-16 11:16:51 +0800225
tony.xie422d51c2017-03-01 11:05:17 +0800226 rockchip_soc_cores_pwr_dm_off();
Tony Xie42e113e2016-07-16 11:16:51 +0800227
228 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
229 lvl_state = target_state->pwr_domain_state[lvl];
tony.xie422d51c2017-03-01 11:05:17 +0800230 ret = rockchip_soc_hlvl_pwr_dm_off(lvl, lvl_state);
231 if (ret == PSCI_E_NOT_SUPPORTED)
232 break;
Tony Xie42e113e2016-07-16 11:16:51 +0800233 }
Tony Xief6118cc2016-01-15 17:17:32 +0800234}
235
236/*******************************************************************************
237 * RockChip handler called when a power domain is about to be suspended. The
238 * target_state encodes the power state that each level should transition to.
239 ******************************************************************************/
240void rockchip_pwr_domain_suspend(const psci_power_state_t *target_state)
241{
Tony Xie42e113e2016-07-16 11:16:51 +0800242 uint32_t lvl;
243 plat_local_state_t lvl_state;
tony.xie422d51c2017-03-01 11:05:17 +0800244 int ret;
Tony Xief6118cc2016-01-15 17:17:32 +0800245
Tony Xie42e113e2016-07-16 11:16:51 +0800246 if (RK_CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
247 return;
Tony Xief6118cc2016-01-15 17:17:32 +0800248
tony.xie422d51c2017-03-01 11:05:17 +0800249 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
250 rockchip_soc_sys_pwr_dm_suspend();
251 else
252 rockchip_soc_cores_pwr_dm_suspend();
Tony Xief6118cc2016-01-15 17:17:32 +0800253
254 /* Prevent interrupts from spuriously waking up this cpu */
255 plat_rockchip_gic_cpuif_disable();
256
257 /* Perform the common cluster specific operations */
258 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
259 plat_cci_disable();
Tony Xie42e113e2016-07-16 11:16:51 +0800260
Tony Xie6d7d93c2016-09-02 11:13:38 -0700261 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
262 return;
263
Tony Xie42e113e2016-07-16 11:16:51 +0800264 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
265 lvl_state = target_state->pwr_domain_state[lvl];
tony.xie422d51c2017-03-01 11:05:17 +0800266 ret = rockchip_soc_hlvl_pwr_dm_suspend(lvl, lvl_state);
267 if (ret == PSCI_E_NOT_SUPPORTED)
268 break;
Tony Xie42e113e2016-07-16 11:16:51 +0800269 }
Tony Xief6118cc2016-01-15 17:17:32 +0800270}
271
272/*******************************************************************************
273 * RockChip handler called when a power domain has just been powered on after
274 * being turned off earlier. The target_state encodes the low power state that
275 * each level has woken up from.
276 ******************************************************************************/
277void rockchip_pwr_domain_on_finish(const psci_power_state_t *target_state)
278{
Tony Xie42e113e2016-07-16 11:16:51 +0800279 uint32_t lvl;
280 plat_local_state_t lvl_state;
tony.xie422d51c2017-03-01 11:05:17 +0800281 int ret;
Tony Xie42e113e2016-07-16 11:16:51 +0800282
Tony Xief6118cc2016-01-15 17:17:32 +0800283 assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
284
tony.xie422d51c2017-03-01 11:05:17 +0800285 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
286 lvl_state = target_state->pwr_domain_state[lvl];
287 ret = rockchip_soc_hlvl_pwr_dm_on_finish(lvl, lvl_state);
288 if (ret == PSCI_E_NOT_SUPPORTED)
289 break;
Tony Xie42e113e2016-07-16 11:16:51 +0800290 }
291
tony.xie422d51c2017-03-01 11:05:17 +0800292 rockchip_soc_cores_pwr_dm_on_finish();
Tony Xief6118cc2016-01-15 17:17:32 +0800293
294 /* Perform the common cluster specific operations */
295 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
296 /* Enable coherency if this cluster was off */
297 plat_cci_enable();
298 }
299
300 /* Enable the gic cpu interface */
301 plat_rockchip_gic_pcpu_init();
302
303 /* Program the gic per-cpu distributor or re-distributor interface */
304 plat_rockchip_gic_cpuif_enable();
305}
306
307/*******************************************************************************
308 * RockChip handler called when a power domain has just been powered on after
309 * having been suspended earlier. The target_state encodes the low power state
310 * that each level has woken up from.
311 * TODO: At the moment we reuse the on finisher and reinitialize the secure
312 * context. Need to implement a separate suspend finisher.
313 ******************************************************************************/
314void rockchip_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
315{
Tony Xie42e113e2016-07-16 11:16:51 +0800316 uint32_t lvl;
317 plat_local_state_t lvl_state;
tony.xie422d51c2017-03-01 11:05:17 +0800318 int ret;
Tony Xie42e113e2016-07-16 11:16:51 +0800319
Tony Xief6118cc2016-01-15 17:17:32 +0800320 /* Nothing to be done on waking up from retention from CPU level */
Tony Xie42e113e2016-07-16 11:16:51 +0800321 if (RK_CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
Tony Xief6118cc2016-01-15 17:17:32 +0800322 return;
323
Tony Xie6d7d93c2016-09-02 11:13:38 -0700324 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
tony.xie422d51c2017-03-01 11:05:17 +0800325 rockchip_soc_sys_pwr_dm_resume();
Tony Xie6d7d93c2016-09-02 11:13:38 -0700326 goto comm_finish;
327 }
328
tony.xie422d51c2017-03-01 11:05:17 +0800329 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
330 lvl_state = target_state->pwr_domain_state[lvl];
331 ret = rockchip_soc_hlvl_pwr_dm_resume(lvl, lvl_state);
332 if (ret == PSCI_E_NOT_SUPPORTED)
333 break;
Tony Xie42e113e2016-07-16 11:16:51 +0800334 }
335
tony.xie422d51c2017-03-01 11:05:17 +0800336 rockchip_soc_cores_pwr_dm_resume();
337
Tony Xie42e113e2016-07-16 11:16:51 +0800338 /*
Tony Xie6d7d93c2016-09-02 11:13:38 -0700339 * Program the gic per-cpu distributor or re-distributor interface.
Caesar Wangda9e6de2016-09-10 06:26:11 +0800340 * For sys power domain operation, resuming of the gic needs to operate
tony.xie422d51c2017-03-01 11:05:17 +0800341 * in rockchip_soc_sys_pwr_dm_resume(), according to the sys power mode
Caesar Wangda9e6de2016-09-10 06:26:11 +0800342 * implements.
343 */
Tony Xie42e113e2016-07-16 11:16:51 +0800344 plat_rockchip_gic_cpuif_enable();
Tony Xief6118cc2016-01-15 17:17:32 +0800345
Tony Xie6d7d93c2016-09-02 11:13:38 -0700346comm_finish:
Tony Xief6118cc2016-01-15 17:17:32 +0800347 /* Perform the common cluster specific operations */
348 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
349 /* Enable coherency if this cluster was off */
350 plat_cci_enable();
351 }
352}
353
354/*******************************************************************************
355 * RockChip handlers to reboot the system
356 ******************************************************************************/
357static void __dead2 rockchip_system_reset(void)
358{
tony.xie422d51c2017-03-01 11:05:17 +0800359 rockchip_soc_soft_reset();
Tony Xief6118cc2016-01-15 17:17:32 +0800360}
361
362/*******************************************************************************
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800363 * RockChip handlers to power off the system
364 ******************************************************************************/
365static void __dead2 rockchip_system_poweroff(void)
366{
tony.xie422d51c2017-03-01 11:05:17 +0800367 rockchip_soc_system_off();
368}
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800369
Antonio Nino Diaz1d74beb2017-05-10 10:59:22 +0100370static void __dead2 rockchip_pd_pwr_down_wfi(
371 const psci_power_state_t *target_state)
372{
373 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
374 rockchip_soc_sys_pd_pwr_dn_wfi();
375 else
376 rockchip_soc_cores_pd_pwr_dn_wfi(target_state);
377}
378
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800379/*******************************************************************************
Tony Xief6118cc2016-01-15 17:17:32 +0800380 * Export the platform handlers via plat_rockchip_psci_pm_ops. The rockchip
381 * standard
382 * platform layer will take care of registering the handlers with PSCI.
383 ******************************************************************************/
384const plat_psci_ops_t plat_rockchip_psci_pm_ops = {
385 .cpu_standby = rockchip_cpu_standby,
386 .pwr_domain_on = rockchip_pwr_domain_on,
387 .pwr_domain_off = rockchip_pwr_domain_off,
388 .pwr_domain_suspend = rockchip_pwr_domain_suspend,
389 .pwr_domain_on_finish = rockchip_pwr_domain_on_finish,
390 .pwr_domain_suspend_finish = rockchip_pwr_domain_suspend_finish,
tony.xie54973e72017-04-24 16:18:10 +0800391 .pwr_domain_pwr_down_wfi = rockchip_pd_pwr_down_wfi,
Tony Xief6118cc2016-01-15 17:17:32 +0800392 .system_reset = rockchip_system_reset,
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800393 .system_off = rockchip_system_poweroff,
Tony Xief6118cc2016-01-15 17:17:32 +0800394 .validate_power_state = rockchip_validate_power_state,
395 .get_sys_suspend_power_state = rockchip_get_sys_suspend_power_state
396};
397
398int plat_setup_psci_ops(uintptr_t sec_entrypoint,
399 const plat_psci_ops_t **psci_ops)
400{
401 *psci_ops = &plat_rockchip_psci_pm_ops;
402 rockchip_sec_entrypoint = sec_entrypoint;
403 return 0;
404}
405
Tony Xie42e113e2016-07-16 11:16:51 +0800406uintptr_t plat_get_sec_entrypoint(void)
407{
408 assert(rockchip_sec_entrypoint);
409 return rockchip_sec_entrypoint;
410}