xref: /rk3399_ARM-atf/plat/arm/board/fvp/fvp_pm.c (revision e75cc247c744d21e52f834a442bf1c26d0ab6161)
13fc4124cSDan Handley /*
23202ce8bSAlexei Fedorov  * Copyright (c) 2013-2021, ARM Limited and Contributors. All rights reserved.
33fc4124cSDan Handley  *
482cb2c1aSdp-arm  * SPDX-License-Identifier: BSD-3-Clause
53fc4124cSDan Handley  */
63fc4124cSDan Handley 
73fc4124cSDan Handley #include <assert.h>
809d40e0eSAntonio Nino Diaz 
909d40e0eSAntonio Nino Diaz #include <arch_helpers.h>
1009d40e0eSAntonio Nino Diaz #include <common/debug.h>
1109d40e0eSAntonio Nino Diaz #include <drivers/arm/gicv3.h>
12560293bbSAntonio Nino Diaz #include <drivers/arm/fvp/fvp_pwrc.h>
1309d40e0eSAntonio Nino Diaz #include <lib/extensions/spe.h>
1409d40e0eSAntonio Nino Diaz #include <lib/mmio.h>
1509d40e0eSAntonio Nino Diaz #include <lib/psci/psci.h>
16bd9344f6SAntonio Nino Diaz #include <plat/arm/common/arm_config.h>
17bd9344f6SAntonio Nino Diaz #include <plat/arm/common/plat_arm.h>
18234bc7f8SAntonio Nino Diaz #include <platform_def.h>
1909d40e0eSAntonio Nino Diaz 
203fc4124cSDan Handley #include "fvp_private.h"
21609e053cSAmbroise Vincent #include "../drivers/arm/gic/v3/gicv3_private.h"
223fc4124cSDan Handley 
233fc4124cSDan Handley 
242204afdeSSoby Mathew #if ARM_RECOM_STATE_ID_ENC
252204afdeSSoby Mathew /*
262204afdeSSoby Mathew  *  The table storing the valid idle power states. Ensure that the
272204afdeSSoby Mathew  *  array entries are populated in ascending order of state-id to
282204afdeSSoby Mathew  *  enable us to use binary search during power state validation.
292204afdeSSoby Mathew  *  The table must be terminated by a NULL entry.
302204afdeSSoby Mathew  */
312204afdeSSoby Mathew const unsigned int arm_pm_idle_states[] = {
322204afdeSSoby Mathew 	/* State-id - 0x01 */
332204afdeSSoby Mathew 	arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_RET,
342204afdeSSoby Mathew 			ARM_PWR_LVL0, PSTATE_TYPE_STANDBY),
352204afdeSSoby Mathew 	/* State-id - 0x02 */
362204afdeSSoby Mathew 	arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_OFF,
372204afdeSSoby Mathew 			ARM_PWR_LVL0, PSTATE_TYPE_POWERDOWN),
382204afdeSSoby Mathew 	/* State-id - 0x22 */
392204afdeSSoby Mathew 	arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_OFF, ARM_LOCAL_STATE_OFF,
402204afdeSSoby Mathew 			ARM_PWR_LVL1, PSTATE_TYPE_POWERDOWN),
41e35a3fb5SSoby Mathew 	/* State-id - 0x222 */
42e35a3fb5SSoby Mathew 	arm_make_pwrstate_lvl2(ARM_LOCAL_STATE_OFF, ARM_LOCAL_STATE_OFF,
43e35a3fb5SSoby Mathew 		ARM_LOCAL_STATE_OFF, ARM_PWR_LVL2, PSTATE_TYPE_POWERDOWN),
442204afdeSSoby Mathew 	0,
452204afdeSSoby Mathew };
462204afdeSSoby Mathew #endif
472204afdeSSoby Mathew 
483fc4124cSDan Handley /*******************************************************************************
493fc4124cSDan Handley  * Function which implements the common FVP specific operations to power down a
503fc4124cSDan Handley  * cluster in response to a CPU_OFF or CPU_SUSPEND request.
513fc4124cSDan Handley  ******************************************************************************/
523fc4124cSDan Handley static void fvp_cluster_pwrdwn_common(void)
533fc4124cSDan Handley {
543fc4124cSDan Handley 	uint64_t mpidr = read_mpidr_el1();
553fc4124cSDan Handley 
56d832aee9Sdp-arm #if ENABLE_SPE_FOR_LOWER_ELS
57d832aee9Sdp-arm 	/*
58d832aee9Sdp-arm 	 * On power down we need to disable statistical profiling extensions
59d832aee9Sdp-arm 	 * before exiting coherency.
60d832aee9Sdp-arm 	 */
61281a08ccSDimitris Papastamos 	spe_disable();
62d832aee9Sdp-arm #endif
63d832aee9Sdp-arm 
643fc4124cSDan Handley 	/* Disable coherency if this cluster is to be turned off */
656355f234SVikram Kanigiri 	fvp_interconnect_disable();
663fc4124cSDan Handley 
679cf7f355SMadhukar Pappireddy #if HW_ASSISTED_COHERENCY
689cf7f355SMadhukar Pappireddy 	uint32_t reg;
699cf7f355SMadhukar Pappireddy 
709cf7f355SMadhukar Pappireddy 	/*
719cf7f355SMadhukar Pappireddy 	 * If we have determined this core to be the last man standing and we
729cf7f355SMadhukar Pappireddy 	 * intend to power down the cluster proactively, we provide a hint to
739cf7f355SMadhukar Pappireddy 	 * the power controller that cluster power is not required when all
749cf7f355SMadhukar Pappireddy 	 * cores are powered down.
759cf7f355SMadhukar Pappireddy 	 * Note that this is only an advisory to power controller and is supported
769cf7f355SMadhukar Pappireddy 	 * by SoCs with DynamIQ Shared Units only.
779cf7f355SMadhukar Pappireddy 	 */
789cf7f355SMadhukar Pappireddy 	reg = read_clusterpwrdn();
799cf7f355SMadhukar Pappireddy 
809cf7f355SMadhukar Pappireddy 	/* Clear and set bit 0 : Cluster power not required */
819cf7f355SMadhukar Pappireddy 	reg &= ~DSU_CLUSTER_PWR_MASK;
829cf7f355SMadhukar Pappireddy 	reg |= DSU_CLUSTER_PWR_OFF;
839cf7f355SMadhukar Pappireddy 	write_clusterpwrdn(reg);
849cf7f355SMadhukar Pappireddy #endif
859cf7f355SMadhukar Pappireddy 
863fc4124cSDan Handley 	/* Program the power controller to turn the cluster off */
873fc4124cSDan Handley 	fvp_pwrc_write_pcoffr(mpidr);
883fc4124cSDan Handley }
893fc4124cSDan Handley 
90e35a3fb5SSoby Mathew /*
91e35a3fb5SSoby Mathew  * Empty implementation of these hooks avoid setting the GICR_WAKER.Sleep bit
92e35a3fb5SSoby Mathew  * on ARM GICv3 implementations on FVP. This is required, because FVP does not
93e35a3fb5SSoby Mathew  * support SYSTEM_SUSPEND and it is `faked` in firmware. Hence, for wake up
94e35a3fb5SSoby Mathew  * from `fake` system suspend the GIC must not be powered off.
95e35a3fb5SSoby Mathew  */
96dc6aad2eSRoberto Vargas void arm_gicv3_distif_pre_save(unsigned int rdist_proc_num)
97e35a3fb5SSoby Mathew {}
98e35a3fb5SSoby Mathew 
99dc6aad2eSRoberto Vargas void arm_gicv3_distif_post_restore(unsigned int rdist_proc_num)
100e35a3fb5SSoby Mathew {}
101e35a3fb5SSoby Mathew 
102f14d1886SSoby Mathew static void fvp_power_domain_on_finish_common(const psci_power_state_t *target_state)
103f14d1886SSoby Mathew {
104f14d1886SSoby Mathew 	unsigned long mpidr;
105f14d1886SSoby Mathew 
106f14d1886SSoby Mathew 	assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
107f14d1886SSoby Mathew 					ARM_LOCAL_STATE_OFF);
108f14d1886SSoby Mathew 
109f14d1886SSoby Mathew 	/* Get the mpidr for this cpu */
110f14d1886SSoby Mathew 	mpidr = read_mpidr_el1();
111f14d1886SSoby Mathew 
112f14d1886SSoby Mathew 	/* Perform the common cluster specific operations */
113f14d1886SSoby Mathew 	if (target_state->pwr_domain_state[ARM_PWR_LVL1] ==
114f14d1886SSoby Mathew 					ARM_LOCAL_STATE_OFF) {
115f14d1886SSoby Mathew 		/*
116f14d1886SSoby Mathew 		 * This CPU might have woken up whilst the cluster was
117f14d1886SSoby Mathew 		 * attempting to power down. In this case the FVP power
118f14d1886SSoby Mathew 		 * controller will have a pending cluster power off request
119f14d1886SSoby Mathew 		 * which needs to be cleared by writing to the PPONR register.
120f14d1886SSoby Mathew 		 * This prevents the power controller from interpreting a
121f14d1886SSoby Mathew 		 * subsequent entry of this cpu into a simple wfi as a power
122f14d1886SSoby Mathew 		 * down request.
123f14d1886SSoby Mathew 		 */
124f14d1886SSoby Mathew 		fvp_pwrc_write_pponr(mpidr);
125f14d1886SSoby Mathew 
126f14d1886SSoby Mathew 		/* Enable coherency if this cluster was off */
1276355f234SVikram Kanigiri 		fvp_interconnect_enable();
128f14d1886SSoby Mathew 	}
129e35a3fb5SSoby Mathew 	/* Perform the common system specific operations */
130e35a3fb5SSoby Mathew 	if (target_state->pwr_domain_state[ARM_PWR_LVL2] ==
131e35a3fb5SSoby Mathew 						ARM_LOCAL_STATE_OFF)
132e35a3fb5SSoby Mathew 		arm_system_pwr_domain_resume();
133f14d1886SSoby Mathew 
134f14d1886SSoby Mathew 	/*
135f14d1886SSoby Mathew 	 * Clear PWKUPR.WEN bit to ensure interrupts do not interfere
136f14d1886SSoby Mathew 	 * with a cpu power down unless the bit is set again
137f14d1886SSoby Mathew 	 */
138f14d1886SSoby Mathew 	fvp_pwrc_clr_wen(mpidr);
139f14d1886SSoby Mathew }
140f14d1886SSoby Mathew 
1413fc4124cSDan Handley /*******************************************************************************
14238dce70fSSoby Mathew  * FVP handler called when a CPU is about to enter standby.
1433fc4124cSDan Handley  ******************************************************************************/
1441af540efSRoberto Vargas static void fvp_cpu_standby(plat_local_state_t cpu_state)
1453fc4124cSDan Handley {
1463202ce8bSAlexei Fedorov 	u_register_t scr = read_scr_el3();
14738dce70fSSoby Mathew 
14838dce70fSSoby Mathew 	assert(cpu_state == ARM_LOCAL_STATE_RET);
14938dce70fSSoby Mathew 
1503fc4124cSDan Handley 	/*
1513202ce8bSAlexei Fedorov 	 * Enable the Non-secure interrupt to wake the CPU.
1523202ce8bSAlexei Fedorov 	 * In GICv3 affinity routing mode, the Non-secure Group 1 interrupts
1533202ce8bSAlexei Fedorov 	 * use Physical FIQ at EL3 whereas in GICv2, Physical IRQ is used.
1543202ce8bSAlexei Fedorov 	 * Enabling both the bits works for both GICv2 mode and GICv3 affinity
1553202ce8bSAlexei Fedorov 	 * routing mode.
1563202ce8bSAlexei Fedorov 	 */
1573202ce8bSAlexei Fedorov 	write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT);
1583202ce8bSAlexei Fedorov 	isb();
1593202ce8bSAlexei Fedorov 
1603202ce8bSAlexei Fedorov 	/*
1613202ce8bSAlexei Fedorov 	 * Enter standby state.
1623202ce8bSAlexei Fedorov 	 * dsb is good practice before using wfi to enter low power states.
1633fc4124cSDan Handley 	 */
1643fc4124cSDan Handley 	dsb();
1653fc4124cSDan Handley 	wfi();
1663202ce8bSAlexei Fedorov 
1673202ce8bSAlexei Fedorov 	/*
1683202ce8bSAlexei Fedorov 	 * Restore SCR_EL3 to the original value, synchronisation of SCR_EL3
1693202ce8bSAlexei Fedorov 	 * is done by eret in el3_exit() to save some execution cycles.
1703202ce8bSAlexei Fedorov 	 */
1713202ce8bSAlexei Fedorov 	write_scr_el3(scr);
1723fc4124cSDan Handley }
1733fc4124cSDan Handley 
1743fc4124cSDan Handley /*******************************************************************************
17538dce70fSSoby Mathew  * FVP handler called when a power domain is about to be turned on. The
17638dce70fSSoby Mathew  * mpidr determines the CPU to be turned on.
1773fc4124cSDan Handley  ******************************************************************************/
1781af540efSRoberto Vargas static int fvp_pwr_domain_on(u_register_t mpidr)
1793fc4124cSDan Handley {
1803fc4124cSDan Handley 	int rc = PSCI_E_SUCCESS;
1813fc4124cSDan Handley 	unsigned int psysr;
1823fc4124cSDan Handley 
1833fc4124cSDan Handley 	/*
1840f09c8f7SSandrine Bailleux 	 * Ensure that we do not cancel an inflight power off request for the
1850f09c8f7SSandrine Bailleux 	 * target cpu. That would leave it in a zombie wfi. Wait for it to power
1860f09c8f7SSandrine Bailleux 	 * off and then program the power controller to turn that CPU on.
1873fc4124cSDan Handley 	 */
1883fc4124cSDan Handley 	do {
1893fc4124cSDan Handley 		psysr = fvp_pwrc_read_psysr(mpidr);
190e02f469fSSathees Balya 	} while ((psysr & PSYSR_AFF_L0) != 0U);
1913fc4124cSDan Handley 
1923fc4124cSDan Handley 	fvp_pwrc_write_pponr(mpidr);
1933fc4124cSDan Handley 	return rc;
1943fc4124cSDan Handley }
1953fc4124cSDan Handley 
1963fc4124cSDan Handley /*******************************************************************************
19738dce70fSSoby Mathew  * FVP handler called when a power domain is about to be turned off. The
19838dce70fSSoby Mathew  * target_state encodes the power state that each level should transition to.
1993fc4124cSDan Handley  ******************************************************************************/
2001af540efSRoberto Vargas static void fvp_pwr_domain_off(const psci_power_state_t *target_state)
2013fc4124cSDan Handley {
20238dce70fSSoby Mathew 	assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
20338dce70fSSoby Mathew 					ARM_LOCAL_STATE_OFF);
2043fc4124cSDan Handley 
2053fc4124cSDan Handley 	/*
20638dce70fSSoby Mathew 	 * If execution reaches this stage then this power domain will be
20738dce70fSSoby Mathew 	 * suspended. Perform at least the cpu specific actions followed
20838dce70fSSoby Mathew 	 * by the cluster specific operations if applicable.
2093fc4124cSDan Handley 	 */
21074a9578cSJeenu Viswambharan 
21174a9578cSJeenu Viswambharan 	/* Prevent interrupts from spuriously waking up this cpu */
21274a9578cSJeenu Viswambharan 	plat_arm_gic_cpuif_disable();
21374a9578cSJeenu Viswambharan 
21474a9578cSJeenu Viswambharan 	/* Turn redistributor off */
21574a9578cSJeenu Viswambharan 	plat_arm_gic_redistif_off();
21674a9578cSJeenu Viswambharan 
21774a9578cSJeenu Viswambharan 	/* Program the power controller to power off this cpu. */
21874a9578cSJeenu Viswambharan 	fvp_pwrc_write_ppoffr(read_mpidr_el1());
2193fc4124cSDan Handley 
22038dce70fSSoby Mathew 	if (target_state->pwr_domain_state[ARM_PWR_LVL1] ==
22138dce70fSSoby Mathew 					ARM_LOCAL_STATE_OFF)
2223fc4124cSDan Handley 		fvp_cluster_pwrdwn_common();
2233fc4124cSDan Handley 
2243fc4124cSDan Handley }
2253fc4124cSDan Handley 
2263fc4124cSDan Handley /*******************************************************************************
22738dce70fSSoby Mathew  * FVP handler called when a power domain is about to be suspended. The
22838dce70fSSoby Mathew  * target_state encodes the power state that each level should transition to.
2293fc4124cSDan Handley  ******************************************************************************/
230*e75cc247SWing Li #if PSCI_OS_INIT_MODE
231*e75cc247SWing Li static int fvp_pwr_domain_suspend(const psci_power_state_t *target_state)
232*e75cc247SWing Li #else
2331af540efSRoberto Vargas static void fvp_pwr_domain_suspend(const psci_power_state_t *target_state)
234*e75cc247SWing Li #endif
2353fc4124cSDan Handley {
2363fc4124cSDan Handley 	unsigned long mpidr;
2373fc4124cSDan Handley 
23838dce70fSSoby Mathew 	/*
23938dce70fSSoby Mathew 	 * FVP has retention only at cpu level. Just return
24038dce70fSSoby Mathew 	 * as nothing is to be done for retention.
24138dce70fSSoby Mathew 	 */
24238dce70fSSoby Mathew 	if (target_state->pwr_domain_state[ARM_PWR_LVL0] ==
24338dce70fSSoby Mathew 					ARM_LOCAL_STATE_RET)
244*e75cc247SWing Li #if PSCI_OS_INIT_MODE
245*e75cc247SWing Li 		return PSCI_E_SUCCESS;
246*e75cc247SWing Li #else
2473fc4124cSDan Handley 		return;
248*e75cc247SWing Li #endif
2493fc4124cSDan Handley 
25038dce70fSSoby Mathew 	assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
25138dce70fSSoby Mathew 					ARM_LOCAL_STATE_OFF);
25238dce70fSSoby Mathew 
2533fc4124cSDan Handley 	/* Get the mpidr for this cpu */
2543fc4124cSDan Handley 	mpidr = read_mpidr_el1();
2553fc4124cSDan Handley 
2563fc4124cSDan Handley 	/* Program the power controller to enable wakeup interrupts. */
2573fc4124cSDan Handley 	fvp_pwrc_set_wen(mpidr);
2583fc4124cSDan Handley 
25974a9578cSJeenu Viswambharan 	/* Prevent interrupts from spuriously waking up this cpu */
26074a9578cSJeenu Viswambharan 	plat_arm_gic_cpuif_disable();
26174a9578cSJeenu Viswambharan 
26274a9578cSJeenu Viswambharan 	/*
26374a9578cSJeenu Viswambharan 	 * The Redistributor is not powered off as it can potentially prevent
26474a9578cSJeenu Viswambharan 	 * wake up events reaching the CPUIF and/or might lead to losing
26574a9578cSJeenu Viswambharan 	 * register context.
26674a9578cSJeenu Viswambharan 	 */
26774a9578cSJeenu Viswambharan 
2683fc4124cSDan Handley 	/* Perform the common cluster specific operations */
26938dce70fSSoby Mathew 	if (target_state->pwr_domain_state[ARM_PWR_LVL1] ==
27038dce70fSSoby Mathew 					ARM_LOCAL_STATE_OFF)
2713fc4124cSDan Handley 		fvp_cluster_pwrdwn_common();
272e35a3fb5SSoby Mathew 
273e35a3fb5SSoby Mathew 	/* Perform the common system specific operations */
274e35a3fb5SSoby Mathew 	if (target_state->pwr_domain_state[ARM_PWR_LVL2] ==
275e35a3fb5SSoby Mathew 						ARM_LOCAL_STATE_OFF)
276e35a3fb5SSoby Mathew 		arm_system_pwr_domain_save();
277e35a3fb5SSoby Mathew 
278e35a3fb5SSoby Mathew 	/* Program the power controller to power off this cpu. */
279e35a3fb5SSoby Mathew 	fvp_pwrc_write_ppoffr(read_mpidr_el1());
280*e75cc247SWing Li 
281*e75cc247SWing Li #if PSCI_OS_INIT_MODE
282*e75cc247SWing Li 	return PSCI_E_SUCCESS;
283*e75cc247SWing Li #else
284*e75cc247SWing Li 	return;
285*e75cc247SWing Li #endif
2863fc4124cSDan Handley }
2873fc4124cSDan Handley 
2883fc4124cSDan Handley /*******************************************************************************
28938dce70fSSoby Mathew  * FVP handler called when a power domain has just been powered on after
29038dce70fSSoby Mathew  * being turned off earlier. The target_state encodes the low power state that
29138dce70fSSoby Mathew  * each level has woken up from.
2923fc4124cSDan Handley  ******************************************************************************/
2931af540efSRoberto Vargas static void fvp_pwr_domain_on_finish(const psci_power_state_t *target_state)
2943fc4124cSDan Handley {
295f14d1886SSoby Mathew 	fvp_power_domain_on_finish_common(target_state);
2963fc4124cSDan Handley 
2976806cd23SMadhukar Pappireddy }
2986806cd23SMadhukar Pappireddy 
2996806cd23SMadhukar Pappireddy /*******************************************************************************
3006806cd23SMadhukar Pappireddy  * FVP handler called when a power domain has just been powered on and the cpu
3016806cd23SMadhukar Pappireddy  * and its cluster are fully participating in coherent transaction on the
3026806cd23SMadhukar Pappireddy  * interconnect. Data cache must be enabled for CPU at this point.
3036806cd23SMadhukar Pappireddy  ******************************************************************************/
3046806cd23SMadhukar Pappireddy static void fvp_pwr_domain_on_finish_late(const psci_power_state_t *target_state)
3056806cd23SMadhukar Pappireddy {
3066806cd23SMadhukar Pappireddy 	/* Program GIC per-cpu distributor or re-distributor interface */
30727573c59SAchin Gupta 	plat_arm_gic_pcpu_init();
30827573c59SAchin Gupta 
3096806cd23SMadhukar Pappireddy 	/* Enable GIC CPU interface */
31027573c59SAchin Gupta 	plat_arm_gic_cpuif_enable();
3113fc4124cSDan Handley }
3123fc4124cSDan Handley 
3133fc4124cSDan Handley /*******************************************************************************
31438dce70fSSoby Mathew  * FVP handler called when a power domain has just been powered on after
31538dce70fSSoby Mathew  * having been suspended earlier. The target_state encodes the low power state
31638dce70fSSoby Mathew  * that each level has woken up from.
3173fc4124cSDan Handley  * TODO: At the moment we reuse the on finisher and reinitialize the secure
3183fc4124cSDan Handley  * context. Need to implement a separate suspend finisher.
3193fc4124cSDan Handley  ******************************************************************************/
3201af540efSRoberto Vargas static void fvp_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
3213fc4124cSDan Handley {
32238dce70fSSoby Mathew 	/*
32338dce70fSSoby Mathew 	 * Nothing to be done on waking up from retention from CPU level.
32438dce70fSSoby Mathew 	 */
32538dce70fSSoby Mathew 	if (target_state->pwr_domain_state[ARM_PWR_LVL0] ==
32638dce70fSSoby Mathew 					ARM_LOCAL_STATE_RET)
32738dce70fSSoby Mathew 		return;
32838dce70fSSoby Mathew 
329f14d1886SSoby Mathew 	fvp_power_domain_on_finish_common(target_state);
330f14d1886SSoby Mathew 
3316806cd23SMadhukar Pappireddy 	/* Enable GIC CPU interface */
33227573c59SAchin Gupta 	plat_arm_gic_cpuif_enable();
3333fc4124cSDan Handley }
3343fc4124cSDan Handley 
3353fc4124cSDan Handley /*******************************************************************************
3363fc4124cSDan Handley  * FVP handlers to shutdown/reboot the system
3373fc4124cSDan Handley  ******************************************************************************/
3383fc4124cSDan Handley static void __dead2 fvp_system_off(void)
3393fc4124cSDan Handley {
3403fc4124cSDan Handley 	/* Write the System Configuration Control Register */
3413fc4124cSDan Handley 	mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL,
3423fc4124cSDan Handley 		V2M_CFGCTRL_START |
3433fc4124cSDan Handley 		V2M_CFGCTRL_RW |
3443fc4124cSDan Handley 		V2M_CFGCTRL_FUNC(V2M_FUNC_SHUTDOWN));
3453fc4124cSDan Handley 	wfi();
3463fc4124cSDan Handley 	ERROR("FVP System Off: operation not handled.\n");
3473fc4124cSDan Handley 	panic();
3483fc4124cSDan Handley }
3493fc4124cSDan Handley 
3503fc4124cSDan Handley static void __dead2 fvp_system_reset(void)
3513fc4124cSDan Handley {
3523fc4124cSDan Handley 	/* Write the System Configuration Control Register */
3533fc4124cSDan Handley 	mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL,
3543fc4124cSDan Handley 		V2M_CFGCTRL_START |
3553fc4124cSDan Handley 		V2M_CFGCTRL_RW |
3563fc4124cSDan Handley 		V2M_CFGCTRL_FUNC(V2M_FUNC_REBOOT));
3573fc4124cSDan Handley 	wfi();
3583fc4124cSDan Handley 	ERROR("FVP System Reset: operation not handled.\n");
3593fc4124cSDan Handley 	panic();
3603fc4124cSDan Handley }
3613fc4124cSDan Handley 
3621298ae02SJeenu Viswambharan static int fvp_node_hw_state(u_register_t target_cpu,
3631298ae02SJeenu Viswambharan 			     unsigned int power_level)
3641298ae02SJeenu Viswambharan {
3651298ae02SJeenu Viswambharan 	unsigned int psysr;
3661298ae02SJeenu Viswambharan 	int ret;
3671298ae02SJeenu Viswambharan 
3681298ae02SJeenu Viswambharan 	/*
3691298ae02SJeenu Viswambharan 	 * The format of 'power_level' is implementation-defined, but 0 must
3701298ae02SJeenu Viswambharan 	 * mean a CPU. We also allow 1 to denote the cluster
3711298ae02SJeenu Viswambharan 	 */
372e02f469fSSathees Balya 	if ((power_level != ARM_PWR_LVL0) && (power_level != ARM_PWR_LVL1))
3731298ae02SJeenu Viswambharan 		return PSCI_E_INVALID_PARAMS;
3741298ae02SJeenu Viswambharan 
3751298ae02SJeenu Viswambharan 	/*
3761298ae02SJeenu Viswambharan 	 * Read the status of the given MPDIR from FVP power controller. The
3771298ae02SJeenu Viswambharan 	 * power controller only gives us on/off status, so map that to expected
3781298ae02SJeenu Viswambharan 	 * return values of the PSCI call
3791298ae02SJeenu Viswambharan 	 */
3801298ae02SJeenu Viswambharan 	psysr = fvp_pwrc_read_psysr(target_cpu);
3811298ae02SJeenu Viswambharan 	if (psysr == PSYSR_INVALID)
3821298ae02SJeenu Viswambharan 		return PSCI_E_INVALID_PARAMS;
3831298ae02SJeenu Viswambharan 
384649c48f5SJonathan Wright 	if (power_level == ARM_PWR_LVL0) {
385e02f469fSSathees Balya 		ret = ((psysr & PSYSR_AFF_L0) != 0U) ? HW_ON : HW_OFF;
386649c48f5SJonathan Wright 	} else {
387649c48f5SJonathan Wright 		/* power_level == ARM_PWR_LVL1 */
388e02f469fSSathees Balya 		ret = ((psysr & PSYSR_AFF_L1) != 0U) ? HW_ON : HW_OFF;
3891298ae02SJeenu Viswambharan 	}
3901298ae02SJeenu Viswambharan 
3911298ae02SJeenu Viswambharan 	return ret;
3921298ae02SJeenu Viswambharan }
3931298ae02SJeenu Viswambharan 
394e35a3fb5SSoby Mathew /*
395e35a3fb5SSoby Mathew  * The FVP doesn't truly support power management at SYSTEM power domain. The
396e35a3fb5SSoby Mathew  * SYSTEM_SUSPEND will be down-graded to the cluster level within the platform
397e35a3fb5SSoby Mathew  * layer. The `fake` SYSTEM_SUSPEND allows us to validate some of the driver
398e35a3fb5SSoby Mathew  * save and restore sequences on FVP.
399e35a3fb5SSoby Mathew  */
4001af540efSRoberto Vargas #if !ARM_BL31_IN_DRAM
4011af540efSRoberto Vargas static void fvp_get_sys_suspend_power_state(psci_power_state_t *req_state)
402e35a3fb5SSoby Mathew {
403e35a3fb5SSoby Mathew 	unsigned int i;
404e35a3fb5SSoby Mathew 
405e35a3fb5SSoby Mathew 	for (i = ARM_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++)
406e35a3fb5SSoby Mathew 		req_state->pwr_domain_state[i] = ARM_LOCAL_STATE_OFF;
407e35a3fb5SSoby Mathew }
4081af540efSRoberto Vargas #endif
409e35a3fb5SSoby Mathew 
410e35a3fb5SSoby Mathew /*******************************************************************************
411e35a3fb5SSoby Mathew  * Handler to filter PSCI requests.
412e35a3fb5SSoby Mathew  ******************************************************************************/
413e35a3fb5SSoby Mathew /*
414e35a3fb5SSoby Mathew  * The system power domain suspend is only supported only via
415e35a3fb5SSoby Mathew  * PSCI SYSTEM_SUSPEND API. PSCI CPU_SUSPEND request to system power domain
416e35a3fb5SSoby Mathew  * will be downgraded to the lower level.
417e35a3fb5SSoby Mathew  */
418e35a3fb5SSoby Mathew static int fvp_validate_power_state(unsigned int power_state,
419e35a3fb5SSoby Mathew 			    psci_power_state_t *req_state)
420e35a3fb5SSoby Mathew {
421e35a3fb5SSoby Mathew 	int rc;
422e35a3fb5SSoby Mathew 	rc = arm_validate_power_state(power_state, req_state);
423e35a3fb5SSoby Mathew 
424e35a3fb5SSoby Mathew 	/*
425e35a3fb5SSoby Mathew 	 * Ensure that the system power domain level is never suspended
426e35a3fb5SSoby Mathew 	 * via PSCI CPU SUSPEND API. Currently system suspend is only
427e35a3fb5SSoby Mathew 	 * supported via PSCI SYSTEM SUSPEND API.
428e35a3fb5SSoby Mathew 	 */
429e35a3fb5SSoby Mathew 	req_state->pwr_domain_state[ARM_PWR_LVL2] = ARM_LOCAL_STATE_RUN;
430e35a3fb5SSoby Mathew 	return rc;
431e35a3fb5SSoby Mathew }
432e35a3fb5SSoby Mathew 
433e35a3fb5SSoby Mathew /*
434e35a3fb5SSoby Mathew  * Custom `translate_power_state_by_mpidr` handler for FVP. Unlike in the
435e35a3fb5SSoby Mathew  * `fvp_validate_power_state`, we do not downgrade the system power
436e35a3fb5SSoby Mathew  * domain level request in `power_state` as it will be used to query the
437e35a3fb5SSoby Mathew  * PSCI_STAT_COUNT/RESIDENCY at the system power domain level.
438e35a3fb5SSoby Mathew  */
439e35a3fb5SSoby Mathew static int fvp_translate_power_state_by_mpidr(u_register_t mpidr,
440e35a3fb5SSoby Mathew 		unsigned int power_state,
441e35a3fb5SSoby Mathew 		psci_power_state_t *output_state)
442e35a3fb5SSoby Mathew {
443e35a3fb5SSoby Mathew 	return arm_validate_power_state(power_state, output_state);
444e35a3fb5SSoby Mathew }
445e35a3fb5SSoby Mathew 
4463fc4124cSDan Handley /*******************************************************************************
447785fb92bSSoby Mathew  * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard
448785fb92bSSoby Mathew  * platform layer will take care of registering the handlers with PSCI.
4493fc4124cSDan Handley  ******************************************************************************/
4505486a965SSoby Mathew plat_psci_ops_t plat_arm_psci_pm_ops = {
45138dce70fSSoby Mathew 	.cpu_standby = fvp_cpu_standby,
45238dce70fSSoby Mathew 	.pwr_domain_on = fvp_pwr_domain_on,
45338dce70fSSoby Mathew 	.pwr_domain_off = fvp_pwr_domain_off,
45438dce70fSSoby Mathew 	.pwr_domain_suspend = fvp_pwr_domain_suspend,
45538dce70fSSoby Mathew 	.pwr_domain_on_finish = fvp_pwr_domain_on_finish,
4566806cd23SMadhukar Pappireddy 	.pwr_domain_on_finish_late = fvp_pwr_domain_on_finish_late,
45738dce70fSSoby Mathew 	.pwr_domain_suspend_finish = fvp_pwr_domain_suspend_finish,
4583fc4124cSDan Handley 	.system_off = fvp_system_off,
4593fc4124cSDan Handley 	.system_reset = fvp_system_reset,
460e35a3fb5SSoby Mathew 	.validate_power_state = fvp_validate_power_state,
46171e7a4e5SJeenu Viswambharan 	.validate_ns_entrypoint = arm_validate_psci_entrypoint,
462e35a3fb5SSoby Mathew 	.translate_power_state_by_mpidr = fvp_translate_power_state_by_mpidr,
463f145403cSRoberto Vargas 	.get_node_hw_state = fvp_node_hw_state,
4647d44ac1eSAntonio Nino Diaz #if !ARM_BL31_IN_DRAM
4657d44ac1eSAntonio Nino Diaz 	/*
4667d44ac1eSAntonio Nino Diaz 	 * The TrustZone Controller is set up during the warmboot sequence after
4677d44ac1eSAntonio Nino Diaz 	 * resuming the CPU from a SYSTEM_SUSPEND. If BL31 is located in SRAM
4687d44ac1eSAntonio Nino Diaz 	 * this is  not a problem but, if it is in TZC-secured DRAM, it tries to
4697d44ac1eSAntonio Nino Diaz 	 * reconfigure the same memory it is running on, causing an exception.
4707d44ac1eSAntonio Nino Diaz 	 */
471e35a3fb5SSoby Mathew 	.get_sys_suspend_power_state = fvp_get_sys_suspend_power_state,
4727d44ac1eSAntonio Nino Diaz #endif
473f145403cSRoberto Vargas 	.mem_protect_chk	= arm_psci_mem_protect_chk,
474f145403cSRoberto Vargas 	.read_mem_protect	= arm_psci_read_mem_protect,
475f145403cSRoberto Vargas 	.write_mem_protect	= arm_nor_psci_write_mem_protect,
4763fc4124cSDan Handley };
47789f2e589SChandni Cherukuri 
47889f2e589SChandni Cherukuri const plat_psci_ops_t *plat_arm_psci_override_pm_ops(plat_psci_ops_t *ops)
47989f2e589SChandni Cherukuri {
48089f2e589SChandni Cherukuri 	return ops;
48189f2e589SChandni Cherukuri }
482