• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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