xref: /arm-trusted-firmware/plat/arm/board/fvp/fvp_pm.c (revision 91f16700b400a8c0651d24a598fc48ee2997a0d7)
1*91f16700Schasinglulu /*
2*91f16700Schasinglulu  * Copyright (c) 2013-2021, ARM Limited and Contributors. All rights reserved.
3*91f16700Schasinglulu  *
4*91f16700Schasinglulu  * SPDX-License-Identifier: BSD-3-Clause
5*91f16700Schasinglulu  */
6*91f16700Schasinglulu 
7*91f16700Schasinglulu #include <assert.h>
8*91f16700Schasinglulu 
9*91f16700Schasinglulu #include <arch_features.h>
10*91f16700Schasinglulu #include <arch_helpers.h>
11*91f16700Schasinglulu #include <common/debug.h>
12*91f16700Schasinglulu #include <drivers/arm/gicv3.h>
13*91f16700Schasinglulu #include <drivers/arm/fvp/fvp_pwrc.h>
14*91f16700Schasinglulu #include <lib/extensions/spe.h>
15*91f16700Schasinglulu #include <lib/mmio.h>
16*91f16700Schasinglulu #include <lib/psci/psci.h>
17*91f16700Schasinglulu #include <plat/arm/common/arm_config.h>
18*91f16700Schasinglulu #include <plat/arm/common/plat_arm.h>
19*91f16700Schasinglulu #include <platform_def.h>
20*91f16700Schasinglulu 
21*91f16700Schasinglulu #include "fvp_private.h"
22*91f16700Schasinglulu #include "../drivers/arm/gic/v3/gicv3_private.h"
23*91f16700Schasinglulu 
24*91f16700Schasinglulu 
25*91f16700Schasinglulu #if ARM_RECOM_STATE_ID_ENC
26*91f16700Schasinglulu /*
27*91f16700Schasinglulu  *  The table storing the valid idle power states. Ensure that the
28*91f16700Schasinglulu  *  array entries are populated in ascending order of state-id to
29*91f16700Schasinglulu  *  enable us to use binary search during power state validation.
30*91f16700Schasinglulu  *  The table must be terminated by a NULL entry.
31*91f16700Schasinglulu  */
32*91f16700Schasinglulu const unsigned int arm_pm_idle_states[] = {
33*91f16700Schasinglulu 	/* State-id - 0x01 */
34*91f16700Schasinglulu 	arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_RET,
35*91f16700Schasinglulu 			ARM_PWR_LVL0, PSTATE_TYPE_STANDBY),
36*91f16700Schasinglulu 	/* State-id - 0x02 */
37*91f16700Schasinglulu 	arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_OFF,
38*91f16700Schasinglulu 			ARM_PWR_LVL0, PSTATE_TYPE_POWERDOWN),
39*91f16700Schasinglulu 	/* State-id - 0x22 */
40*91f16700Schasinglulu 	arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_OFF, ARM_LOCAL_STATE_OFF,
41*91f16700Schasinglulu 			ARM_PWR_LVL1, PSTATE_TYPE_POWERDOWN),
42*91f16700Schasinglulu 	/* State-id - 0x222 */
43*91f16700Schasinglulu 	arm_make_pwrstate_lvl2(ARM_LOCAL_STATE_OFF, ARM_LOCAL_STATE_OFF,
44*91f16700Schasinglulu 		ARM_LOCAL_STATE_OFF, ARM_PWR_LVL2, PSTATE_TYPE_POWERDOWN),
45*91f16700Schasinglulu 	0,
46*91f16700Schasinglulu };
47*91f16700Schasinglulu #endif
48*91f16700Schasinglulu 
49*91f16700Schasinglulu /*******************************************************************************
50*91f16700Schasinglulu  * Function which implements the common FVP specific operations to power down a
51*91f16700Schasinglulu  * cluster in response to a CPU_OFF or CPU_SUSPEND request.
52*91f16700Schasinglulu  ******************************************************************************/
53*91f16700Schasinglulu static void fvp_cluster_pwrdwn_common(void)
54*91f16700Schasinglulu {
55*91f16700Schasinglulu 	uint64_t mpidr = read_mpidr_el1();
56*91f16700Schasinglulu 
57*91f16700Schasinglulu 	/*
58*91f16700Schasinglulu 	 * On power down we need to disable statistical profiling extensions
59*91f16700Schasinglulu 	 * before exiting coherency.
60*91f16700Schasinglulu 	 */
61*91f16700Schasinglulu 	if (is_feat_spe_supported()) {
62*91f16700Schasinglulu 		spe_disable();
63*91f16700Schasinglulu 	}
64*91f16700Schasinglulu 
65*91f16700Schasinglulu 	/* Disable coherency if this cluster is to be turned off */
66*91f16700Schasinglulu 	fvp_interconnect_disable();
67*91f16700Schasinglulu 
68*91f16700Schasinglulu 	/* Program the power controller to turn the cluster off */
69*91f16700Schasinglulu 	fvp_pwrc_write_pcoffr(mpidr);
70*91f16700Schasinglulu }
71*91f16700Schasinglulu 
72*91f16700Schasinglulu /*
73*91f16700Schasinglulu  * Empty implementation of these hooks avoid setting the GICR_WAKER.Sleep bit
74*91f16700Schasinglulu  * on ARM GICv3 implementations on FVP. This is required, because FVP does not
75*91f16700Schasinglulu  * support SYSTEM_SUSPEND and it is `faked` in firmware. Hence, for wake up
76*91f16700Schasinglulu  * from `fake` system suspend the GIC must not be powered off.
77*91f16700Schasinglulu  */
78*91f16700Schasinglulu void arm_gicv3_distif_pre_save(unsigned int rdist_proc_num)
79*91f16700Schasinglulu {}
80*91f16700Schasinglulu 
81*91f16700Schasinglulu void arm_gicv3_distif_post_restore(unsigned int rdist_proc_num)
82*91f16700Schasinglulu {}
83*91f16700Schasinglulu 
84*91f16700Schasinglulu static void fvp_power_domain_on_finish_common(const psci_power_state_t *target_state)
85*91f16700Schasinglulu {
86*91f16700Schasinglulu 	unsigned long mpidr;
87*91f16700Schasinglulu 
88*91f16700Schasinglulu 	assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
89*91f16700Schasinglulu 					ARM_LOCAL_STATE_OFF);
90*91f16700Schasinglulu 
91*91f16700Schasinglulu 	/* Get the mpidr for this cpu */
92*91f16700Schasinglulu 	mpidr = read_mpidr_el1();
93*91f16700Schasinglulu 
94*91f16700Schasinglulu 	/* Perform the common cluster specific operations */
95*91f16700Schasinglulu 	if (target_state->pwr_domain_state[ARM_PWR_LVL1] ==
96*91f16700Schasinglulu 					ARM_LOCAL_STATE_OFF) {
97*91f16700Schasinglulu 		/*
98*91f16700Schasinglulu 		 * This CPU might have woken up whilst the cluster was
99*91f16700Schasinglulu 		 * attempting to power down. In this case the FVP power
100*91f16700Schasinglulu 		 * controller will have a pending cluster power off request
101*91f16700Schasinglulu 		 * which needs to be cleared by writing to the PPONR register.
102*91f16700Schasinglulu 		 * This prevents the power controller from interpreting a
103*91f16700Schasinglulu 		 * subsequent entry of this cpu into a simple wfi as a power
104*91f16700Schasinglulu 		 * down request.
105*91f16700Schasinglulu 		 */
106*91f16700Schasinglulu 		fvp_pwrc_write_pponr(mpidr);
107*91f16700Schasinglulu 
108*91f16700Schasinglulu 		/* Enable coherency if this cluster was off */
109*91f16700Schasinglulu 		fvp_interconnect_enable();
110*91f16700Schasinglulu 	}
111*91f16700Schasinglulu 	/* Perform the common system specific operations */
112*91f16700Schasinglulu 	if (target_state->pwr_domain_state[ARM_PWR_LVL2] ==
113*91f16700Schasinglulu 						ARM_LOCAL_STATE_OFF)
114*91f16700Schasinglulu 		arm_system_pwr_domain_resume();
115*91f16700Schasinglulu 
116*91f16700Schasinglulu 	/*
117*91f16700Schasinglulu 	 * Clear PWKUPR.WEN bit to ensure interrupts do not interfere
118*91f16700Schasinglulu 	 * with a cpu power down unless the bit is set again
119*91f16700Schasinglulu 	 */
120*91f16700Schasinglulu 	fvp_pwrc_clr_wen(mpidr);
121*91f16700Schasinglulu }
122*91f16700Schasinglulu 
123*91f16700Schasinglulu /*******************************************************************************
124*91f16700Schasinglulu  * FVP handler called when a CPU is about to enter standby.
125*91f16700Schasinglulu  ******************************************************************************/
126*91f16700Schasinglulu static void fvp_cpu_standby(plat_local_state_t cpu_state)
127*91f16700Schasinglulu {
128*91f16700Schasinglulu 	u_register_t scr = read_scr_el3();
129*91f16700Schasinglulu 
130*91f16700Schasinglulu 	assert(cpu_state == ARM_LOCAL_STATE_RET);
131*91f16700Schasinglulu 
132*91f16700Schasinglulu 	/*
133*91f16700Schasinglulu 	 * Enable the Non-secure interrupt to wake the CPU.
134*91f16700Schasinglulu 	 * In GICv3 affinity routing mode, the Non-secure Group 1 interrupts
135*91f16700Schasinglulu 	 * use Physical FIQ at EL3 whereas in GICv2, Physical IRQ is used.
136*91f16700Schasinglulu 	 * Enabling both the bits works for both GICv2 mode and GICv3 affinity
137*91f16700Schasinglulu 	 * routing mode.
138*91f16700Schasinglulu 	 */
139*91f16700Schasinglulu 	write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT);
140*91f16700Schasinglulu 	isb();
141*91f16700Schasinglulu 
142*91f16700Schasinglulu 	/*
143*91f16700Schasinglulu 	 * Enter standby state.
144*91f16700Schasinglulu 	 * dsb is good practice before using wfi to enter low power states.
145*91f16700Schasinglulu 	 */
146*91f16700Schasinglulu 	dsb();
147*91f16700Schasinglulu 	wfi();
148*91f16700Schasinglulu 
149*91f16700Schasinglulu 	/*
150*91f16700Schasinglulu 	 * Restore SCR_EL3 to the original value, synchronisation of SCR_EL3
151*91f16700Schasinglulu 	 * is done by eret in el3_exit() to save some execution cycles.
152*91f16700Schasinglulu 	 */
153*91f16700Schasinglulu 	write_scr_el3(scr);
154*91f16700Schasinglulu }
155*91f16700Schasinglulu 
156*91f16700Schasinglulu /*******************************************************************************
157*91f16700Schasinglulu  * FVP handler called when a power domain is about to be turned on. The
158*91f16700Schasinglulu  * mpidr determines the CPU to be turned on.
159*91f16700Schasinglulu  ******************************************************************************/
160*91f16700Schasinglulu static int fvp_pwr_domain_on(u_register_t mpidr)
161*91f16700Schasinglulu {
162*91f16700Schasinglulu 	int rc = PSCI_E_SUCCESS;
163*91f16700Schasinglulu 	unsigned int psysr;
164*91f16700Schasinglulu 
165*91f16700Schasinglulu 	/*
166*91f16700Schasinglulu 	 * Ensure that we do not cancel an inflight power off request for the
167*91f16700Schasinglulu 	 * target cpu. That would leave it in a zombie wfi. Wait for it to power
168*91f16700Schasinglulu 	 * off and then program the power controller to turn that CPU on.
169*91f16700Schasinglulu 	 */
170*91f16700Schasinglulu 	do {
171*91f16700Schasinglulu 		psysr = fvp_pwrc_read_psysr(mpidr);
172*91f16700Schasinglulu 	} while ((psysr & PSYSR_AFF_L0) != 0U);
173*91f16700Schasinglulu 
174*91f16700Schasinglulu 	fvp_pwrc_write_pponr(mpidr);
175*91f16700Schasinglulu 	return rc;
176*91f16700Schasinglulu }
177*91f16700Schasinglulu 
178*91f16700Schasinglulu /*******************************************************************************
179*91f16700Schasinglulu  * FVP handler called when a power domain is about to be turned off. The
180*91f16700Schasinglulu  * target_state encodes the power state that each level should transition to.
181*91f16700Schasinglulu  ******************************************************************************/
182*91f16700Schasinglulu static void fvp_pwr_domain_off(const psci_power_state_t *target_state)
183*91f16700Schasinglulu {
184*91f16700Schasinglulu 	assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
185*91f16700Schasinglulu 					ARM_LOCAL_STATE_OFF);
186*91f16700Schasinglulu 
187*91f16700Schasinglulu 	/*
188*91f16700Schasinglulu 	 * If execution reaches this stage then this power domain will be
189*91f16700Schasinglulu 	 * suspended. Perform at least the cpu specific actions followed
190*91f16700Schasinglulu 	 * by the cluster specific operations if applicable.
191*91f16700Schasinglulu 	 */
192*91f16700Schasinglulu 
193*91f16700Schasinglulu 	/* Prevent interrupts from spuriously waking up this cpu */
194*91f16700Schasinglulu 	plat_arm_gic_cpuif_disable();
195*91f16700Schasinglulu 
196*91f16700Schasinglulu 	/* Turn redistributor off */
197*91f16700Schasinglulu 	plat_arm_gic_redistif_off();
198*91f16700Schasinglulu 
199*91f16700Schasinglulu 	/* Program the power controller to power off this cpu. */
200*91f16700Schasinglulu 	fvp_pwrc_write_ppoffr(read_mpidr_el1());
201*91f16700Schasinglulu 
202*91f16700Schasinglulu 	if (target_state->pwr_domain_state[ARM_PWR_LVL1] ==
203*91f16700Schasinglulu 					ARM_LOCAL_STATE_OFF)
204*91f16700Schasinglulu 		fvp_cluster_pwrdwn_common();
205*91f16700Schasinglulu 
206*91f16700Schasinglulu }
207*91f16700Schasinglulu 
208*91f16700Schasinglulu /*******************************************************************************
209*91f16700Schasinglulu  * FVP handler called when a power domain is about to be suspended. The
210*91f16700Schasinglulu  * target_state encodes the power state that each level should transition to.
211*91f16700Schasinglulu  ******************************************************************************/
212*91f16700Schasinglulu static void fvp_pwr_domain_suspend(const psci_power_state_t *target_state)
213*91f16700Schasinglulu {
214*91f16700Schasinglulu 	unsigned long mpidr;
215*91f16700Schasinglulu 
216*91f16700Schasinglulu 	/*
217*91f16700Schasinglulu 	 * FVP has retention only at cpu level. Just return
218*91f16700Schasinglulu 	 * as nothing is to be done for retention.
219*91f16700Schasinglulu 	 */
220*91f16700Schasinglulu 	if (target_state->pwr_domain_state[ARM_PWR_LVL0] ==
221*91f16700Schasinglulu 					ARM_LOCAL_STATE_RET)
222*91f16700Schasinglulu 		return;
223*91f16700Schasinglulu 
224*91f16700Schasinglulu 	assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
225*91f16700Schasinglulu 					ARM_LOCAL_STATE_OFF);
226*91f16700Schasinglulu 
227*91f16700Schasinglulu 	/* Get the mpidr for this cpu */
228*91f16700Schasinglulu 	mpidr = read_mpidr_el1();
229*91f16700Schasinglulu 
230*91f16700Schasinglulu 	/* Program the power controller to enable wakeup interrupts. */
231*91f16700Schasinglulu 	fvp_pwrc_set_wen(mpidr);
232*91f16700Schasinglulu 
233*91f16700Schasinglulu 	/* Prevent interrupts from spuriously waking up this cpu */
234*91f16700Schasinglulu 	plat_arm_gic_cpuif_disable();
235*91f16700Schasinglulu 
236*91f16700Schasinglulu 	/*
237*91f16700Schasinglulu 	 * The Redistributor is not powered off as it can potentially prevent
238*91f16700Schasinglulu 	 * wake up events reaching the CPUIF and/or might lead to losing
239*91f16700Schasinglulu 	 * register context.
240*91f16700Schasinglulu 	 */
241*91f16700Schasinglulu 
242*91f16700Schasinglulu 	/* Perform the common cluster specific operations */
243*91f16700Schasinglulu 	if (target_state->pwr_domain_state[ARM_PWR_LVL1] ==
244*91f16700Schasinglulu 					ARM_LOCAL_STATE_OFF)
245*91f16700Schasinglulu 		fvp_cluster_pwrdwn_common();
246*91f16700Schasinglulu 
247*91f16700Schasinglulu 	/* Perform the common system specific operations */
248*91f16700Schasinglulu 	if (target_state->pwr_domain_state[ARM_PWR_LVL2] ==
249*91f16700Schasinglulu 						ARM_LOCAL_STATE_OFF)
250*91f16700Schasinglulu 		arm_system_pwr_domain_save();
251*91f16700Schasinglulu 
252*91f16700Schasinglulu 	/* Program the power controller to power off this cpu. */
253*91f16700Schasinglulu 	fvp_pwrc_write_ppoffr(read_mpidr_el1());
254*91f16700Schasinglulu 
255*91f16700Schasinglulu 	return;
256*91f16700Schasinglulu }
257*91f16700Schasinglulu 
258*91f16700Schasinglulu /*******************************************************************************
259*91f16700Schasinglulu  * FVP handler called when a power domain has just been powered on after
260*91f16700Schasinglulu  * being turned off earlier. The target_state encodes the low power state that
261*91f16700Schasinglulu  * each level has woken up from.
262*91f16700Schasinglulu  ******************************************************************************/
263*91f16700Schasinglulu static void fvp_pwr_domain_on_finish(const psci_power_state_t *target_state)
264*91f16700Schasinglulu {
265*91f16700Schasinglulu 	fvp_power_domain_on_finish_common(target_state);
266*91f16700Schasinglulu 
267*91f16700Schasinglulu }
268*91f16700Schasinglulu 
269*91f16700Schasinglulu /*******************************************************************************
270*91f16700Schasinglulu  * FVP handler called when a power domain has just been powered on and the cpu
271*91f16700Schasinglulu  * and its cluster are fully participating in coherent transaction on the
272*91f16700Schasinglulu  * interconnect. Data cache must be enabled for CPU at this point.
273*91f16700Schasinglulu  ******************************************************************************/
274*91f16700Schasinglulu static void fvp_pwr_domain_on_finish_late(const psci_power_state_t *target_state)
275*91f16700Schasinglulu {
276*91f16700Schasinglulu 	/* Program GIC per-cpu distributor or re-distributor interface */
277*91f16700Schasinglulu 	plat_arm_gic_pcpu_init();
278*91f16700Schasinglulu 
279*91f16700Schasinglulu 	/* Enable GIC CPU interface */
280*91f16700Schasinglulu 	plat_arm_gic_cpuif_enable();
281*91f16700Schasinglulu }
282*91f16700Schasinglulu 
283*91f16700Schasinglulu /*******************************************************************************
284*91f16700Schasinglulu  * FVP handler called when a power domain has just been powered on after
285*91f16700Schasinglulu  * having been suspended earlier. The target_state encodes the low power state
286*91f16700Schasinglulu  * that each level has woken up from.
287*91f16700Schasinglulu  * TODO: At the moment we reuse the on finisher and reinitialize the secure
288*91f16700Schasinglulu  * context. Need to implement a separate suspend finisher.
289*91f16700Schasinglulu  ******************************************************************************/
290*91f16700Schasinglulu static void fvp_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
291*91f16700Schasinglulu {
292*91f16700Schasinglulu 	/*
293*91f16700Schasinglulu 	 * Nothing to be done on waking up from retention from CPU level.
294*91f16700Schasinglulu 	 */
295*91f16700Schasinglulu 	if (target_state->pwr_domain_state[ARM_PWR_LVL0] ==
296*91f16700Schasinglulu 					ARM_LOCAL_STATE_RET)
297*91f16700Schasinglulu 		return;
298*91f16700Schasinglulu 
299*91f16700Schasinglulu 	fvp_power_domain_on_finish_common(target_state);
300*91f16700Schasinglulu 
301*91f16700Schasinglulu 	/* Enable GIC CPU interface */
302*91f16700Schasinglulu 	plat_arm_gic_cpuif_enable();
303*91f16700Schasinglulu }
304*91f16700Schasinglulu 
305*91f16700Schasinglulu /*******************************************************************************
306*91f16700Schasinglulu  * FVP handlers to shutdown/reboot the system
307*91f16700Schasinglulu  ******************************************************************************/
308*91f16700Schasinglulu static void __dead2 fvp_system_off(void)
309*91f16700Schasinglulu {
310*91f16700Schasinglulu 	/* Write the System Configuration Control Register */
311*91f16700Schasinglulu 	mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL,
312*91f16700Schasinglulu 		V2M_CFGCTRL_START |
313*91f16700Schasinglulu 		V2M_CFGCTRL_RW |
314*91f16700Schasinglulu 		V2M_CFGCTRL_FUNC(V2M_FUNC_SHUTDOWN));
315*91f16700Schasinglulu 	wfi();
316*91f16700Schasinglulu 	ERROR("FVP System Off: operation not handled.\n");
317*91f16700Schasinglulu 	panic();
318*91f16700Schasinglulu }
319*91f16700Schasinglulu 
320*91f16700Schasinglulu static void __dead2 fvp_system_reset(void)
321*91f16700Schasinglulu {
322*91f16700Schasinglulu 	/* Write the System Configuration Control Register */
323*91f16700Schasinglulu 	mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL,
324*91f16700Schasinglulu 		V2M_CFGCTRL_START |
325*91f16700Schasinglulu 		V2M_CFGCTRL_RW |
326*91f16700Schasinglulu 		V2M_CFGCTRL_FUNC(V2M_FUNC_REBOOT));
327*91f16700Schasinglulu 	wfi();
328*91f16700Schasinglulu 	ERROR("FVP System Reset: operation not handled.\n");
329*91f16700Schasinglulu 	panic();
330*91f16700Schasinglulu }
331*91f16700Schasinglulu 
332*91f16700Schasinglulu static int fvp_node_hw_state(u_register_t target_cpu,
333*91f16700Schasinglulu 			     unsigned int power_level)
334*91f16700Schasinglulu {
335*91f16700Schasinglulu 	unsigned int psysr;
336*91f16700Schasinglulu 	int ret;
337*91f16700Schasinglulu 
338*91f16700Schasinglulu 	/*
339*91f16700Schasinglulu 	 * The format of 'power_level' is implementation-defined, but 0 must
340*91f16700Schasinglulu 	 * mean a CPU. We also allow 1 to denote the cluster
341*91f16700Schasinglulu 	 */
342*91f16700Schasinglulu 	if ((power_level != ARM_PWR_LVL0) && (power_level != ARM_PWR_LVL1))
343*91f16700Schasinglulu 		return PSCI_E_INVALID_PARAMS;
344*91f16700Schasinglulu 
345*91f16700Schasinglulu 	/*
346*91f16700Schasinglulu 	 * Read the status of the given MPDIR from FVP power controller. The
347*91f16700Schasinglulu 	 * power controller only gives us on/off status, so map that to expected
348*91f16700Schasinglulu 	 * return values of the PSCI call
349*91f16700Schasinglulu 	 */
350*91f16700Schasinglulu 	psysr = fvp_pwrc_read_psysr(target_cpu);
351*91f16700Schasinglulu 	if (psysr == PSYSR_INVALID)
352*91f16700Schasinglulu 		return PSCI_E_INVALID_PARAMS;
353*91f16700Schasinglulu 
354*91f16700Schasinglulu 	if (power_level == ARM_PWR_LVL0) {
355*91f16700Schasinglulu 		ret = ((psysr & PSYSR_AFF_L0) != 0U) ? HW_ON : HW_OFF;
356*91f16700Schasinglulu 	} else {
357*91f16700Schasinglulu 		/* power_level == ARM_PWR_LVL1 */
358*91f16700Schasinglulu 		ret = ((psysr & PSYSR_AFF_L1) != 0U) ? HW_ON : HW_OFF;
359*91f16700Schasinglulu 	}
360*91f16700Schasinglulu 
361*91f16700Schasinglulu 	return ret;
362*91f16700Schasinglulu }
363*91f16700Schasinglulu 
364*91f16700Schasinglulu /*
365*91f16700Schasinglulu  * The FVP doesn't truly support power management at SYSTEM power domain. The
366*91f16700Schasinglulu  * SYSTEM_SUSPEND will be down-graded to the cluster level within the platform
367*91f16700Schasinglulu  * layer. The `fake` SYSTEM_SUSPEND allows us to validate some of the driver
368*91f16700Schasinglulu  * save and restore sequences on FVP.
369*91f16700Schasinglulu  */
370*91f16700Schasinglulu #if !ARM_BL31_IN_DRAM
371*91f16700Schasinglulu static void fvp_get_sys_suspend_power_state(psci_power_state_t *req_state)
372*91f16700Schasinglulu {
373*91f16700Schasinglulu 	unsigned int i;
374*91f16700Schasinglulu 
375*91f16700Schasinglulu 	for (i = ARM_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++)
376*91f16700Schasinglulu 		req_state->pwr_domain_state[i] = ARM_LOCAL_STATE_OFF;
377*91f16700Schasinglulu 
378*91f16700Schasinglulu #if PSCI_OS_INIT_MODE
379*91f16700Schasinglulu 	req_state->last_at_pwrlvl = PLAT_MAX_PWR_LVL;
380*91f16700Schasinglulu #endif
381*91f16700Schasinglulu }
382*91f16700Schasinglulu #endif
383*91f16700Schasinglulu 
384*91f16700Schasinglulu /*******************************************************************************
385*91f16700Schasinglulu  * Handler to filter PSCI requests.
386*91f16700Schasinglulu  ******************************************************************************/
387*91f16700Schasinglulu /*
388*91f16700Schasinglulu  * The system power domain suspend is only supported only via
389*91f16700Schasinglulu  * PSCI SYSTEM_SUSPEND API. PSCI CPU_SUSPEND request to system power domain
390*91f16700Schasinglulu  * will be downgraded to the lower level.
391*91f16700Schasinglulu  */
392*91f16700Schasinglulu static int fvp_validate_power_state(unsigned int power_state,
393*91f16700Schasinglulu 			    psci_power_state_t *req_state)
394*91f16700Schasinglulu {
395*91f16700Schasinglulu 	int rc;
396*91f16700Schasinglulu 	rc = arm_validate_power_state(power_state, req_state);
397*91f16700Schasinglulu 
398*91f16700Schasinglulu 	/*
399*91f16700Schasinglulu 	 * Ensure that the system power domain level is never suspended
400*91f16700Schasinglulu 	 * via PSCI CPU SUSPEND API. Currently system suspend is only
401*91f16700Schasinglulu 	 * supported via PSCI SYSTEM SUSPEND API.
402*91f16700Schasinglulu 	 */
403*91f16700Schasinglulu 	req_state->pwr_domain_state[ARM_PWR_LVL2] = ARM_LOCAL_STATE_RUN;
404*91f16700Schasinglulu 	return rc;
405*91f16700Schasinglulu }
406*91f16700Schasinglulu 
407*91f16700Schasinglulu /*
408*91f16700Schasinglulu  * Custom `translate_power_state_by_mpidr` handler for FVP. Unlike in the
409*91f16700Schasinglulu  * `fvp_validate_power_state`, we do not downgrade the system power
410*91f16700Schasinglulu  * domain level request in `power_state` as it will be used to query the
411*91f16700Schasinglulu  * PSCI_STAT_COUNT/RESIDENCY at the system power domain level.
412*91f16700Schasinglulu  */
413*91f16700Schasinglulu static int fvp_translate_power_state_by_mpidr(u_register_t mpidr,
414*91f16700Schasinglulu 		unsigned int power_state,
415*91f16700Schasinglulu 		psci_power_state_t *output_state)
416*91f16700Schasinglulu {
417*91f16700Schasinglulu 	return arm_validate_power_state(power_state, output_state);
418*91f16700Schasinglulu }
419*91f16700Schasinglulu 
420*91f16700Schasinglulu /*******************************************************************************
421*91f16700Schasinglulu  * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard
422*91f16700Schasinglulu  * platform layer will take care of registering the handlers with PSCI.
423*91f16700Schasinglulu  ******************************************************************************/
424*91f16700Schasinglulu plat_psci_ops_t plat_arm_psci_pm_ops = {
425*91f16700Schasinglulu 	.cpu_standby = fvp_cpu_standby,
426*91f16700Schasinglulu 	.pwr_domain_on = fvp_pwr_domain_on,
427*91f16700Schasinglulu 	.pwr_domain_off = fvp_pwr_domain_off,
428*91f16700Schasinglulu 	.pwr_domain_suspend = fvp_pwr_domain_suspend,
429*91f16700Schasinglulu 	.pwr_domain_on_finish = fvp_pwr_domain_on_finish,
430*91f16700Schasinglulu 	.pwr_domain_on_finish_late = fvp_pwr_domain_on_finish_late,
431*91f16700Schasinglulu 	.pwr_domain_suspend_finish = fvp_pwr_domain_suspend_finish,
432*91f16700Schasinglulu 	.system_off = fvp_system_off,
433*91f16700Schasinglulu 	.system_reset = fvp_system_reset,
434*91f16700Schasinglulu 	.validate_power_state = fvp_validate_power_state,
435*91f16700Schasinglulu 	.validate_ns_entrypoint = arm_validate_psci_entrypoint,
436*91f16700Schasinglulu 	.translate_power_state_by_mpidr = fvp_translate_power_state_by_mpidr,
437*91f16700Schasinglulu 	.get_node_hw_state = fvp_node_hw_state,
438*91f16700Schasinglulu #if !ARM_BL31_IN_DRAM
439*91f16700Schasinglulu 	/*
440*91f16700Schasinglulu 	 * The TrustZone Controller is set up during the warmboot sequence after
441*91f16700Schasinglulu 	 * resuming the CPU from a SYSTEM_SUSPEND. If BL31 is located in SRAM
442*91f16700Schasinglulu 	 * this is  not a problem but, if it is in TZC-secured DRAM, it tries to
443*91f16700Schasinglulu 	 * reconfigure the same memory it is running on, causing an exception.
444*91f16700Schasinglulu 	 */
445*91f16700Schasinglulu 	.get_sys_suspend_power_state = fvp_get_sys_suspend_power_state,
446*91f16700Schasinglulu #endif
447*91f16700Schasinglulu 	.mem_protect_chk	= arm_psci_mem_protect_chk,
448*91f16700Schasinglulu 	.read_mem_protect	= arm_psci_read_mem_protect,
449*91f16700Schasinglulu 	.write_mem_protect	= arm_nor_psci_write_mem_protect,
450*91f16700Schasinglulu };
451*91f16700Schasinglulu 
452*91f16700Schasinglulu const plat_psci_ops_t *plat_arm_psci_override_pm_ops(plat_psci_ops_t *ops)
453*91f16700Schasinglulu {
454*91f16700Schasinglulu 	return ops;
455*91f16700Schasinglulu }
456