blob: c3dc234747424a8752000185182119f126f746a8 [file] [log] [blame]
Tony Xief6118cc2016-01-15 17:17:32 +08001/*
Louis Mayencourt1c819c32020-01-24 13:30:28 +00002 * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
Tony Xief6118cc2016-01-15 17:17:32 +08003 *
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();
Boyan Karatotevff8a6152024-10-21 07:36:31 +0100121 /* should never reach here */
122 panic();
tony.xie422d51c2017-03-01 11:05:17 +0800123}
124
125void __dead2 rockchip_soc_sys_pd_pwr_dn_wfi(void)
126{
127 psci_power_down_wfi();
Boyan Karatotevff8a6152024-10-21 07:36:31 +0100128 /* should never reach here */
129 panic();
tony.xie422d51c2017-03-01 11:05:17 +0800130}
Tony Xief6118cc2016-01-15 17:17:32 +0800131
Tony Xief6118cc2016-01-15 17:17:32 +0800132/*******************************************************************************
133 * Rockchip standard platform handler called to check the validity of the power
134 * state parameter.
135 ******************************************************************************/
136int rockchip_validate_power_state(unsigned int power_state,
137 psci_power_state_t *req_state)
138{
139 int pstate = psci_get_pstate_type(power_state);
140 int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
141 int i;
142
143 assert(req_state);
144
145 if (pwr_lvl > PLAT_MAX_PWR_LVL)
146 return PSCI_E_INVALID_PARAMS;
147
148 /* Sanity check the requested state */
149 if (pstate == PSTATE_TYPE_STANDBY) {
150 /*
151 * It's probably to enter standby only on power level 0
152 * ignore any other power level.
153 */
154 if (pwr_lvl != MPIDR_AFFLVL0)
155 return PSCI_E_INVALID_PARAMS;
156
157 req_state->pwr_domain_state[MPIDR_AFFLVL0] =
158 PLAT_MAX_RET_STATE;
159 } else {
160 for (i = MPIDR_AFFLVL0; i <= pwr_lvl; i++)
161 req_state->pwr_domain_state[i] =
162 PLAT_MAX_OFF_STATE;
Tony Xie42e113e2016-07-16 11:16:51 +0800163
164 for (i = (pwr_lvl + 1); i <= PLAT_MAX_PWR_LVL; i++)
165 req_state->pwr_domain_state[i] =
166 PLAT_MAX_RET_STATE;
Tony Xief6118cc2016-01-15 17:17:32 +0800167 }
168
169 /* We expect the 'state id' to be zero */
170 if (psci_get_pstate_id(power_state))
171 return PSCI_E_INVALID_PARAMS;
172
173 return PSCI_E_SUCCESS;
174}
175
176void rockchip_get_sys_suspend_power_state(psci_power_state_t *req_state)
177{
178 int i;
179
180 for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++)
181 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
182}
183
184/*******************************************************************************
185 * RockChip handler called when a CPU is about to enter standby.
186 ******************************************************************************/
187void rockchip_cpu_standby(plat_local_state_t cpu_state)
188{
Louis Mayencourt1c819c32020-01-24 13:30:28 +0000189 u_register_t scr;
Tony Xief6118cc2016-01-15 17:17:32 +0800190
191 assert(cpu_state == PLAT_MAX_RET_STATE);
192
193 scr = read_scr_el3();
194 /* Enable PhysicalIRQ bit for NS world to wake the CPU */
195 write_scr_el3(scr | SCR_IRQ_BIT);
196 isb();
197 dsb();
198 wfi();
199
200 /*
201 * Restore SCR to the original value, synchronisation of scr_el3 is
202 * done by eret while el3_exit to save some execution cycles.
203 */
204 write_scr_el3(scr);
205}
206
207/*******************************************************************************
208 * RockChip handler called when a power domain is about to be turned on. The
209 * mpidr determines the CPU to be turned on.
210 ******************************************************************************/
211int rockchip_pwr_domain_on(u_register_t mpidr)
212{
tony.xie422d51c2017-03-01 11:05:17 +0800213 return rockchip_soc_cores_pwr_dm_on(mpidr, rockchip_sec_entrypoint);
Tony Xief6118cc2016-01-15 17:17:32 +0800214}
215
216/*******************************************************************************
217 * RockChip handler called when a power domain is about to be turned off. The
218 * target_state encodes the power state that each level should transition to.
219 ******************************************************************************/
220void rockchip_pwr_domain_off(const psci_power_state_t *target_state)
221{
Tony Xie42e113e2016-07-16 11:16:51 +0800222 uint32_t lvl;
223 plat_local_state_t lvl_state;
tony.xie422d51c2017-03-01 11:05:17 +0800224 int ret;
Tony Xie42e113e2016-07-16 11:16:51 +0800225
Tony Xief6118cc2016-01-15 17:17:32 +0800226 assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
227
228 plat_rockchip_gic_cpuif_disable();
229
230 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
231 plat_cci_disable();
Tony Xie42e113e2016-07-16 11:16:51 +0800232
tony.xie422d51c2017-03-01 11:05:17 +0800233 rockchip_soc_cores_pwr_dm_off();
Tony Xie42e113e2016-07-16 11:16:51 +0800234
235 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
236 lvl_state = target_state->pwr_domain_state[lvl];
tony.xie422d51c2017-03-01 11:05:17 +0800237 ret = rockchip_soc_hlvl_pwr_dm_off(lvl, lvl_state);
238 if (ret == PSCI_E_NOT_SUPPORTED)
239 break;
Tony Xie42e113e2016-07-16 11:16:51 +0800240 }
Tony Xief6118cc2016-01-15 17:17:32 +0800241}
242
243/*******************************************************************************
244 * RockChip handler called when a power domain is about to be suspended. The
245 * target_state encodes the power state that each level should transition to.
246 ******************************************************************************/
247void rockchip_pwr_domain_suspend(const psci_power_state_t *target_state)
248{
Tony Xie42e113e2016-07-16 11:16:51 +0800249 uint32_t lvl;
250 plat_local_state_t lvl_state;
tony.xie422d51c2017-03-01 11:05:17 +0800251 int ret;
Tony Xief6118cc2016-01-15 17:17:32 +0800252
Tony Xie42e113e2016-07-16 11:16:51 +0800253 if (RK_CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
254 return;
Tony Xief6118cc2016-01-15 17:17:32 +0800255
Derek Basehorea169a802018-01-25 22:05:41 -0800256 /* Prevent interrupts from spuriously waking up this cpu */
257 plat_rockchip_gic_cpuif_disable();
258
tony.xie422d51c2017-03-01 11:05:17 +0800259 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
260 rockchip_soc_sys_pwr_dm_suspend();
261 else
262 rockchip_soc_cores_pwr_dm_suspend();
Tony Xief6118cc2016-01-15 17:17:32 +0800263
Tony Xief6118cc2016-01-15 17:17:32 +0800264 /* Perform the common cluster specific operations */
265 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
266 plat_cci_disable();
Tony Xie42e113e2016-07-16 11:16:51 +0800267
Tony Xie6d7d93c2016-09-02 11:13:38 -0700268 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
269 return;
270
Tony Xie42e113e2016-07-16 11:16:51 +0800271 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
272 lvl_state = target_state->pwr_domain_state[lvl];
tony.xie422d51c2017-03-01 11:05:17 +0800273 ret = rockchip_soc_hlvl_pwr_dm_suspend(lvl, lvl_state);
274 if (ret == PSCI_E_NOT_SUPPORTED)
275 break;
Tony Xie42e113e2016-07-16 11:16:51 +0800276 }
Tony Xief6118cc2016-01-15 17:17:32 +0800277}
278
279/*******************************************************************************
280 * RockChip handler called when a power domain has just been powered on after
281 * being turned off earlier. The target_state encodes the low power state that
282 * each level has woken up from.
283 ******************************************************************************/
284void rockchip_pwr_domain_on_finish(const psci_power_state_t *target_state)
285{
Tony Xie42e113e2016-07-16 11:16:51 +0800286 uint32_t lvl;
287 plat_local_state_t lvl_state;
tony.xie422d51c2017-03-01 11:05:17 +0800288 int ret;
Tony Xie42e113e2016-07-16 11:16:51 +0800289
Tony Xief6118cc2016-01-15 17:17:32 +0800290 assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
291
tony.xie422d51c2017-03-01 11:05:17 +0800292 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
293 lvl_state = target_state->pwr_domain_state[lvl];
294 ret = rockchip_soc_hlvl_pwr_dm_on_finish(lvl, lvl_state);
295 if (ret == PSCI_E_NOT_SUPPORTED)
296 break;
Tony Xie42e113e2016-07-16 11:16:51 +0800297 }
298
tony.xie422d51c2017-03-01 11:05:17 +0800299 rockchip_soc_cores_pwr_dm_on_finish();
Tony Xief6118cc2016-01-15 17:17:32 +0800300
301 /* Perform the common cluster specific operations */
302 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
303 /* Enable coherency if this cluster was off */
304 plat_cci_enable();
305 }
306
307 /* Enable the gic cpu interface */
308 plat_rockchip_gic_pcpu_init();
309
310 /* Program the gic per-cpu distributor or re-distributor interface */
311 plat_rockchip_gic_cpuif_enable();
312}
313
314/*******************************************************************************
315 * RockChip handler called when a power domain has just been powered on after
316 * having been suspended earlier. The target_state encodes the low power state
317 * that each level has woken up from.
318 * TODO: At the moment we reuse the on finisher and reinitialize the secure
319 * context. Need to implement a separate suspend finisher.
320 ******************************************************************************/
321void rockchip_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
322{
Tony Xie42e113e2016-07-16 11:16:51 +0800323 uint32_t lvl;
324 plat_local_state_t lvl_state;
tony.xie422d51c2017-03-01 11:05:17 +0800325 int ret;
Tony Xie42e113e2016-07-16 11:16:51 +0800326
Tony Xief6118cc2016-01-15 17:17:32 +0800327 /* Nothing to be done on waking up from retention from CPU level */
Tony Xie42e113e2016-07-16 11:16:51 +0800328 if (RK_CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
Tony Xief6118cc2016-01-15 17:17:32 +0800329 return;
330
Tony Xie6d7d93c2016-09-02 11:13:38 -0700331 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
tony.xie422d51c2017-03-01 11:05:17 +0800332 rockchip_soc_sys_pwr_dm_resume();
Tony Xie6d7d93c2016-09-02 11:13:38 -0700333 goto comm_finish;
334 }
335
tony.xie422d51c2017-03-01 11:05:17 +0800336 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
337 lvl_state = target_state->pwr_domain_state[lvl];
338 ret = rockchip_soc_hlvl_pwr_dm_resume(lvl, lvl_state);
339 if (ret == PSCI_E_NOT_SUPPORTED)
340 break;
Tony Xie42e113e2016-07-16 11:16:51 +0800341 }
342
tony.xie422d51c2017-03-01 11:05:17 +0800343 rockchip_soc_cores_pwr_dm_resume();
344
Tony Xie42e113e2016-07-16 11:16:51 +0800345 /*
Tony Xie6d7d93c2016-09-02 11:13:38 -0700346 * Program the gic per-cpu distributor or re-distributor interface.
Caesar Wangda9e6de2016-09-10 06:26:11 +0800347 * For sys power domain operation, resuming of the gic needs to operate
tony.xie422d51c2017-03-01 11:05:17 +0800348 * in rockchip_soc_sys_pwr_dm_resume(), according to the sys power mode
Caesar Wangda9e6de2016-09-10 06:26:11 +0800349 * implements.
350 */
Tony Xie42e113e2016-07-16 11:16:51 +0800351 plat_rockchip_gic_cpuif_enable();
Tony Xief6118cc2016-01-15 17:17:32 +0800352
Tony Xie6d7d93c2016-09-02 11:13:38 -0700353comm_finish:
Tony Xief6118cc2016-01-15 17:17:32 +0800354 /* Perform the common cluster specific operations */
355 if (RK_CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
356 /* Enable coherency if this cluster was off */
357 plat_cci_enable();
358 }
359}
360
361/*******************************************************************************
362 * RockChip handlers to reboot the system
363 ******************************************************************************/
364static void __dead2 rockchip_system_reset(void)
365{
tony.xie422d51c2017-03-01 11:05:17 +0800366 rockchip_soc_soft_reset();
Tony Xief6118cc2016-01-15 17:17:32 +0800367}
368
369/*******************************************************************************
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800370 * RockChip handlers to power off the system
371 ******************************************************************************/
372static void __dead2 rockchip_system_poweroff(void)
373{
tony.xie422d51c2017-03-01 11:05:17 +0800374 rockchip_soc_system_off();
375}
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800376
Antonio Nino Diaz1d74beb2017-05-10 10:59:22 +0100377static void __dead2 rockchip_pd_pwr_down_wfi(
378 const psci_power_state_t *target_state)
379{
380 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
381 rockchip_soc_sys_pd_pwr_dn_wfi();
382 else
383 rockchip_soc_cores_pd_pwr_dn_wfi(target_state);
384}
385
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800386/*******************************************************************************
Tony Xief6118cc2016-01-15 17:17:32 +0800387 * Export the platform handlers via plat_rockchip_psci_pm_ops. The rockchip
388 * standard
389 * platform layer will take care of registering the handlers with PSCI.
390 ******************************************************************************/
391const plat_psci_ops_t plat_rockchip_psci_pm_ops = {
392 .cpu_standby = rockchip_cpu_standby,
393 .pwr_domain_on = rockchip_pwr_domain_on,
394 .pwr_domain_off = rockchip_pwr_domain_off,
395 .pwr_domain_suspend = rockchip_pwr_domain_suspend,
396 .pwr_domain_on_finish = rockchip_pwr_domain_on_finish,
397 .pwr_domain_suspend_finish = rockchip_pwr_domain_suspend_finish,
tony.xie54973e72017-04-24 16:18:10 +0800398 .pwr_domain_pwr_down_wfi = rockchip_pd_pwr_down_wfi,
Tony Xief6118cc2016-01-15 17:17:32 +0800399 .system_reset = rockchip_system_reset,
Caesar Wangd1b9d2d2016-05-25 19:05:19 +0800400 .system_off = rockchip_system_poweroff,
Tony Xief6118cc2016-01-15 17:17:32 +0800401 .validate_power_state = rockchip_validate_power_state,
402 .get_sys_suspend_power_state = rockchip_get_sys_suspend_power_state
403};
404
405int plat_setup_psci_ops(uintptr_t sec_entrypoint,
406 const plat_psci_ops_t **psci_ops)
407{
408 *psci_ops = &plat_rockchip_psci_pm_ops;
409 rockchip_sec_entrypoint = sec_entrypoint;
410 return 0;
411}
412
Tony Xie42e113e2016-07-16 11:16:51 +0800413uintptr_t plat_get_sec_entrypoint(void)
414{
415 assert(rockchip_sec_entrypoint);
416 return rockchip_sec_entrypoint;
417}