1 /*
2 * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved.
3 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7 #include <arch_helpers.h>
8 #include <assert.h>
9 #include <console.h>
10 #include <debug.h>
11 #include <delay_timer.h>
12 #include <errno.h>
13 #include <plat_private.h>
14 #include <platform_def.h>
15 #include <psci.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
25 static uintptr_t rockchip_sec_entrypoint;
26
27 #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
rockchip_soc_cores_pwr_dm_on(unsigned long mpidr,uint64_t entrypoint)43 int rockchip_soc_cores_pwr_dm_on(unsigned long mpidr, uint64_t entrypoint)
44 {
45 return PSCI_E_NOT_SUPPORTED;
46 }
47
rockchip_soc_hlvl_pwr_dm_off(uint32_t lvl,plat_local_state_t lvl_state)48 int 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
rockchip_soc_cores_pwr_dm_off(void)54 int rockchip_soc_cores_pwr_dm_off(void)
55 {
56 return PSCI_E_NOT_SUPPORTED;
57 }
58
rockchip_soc_sys_pwr_dm_suspend(void)59 int rockchip_soc_sys_pwr_dm_suspend(void)
60 {
61 return PSCI_E_NOT_SUPPORTED;
62 }
63
rockchip_soc_cores_pwr_dm_suspend(void)64 int rockchip_soc_cores_pwr_dm_suspend(void)
65 {
66 return PSCI_E_NOT_SUPPORTED;
67 }
68
rockchip_soc_hlvl_pwr_dm_suspend(uint32_t lvl,plat_local_state_t lvl_state)69 int 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
rockchip_soc_hlvl_pwr_dm_on_finish(uint32_t lvl,plat_local_state_t lvl_state)75 int 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
rockchip_soc_cores_pwr_dm_on_finish(void)81 int rockchip_soc_cores_pwr_dm_on_finish(void)
82 {
83 return PSCI_E_NOT_SUPPORTED;
84 }
85
rockchip_soc_sys_pwr_dm_resume(void)86 int rockchip_soc_sys_pwr_dm_resume(void)
87 {
88 return PSCI_E_NOT_SUPPORTED;
89 }
90
rockchip_soc_hlvl_pwr_dm_resume(uint32_t lvl,plat_local_state_t lvl_state)91 int 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
rockchip_soc_cores_pwr_dm_resume(void)97 int rockchip_soc_cores_pwr_dm_resume(void)
98 {
99 return PSCI_E_NOT_SUPPORTED;
100 }
101
rockchip_soc_soft_reset(void)102 void __dead2 rockchip_soc_soft_reset(void)
103 {
104 while (1)
105 ;
106 }
107
rockchip_soc_system_off(void)108 void __dead2 rockchip_soc_system_off(void)
109 {
110 while (1)
111 ;
112 }
113
rockchip_soc_cores_pd_pwr_dn_wfi(const psci_power_state_t * target_state)114 void __dead2 rockchip_soc_cores_pd_pwr_dn_wfi(
115 const psci_power_state_t *target_state)
116 {
117 psci_power_down_wfi();
118 }
119
rockchip_soc_sys_pd_pwr_dn_wfi(void)120 void __dead2 rockchip_soc_sys_pd_pwr_dn_wfi(void)
121 {
122 psci_power_down_wfi();
123 }
124
125 /*******************************************************************************
126 * Rockchip standard platform handler called to check the validity of the power
127 * state parameter.
128 ******************************************************************************/
rockchip_validate_power_state(unsigned int power_state,psci_power_state_t * req_state)129 int 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;
156
157 for (i = (pwr_lvl + 1); i <= PLAT_MAX_PWR_LVL; i++)
158 req_state->pwr_domain_state[i] =
159 PLAT_MAX_RET_STATE;
160 }
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
rockchip_get_sys_suspend_power_state(psci_power_state_t * req_state)169 void 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 ******************************************************************************/
rockchip_cpu_standby(plat_local_state_t cpu_state)180 void 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 ******************************************************************************/
rockchip_pwr_domain_on(u_register_t mpidr)204 int rockchip_pwr_domain_on(u_register_t mpidr)
205 {
206 return rockchip_soc_cores_pwr_dm_on(mpidr, rockchip_sec_entrypoint);
207 }
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 ******************************************************************************/
rockchip_pwr_domain_off(const psci_power_state_t * target_state)213 void rockchip_pwr_domain_off(const psci_power_state_t *target_state)
214 {
215 uint32_t lvl;
216 plat_local_state_t lvl_state;
217 int ret;
218
219 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();
225
226 rockchip_soc_cores_pwr_dm_off();
227
228 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
229 lvl_state = target_state->pwr_domain_state[lvl];
230 ret = rockchip_soc_hlvl_pwr_dm_off(lvl, lvl_state);
231 if (ret == PSCI_E_NOT_SUPPORTED)
232 break;
233 }
234 }
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 ******************************************************************************/
rockchip_pwr_domain_suspend(const psci_power_state_t * target_state)240 void rockchip_pwr_domain_suspend(const psci_power_state_t *target_state)
241 {
242 uint32_t lvl;
243 plat_local_state_t lvl_state;
244 int ret;
245
246 if (RK_CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
247 return;
248
249 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();
253
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();
260
261 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE)
262 return;
263
264 for (lvl = MPIDR_AFFLVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
265 lvl_state = target_state->pwr_domain_state[lvl];
266 ret = rockchip_soc_hlvl_pwr_dm_suspend(lvl, lvl_state);
267 if (ret == PSCI_E_NOT_SUPPORTED)
268 break;
269 }
270 }
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 ******************************************************************************/
rockchip_pwr_domain_on_finish(const psci_power_state_t * target_state)277 void rockchip_pwr_domain_on_finish(const psci_power_state_t *target_state)
278 {
279 uint32_t lvl;
280 plat_local_state_t lvl_state;
281 int ret;
282
283 assert(RK_CORE_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE);
284
285 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;
290 }
291
292 rockchip_soc_cores_pwr_dm_on_finish();
293
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 ******************************************************************************/
rockchip_pwr_domain_suspend_finish(const psci_power_state_t * target_state)314 void rockchip_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
315 {
316 uint32_t lvl;
317 plat_local_state_t lvl_state;
318 int ret;
319
320 /* Nothing to be done on waking up from retention from CPU level */
321 if (RK_CORE_PWR_STATE(target_state) != PLAT_MAX_OFF_STATE)
322 return;
323
324 if (RK_SYSTEM_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
325 rockchip_soc_sys_pwr_dm_resume();
326 goto comm_finish;
327 }
328
329 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;
334 }
335
336 rockchip_soc_cores_pwr_dm_resume();
337
338 /*
339 * Program the gic per-cpu distributor or re-distributor interface.
340 * For sys power domain operation, resuming of the gic needs to operate
341 * in rockchip_soc_sys_pwr_dm_resume(), according to the sys power mode
342 * implements.
343 */
344 plat_rockchip_gic_cpuif_enable();
345
346 comm_finish:
347 /* 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 ******************************************************************************/
rockchip_system_reset(void)357 static void __dead2 rockchip_system_reset(void)
358 {
359 rockchip_soc_soft_reset();
360 }
361
362 /*******************************************************************************
363 * RockChip handlers to power off the system
364 ******************************************************************************/
rockchip_system_poweroff(void)365 static void __dead2 rockchip_system_poweroff(void)
366 {
367 rockchip_soc_system_off();
368 }
369
rockchip_pd_pwr_down_wfi(const psci_power_state_t * target_state)370 static 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
379 /*******************************************************************************
380 * 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 ******************************************************************************/
384 const 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,
391 .pwr_domain_pwr_down_wfi = rockchip_pd_pwr_down_wfi,
392 .system_reset = rockchip_system_reset,
393 .system_off = rockchip_system_poweroff,
394 .validate_power_state = rockchip_validate_power_state,
395 .get_sys_suspend_power_state = rockchip_get_sys_suspend_power_state
396 };
397
plat_setup_psci_ops(uintptr_t sec_entrypoint,const plat_psci_ops_t ** psci_ops)398 int 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
plat_get_sec_entrypoint(void)406 uintptr_t plat_get_sec_entrypoint(void)
407 {
408 assert(rockchip_sec_entrypoint);
409 return rockchip_sec_entrypoint;
410 }
411