blob: c9563c9ed957c6f5676ef56a47a5aa00382cff30 [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
Tony Xief6118cc2016-01-15 17:17:32 +08007#include <assert.h>
Isla Mitchelle3631462017-07-14 10:46:32 +01008#include <errno.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +00009
Isla Mitchelle3631462017-07-14 10:46:32 +010010#include <platform_def.h>
Antonio Nino Diaze0f90632018-12-14 00:18:21 +000011
12#include <arch_helpers.h>
13#include <common/debug.h>
14#include <drivers/console.h>
15#include <drivers/delay_timer.h>
16#include <lib/psci/psci.h>
17
18#include <plat_private.h>
Tony Xief6118cc2016-01-15 17:17:32 +080019
20/* Macros to read the rk power domain state */
21#define RK_CORE_PWR_STATE(state) \
22 ((state)->pwr_domain_state[MPIDR_AFFLVL0])
23#define RK_CLUSTER_PWR_STATE(state) \
24 ((state)->pwr_domain_state[MPIDR_AFFLVL1])
25#define RK_SYSTEM_PWR_STATE(state) \
26 ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
27
28static uintptr_t rockchip_sec_entrypoint;
29
tony.xie422d51c2017-03-01 11:05:17 +080030#pragma weak rockchip_soc_cores_pwr_dm_on
31#pragma weak rockchip_soc_hlvl_pwr_dm_off
32#pragma weak rockchip_soc_cores_pwr_dm_off
33#pragma weak rockchip_soc_sys_pwr_dm_suspend
34#pragma weak rockchip_soc_cores_pwr_dm_suspend
35#pragma weak rockchip_soc_hlvl_pwr_dm_suspend
36#pragma weak rockchip_soc_hlvl_pwr_dm_on_finish
37#pragma weak rockchip_soc_cores_pwr_dm_on_finish
38#pragma weak rockchip_soc_sys_pwr_dm_resume
39#pragma weak rockchip_soc_hlvl_pwr_dm_resume
40#pragma weak rockchip_soc_cores_pwr_dm_resume
41#pragma weak rockchip_soc_soft_reset
42#pragma weak rockchip_soc_system_off
43#pragma weak rockchip_soc_sys_pd_pwr_dn_wfi
44#pragma weak rockchip_soc_cores_pd_pwr_dn_wfi
45
46int rockchip_soc_cores_pwr_dm_on(unsigned long mpidr, uint64_t entrypoint)
47{
48 return PSCI_E_NOT_SUPPORTED;
49}
50
51int rockchip_soc_hlvl_pwr_dm_off(uint32_t lvl,
52 plat_local_state_t lvl_state)
53{
54 return PSCI_E_NOT_SUPPORTED;
55}
56
57int rockchip_soc_cores_pwr_dm_off(void)
58{
59 return PSCI_E_NOT_SUPPORTED;
60}
61
62int rockchip_soc_sys_pwr_dm_suspend(void)
63{
64 return PSCI_E_NOT_SUPPORTED;
65}
66
67int rockchip_soc_cores_pwr_dm_suspend(void)
68{
69 return PSCI_E_NOT_SUPPORTED;
70}
71
72int rockchip_soc_hlvl_pwr_dm_suspend(uint32_t lvl,
73 plat_local_state_t lvl_state)
74{
75 return PSCI_E_NOT_SUPPORTED;
76}
77
78int rockchip_soc_hlvl_pwr_dm_on_finish(uint32_t lvl,
79 plat_local_state_t lvl_state)
80{
81 return PSCI_E_NOT_SUPPORTED;
82}
83
84int rockchip_soc_cores_pwr_dm_on_finish(void)
85{
86 return PSCI_E_NOT_SUPPORTED;
87}
88
89int rockchip_soc_sys_pwr_dm_resume(void)
90{
91 return PSCI_E_NOT_SUPPORTED;
92}
93
94int rockchip_soc_hlvl_pwr_dm_resume(uint32_t lvl,
95 plat_local_state_t lvl_state)
96{
97 return PSCI_E_NOT_SUPPORTED;
98}
99
100int rockchip_soc_cores_pwr_dm_resume(void)
101{
102 return PSCI_E_NOT_SUPPORTED;
103}
104
105void __dead2 rockchip_soc_soft_reset(void)
106{
107 while (1)
108 ;
109}
110
111void __dead2 rockchip_soc_system_off(void)
112{
113 while (1)
114 ;
115}
116
117void __dead2 rockchip_soc_cores_pd_pwr_dn_wfi(
118 const psci_power_state_t *target_state)
119{
120 psci_power_down_wfi();
121}
122
123void __dead2 rockchip_soc_sys_pd_pwr_dn_wfi(void)
124{
125 psci_power_down_wfi();
126}
Tony Xief6118cc2016-01-15 17:17:32 +0800127
Tony Xief6118cc2016-01-15 17:17:32 +0800128/*******************************************************************************
129 * Rockchip standard platform handler called to check the validity of the power
130 * state parameter.
131 ******************************************************************************/
132int rockchip_validate_power_state(unsigned int power_state,
133 psci_power_state_t *req_state)
134{
135 int pstate = psci_get_pstate_type(power_state);
136 int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
137 int i;
138
139 assert(req_state);
140
141 if (pwr_lvl > PLAT_MAX_PWR_LVL)
142 return PSCI_E_INVALID_PARAMS;
143
144 /* Sanity check the requested state */
145 if (pstate == PSTATE_TYPE_STANDBY) {
146 /*
147 * It's probably to enter standby only on power level 0
148 * ignore any other power level.
149 */
150 if (pwr_lvl != MPIDR_AFFLVL0)
151 return PSCI_E_INVALID_PARAMS;
152
153 req_state->pwr_domain_state[MPIDR_AFFLVL0] =
154 PLAT_MAX_RET_STATE;
155 } else {
156 for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++)
157 req_state->pwr_domain_state[i] =
158 PLAT_MAX_OFF_STATE;
Tony Xie42e113e2016-07-16 11:16:51 +0800159
160 for (i = (pwr_lvl + 1); i <= PLAT_MAX_PWR_LVL; i++)
161 req_state->pwr_domain_state[i] =
162 PLAT_MAX_RET_STATE;
Tony Xief6118cc2016-01-15 17:17:32 +0800163 }
164
165 /* We expect the 'state id' to be zero */
166 if (psci_get_pstate_id(power_state))
167 return PSCI_E_INVALID_PARAMS;
168
169 return PSCI_E_SUCCESS;
170}
171
172void rockchip_get_sys_suspend_power_state(psci_power_state_t *req_state)
173{
174 int i;
175
176 for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++)
177 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
178}
179
180/*******************************************************************************
181 * RockChip handler called when a CPU is about to enter standby.
182 ******************************************************************************/
183void rockchip_cpu_standby(plat_local_state_t cpu_state)
184{
185 unsigned int scr;
186
187 assert(cpu_state == PLAT_MAX_RET_STATE);
188
189 scr = read_scr_el3();
190 /* Enable PhysicalIRQ bit for NS world to wake the CPU */
191 write_scr_el3(scr | SCR_IRQ_BIT);
192 isb();
193 dsb();
194 wfi();
195
196 /*
197 * Restore SCR to the original value, synchronisation of scr_el3 is
198 * done by eret while el3_exit to save some execution cycles.
199 */
200 write_scr_el3(scr);
201}
202
203/*******************************************************************************
204 * RockChip handler called when a power domain is about to be turned on. The
205 * mpidr determines the CPU to be turned on.
206 ******************************************************************************/
207int rockchip_pwr_domain_on(u_register_t mpidr)
208{
tony.xie422d51c2017-03-01 11:05:17 +0800209 return rockchip_soc_cores_pwr_dm_on(mpidr, rockchip_sec_entrypoint);
Tony Xief6118cc2016-01-15 17:17:32 +0800210}
211
212/*******************************************************************************
213 * RockChip handler called when a power domain is about to be turned off. The
214 * target_state encodes the power state that each level should transition to.
215 ******************************************************************************/
216void rockchip_pwr_domain_off(const psci_power_state_t *target_state)
217{
Tony Xie42e113e2016-07-16 11:16:51 +0800218 uint32_t lvl;
219 plat_local_state_t lvl_state;
tony.xie422d51c2017-03-01 11:05:17 +0800220 int ret;
Tony Xie42e113e2016-07-16 11:16:51 +0800221
Tony Xief6118cc2016-01-15 17:17:32 +0800222 assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
223
224 plat_rockchip_gic_cpuif_disable();
225
226 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
227 plat_cci_disable();
Tony Xie42e113e2016-07-16 11:16:51 +0800228
tony.xie422d51c2017-03-01 11:05:17 +0800229 rockchip_soc_cores_pwr_dm_off();
Tony Xie42e113e2016-07-16 11:16:51 +0800230
231 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
232 lvl_state = target_state->pwr_domain_state[lvl];
tony.xie422d51c2017-03-01 11:05:17 +0800233 ret = rockchip_soc_hlvl_pwr_dm_off(lvl, lvl_state);
234 if (ret == PSCI_E_NOT_SUPPORTED)
235 break;
Tony Xie42e113e2016-07-16 11:16:51 +0800236 }
Tony Xief6118cc2016-01-15 17:17:32 +0800237}
238
239/*******************************************************************************
240 * RockChip handler called when a power domain is about to be suspended. The
241 * target_state encodes the power state that each level should transition to.
242 ******************************************************************************/
243void rockchip_pwr_domain_suspend(const psci_power_state_t *target_state)
244{
Tony Xie42e113e2016-07-16 11:16:51 +0800245 uint32_t lvl;
246 plat_local_state_t lvl_state;
tony.xie422d51c2017-03-01 11:05:17 +0800247 int ret;
Tony Xief6118cc2016-01-15 17:17:32 +0800248
Tony Xie42e113e2016-07-16 11:16:51 +0800249 if (RK_CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
250 return;
Tony Xief6118cc2016-01-15 17:17:32 +0800251
Derek Basehorea169a802018-01-25 22:05:41 -0800252 /* Prevent interrupts from spuriously waking up this cpu */
253 plat_rockchip_gic_cpuif_disable();
254
tony.xie422d51c2017-03-01 11:05:17 +0800255 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
256 rockchip_soc_sys_pwr_dm_suspend();
257 else
258 rockchip_soc_cores_pwr_dm_suspend();
Tony Xief6118cc2016-01-15 17:17:32 +0800259
Tony Xief6118cc2016-01-15 17:17:32 +0800260 /* Perform the common cluster specific operations */
261 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
262 plat_cci_disable();
Tony Xie42e113e2016-07-16 11:16:51 +0800263
Tony Xie6d7d93c2016-09-02 11:13:38 -0700264 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
265 return;
266
Tony Xie42e113e2016-07-16 11:16:51 +0800267 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
268 lvl_state = target_state->pwr_domain_state[lvl];
tony.xie422d51c2017-03-01 11:05:17 +0800269 ret = rockchip_soc_hlvl_pwr_dm_suspend(lvl, lvl_state);
270 if (ret == PSCI_E_NOT_SUPPORTED)
271 break;
Tony Xie42e113e2016-07-16 11:16:51 +0800272 }
Tony Xief6118cc2016-01-15 17:17:32 +0800273}
274
275/*******************************************************************************
276 * RockChip handler called when a power domain has just been powered on after
277 * being turned off earlier. The target_state encodes the low power state that
278 * each level has woken up from.
279 ******************************************************************************/
280void rockchip_pwr_domain_on_finish(const psci_power_state_t *target_state)
281{
Tony Xie42e113e2016-07-16 11:16:51 +0800282 uint32_t lvl;
283 plat_local_state_t lvl_state;
tony.xie422d51c2017-03-01 11:05:17 +0800284 int ret;
Tony Xie42e113e2016-07-16 11:16:51 +0800285
Tony Xief6118cc2016-01-15 17:17:32 +0800286 assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
287
tony.xie422d51c2017-03-01 11:05:17 +0800288 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
289 lvl_state = target_state->pwr_domain_state[lvl];
290 ret = rockchip_soc_hlvl_pwr_dm_on_finish(lvl, lvl_state);
291 if (ret == PSCI_E_NOT_SUPPORTED)
292 break;
Tony Xie42e113e2016-07-16 11:16:51 +0800293 }
294
tony.xie422d51c2017-03-01 11:05:17 +0800295 rockchip_soc_cores_pwr_dm_on_finish();
Tony Xief6118cc2016-01-15 17:17:32 +0800296
297 /* Perform the common cluster specific operations */
298 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
299 /* Enable coherency if this cluster was off */
300 plat_cci_enable();
301 }
302
303 /* Enable the gic cpu interface */
304 plat_rockchip_gic_pcpu_init();
305
306 /* Program the gic per-cpu distributor or re-distributor interface */
307 plat_rockchip_gic_cpuif_enable();
308}
309
310/*******************************************************************************
311 * RockChip handler called when a power domain has just been powered on after
312 * having been suspended earlier. The target_state encodes the low power state
313 * that each level has woken up from.
314 * TODO: At the moment we reuse the on finisher and reinitialize the secure
315 * context. Need to implement a separate suspend finisher.
316 ******************************************************************************/
317void rockchip_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
318{
Tony Xie42e113e2016-07-16 11:16:51 +0800319 uint32_t lvl;
320 plat_local_state_t lvl_state;
tony.xie422d51c2017-03-01 11:05:17 +0800321 int ret;
Tony Xie42e113e2016-07-16 11:16:51 +0800322
Tony Xief6118cc2016-01-15 17:17:32 +0800323 /* Nothing to be done on waking up from retention from CPU level */
Tony Xie42e113e2016-07-16 11:16:51 +0800324 if (RK_CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
Tony Xief6118cc2016-01-15 17:17:32 +0800325 return;
326
Tony Xie6d7d93c2016-09-02 11:13:38 -0700327 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
tony.xie422d51c2017-03-01 11:05:17 +0800328 rockchip_soc_sys_pwr_dm_resume();
Tony Xie6d7d93c2016-09-02 11:13:38 -0700329 goto comm_finish;
330 }
331
tony.xie422d51c2017-03-01 11:05:17 +0800332 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
333 lvl_state = target_state->pwr_domain_state[lvl];
334 ret = rockchip_soc_hlvl_pwr_dm_resume(lvl, lvl_state);
335 if (ret == PSCI_E_NOT_SUPPORTED)
336 break;
Tony Xie42e113e2016-07-16 11:16:51 +0800337 }
338
tony.xie422d51c2017-03-01 11:05:17 +0800339 rockchip_soc_cores_pwr_dm_resume();
340
Tony Xie42e113e2016-07-16 11:16:51 +0800341 /*
Tony Xie6d7d93c2016-09-02 11:13:38 -0700342 * Program the gic per-cpu distributor or re-distributor interface.
Caesar Wangda9e6de2016-09-10 06:26:11 +0800343 * For sys power domain operation, resuming of the gic needs to operate
tony.xie422d51c2017-03-01 11:05:17 +0800344 * in rockchip_soc_sys_pwr_dm_resume(), according to the sys power mode
Caesar Wangda9e6de2016-09-10 06:26:11 +0800345 * implements.
346 */
Tony Xie42e113e2016-07-16 11:16:51 +0800347 plat_rockchip_gic_cpuif_enable();
Tony Xief6118cc2016-01-15 17:17:32 +0800348
Tony Xie6d7d93c2016-09-02 11:13:38 -0700349comm_finish:
Tony Xief6118cc2016-01-15 17:17:32 +0800350 /* Perform the common cluster specific operations */
351 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
352 /* Enable coherency if this cluster was off */
353 plat_cci_enable();
354 }
355}
356
357/*******************************************************************************
358 * RockChip handlers to reboot the system
359 ******************************************************************************/
360static void __dead2 rockchip_system_reset(void)
361{
tony.xie422d51c2017-03-01 11:05:17 +0800362 rockchip_soc_soft_reset();
Tony Xief6118cc2016-01-15 17:17:32 +0800363}
364
365/*******************************************************************************
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800366 * RockChip handlers to power off the system
367 ******************************************************************************/
368static void __dead2 rockchip_system_poweroff(void)
369{
tony.xie422d51c2017-03-01 11:05:17 +0800370 rockchip_soc_system_off();
371}
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800372
Antonio Nino Diaz1d74beb2017-05-10 10:59:22 +0100373static void __dead2 rockchip_pd_pwr_down_wfi(
374 const psci_power_state_t *target_state)
375{
376 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
377 rockchip_soc_sys_pd_pwr_dn_wfi();
378 else
379 rockchip_soc_cores_pd_pwr_dn_wfi(target_state);
380}
381
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800382/*******************************************************************************
Tony Xief6118cc2016-01-15 17:17:32 +0800383 * Export the platform handlers via plat_rockchip_psci_pm_ops. The rockchip
384 * standard
385 * platform layer will take care of registering the handlers with PSCI.
386 ******************************************************************************/
387const plat_psci_ops_t plat_rockchip_psci_pm_ops = {
388 .cpu_standby = rockchip_cpu_standby,
389 .pwr_domain_on = rockchip_pwr_domain_on,
390 .pwr_domain_off = rockchip_pwr_domain_off,
391 .pwr_domain_suspend = rockchip_pwr_domain_suspend,
392 .pwr_domain_on_finish = rockchip_pwr_domain_on_finish,
393 .pwr_domain_suspend_finish = rockchip_pwr_domain_suspend_finish,
tony.xie54973e72017-04-24 16:18:10 +0800394 .pwr_domain_pwr_down_wfi = rockchip_pd_pwr_down_wfi,
Tony Xief6118cc2016-01-15 17:17:32 +0800395 .system_reset = rockchip_system_reset,
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800396 .system_off = rockchip_system_poweroff,
Tony Xief6118cc2016-01-15 17:17:32 +0800397 .validate_power_state = rockchip_validate_power_state,
398 .get_sys_suspend_power_state = rockchip_get_sys_suspend_power_state
399};
400
401int plat_setup_psci_ops(uintptr_t sec_entrypoint,
402 const plat_psci_ops_t **psci_ops)
403{
404 *psci_ops = &plat_rockchip_psci_pm_ops;
405 rockchip_sec_entrypoint = sec_entrypoint;
406 return 0;
407}
408
Tony Xie42e113e2016-07-16 11:16:51 +0800409uintptr_t plat_get_sec_entrypoint(void)
410{
411 assert(rockchip_sec_entrypoint);
412 return rockchip_sec_entrypoint;
413}