13fc4124cSDan Handley /*
2cb331826SBoyan Karatotev * Copyright (c) 2013-2025, 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/mmio.h>
1409d40e0eSAntonio Nino Diaz #include <lib/psci/psci.h>
15bd9344f6SAntonio Nino Diaz #include <plat/arm/common/arm_config.h>
16bd9344f6SAntonio Nino Diaz #include <plat/arm/common/plat_arm.h>
17234bc7f8SAntonio Nino Diaz #include <platform_def.h>
1809d40e0eSAntonio Nino Diaz
193fc4124cSDan Handley #include "fvp_private.h"
20609e053cSAmbroise Vincent #include "../drivers/arm/gic/v3/gicv3_private.h"
213fc4124cSDan Handley
223fc4124cSDan Handley
232204afdeSSoby Mathew #if ARM_RECOM_STATE_ID_ENC
242204afdeSSoby Mathew /*
252204afdeSSoby Mathew * The table storing the valid idle power states. Ensure that the
262204afdeSSoby Mathew * array entries are populated in ascending order of state-id to
272204afdeSSoby Mathew * enable us to use binary search during power state validation.
282204afdeSSoby Mathew * The table must be terminated by a NULL entry.
292204afdeSSoby Mathew */
302204afdeSSoby Mathew const unsigned int arm_pm_idle_states[] = {
312204afdeSSoby Mathew /* State-id - 0x01 */
322204afdeSSoby Mathew arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_RET,
332204afdeSSoby Mathew ARM_PWR_LVL0, PSTATE_TYPE_STANDBY),
342204afdeSSoby Mathew /* State-id - 0x02 */
352204afdeSSoby Mathew arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_OFF,
362204afdeSSoby Mathew ARM_PWR_LVL0, PSTATE_TYPE_POWERDOWN),
372204afdeSSoby Mathew /* State-id - 0x22 */
382204afdeSSoby Mathew arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_OFF, ARM_LOCAL_STATE_OFF,
392204afdeSSoby Mathew ARM_PWR_LVL1, PSTATE_TYPE_POWERDOWN),
40e35a3fb5SSoby Mathew /* State-id - 0x222 */
41e35a3fb5SSoby Mathew arm_make_pwrstate_lvl2(ARM_LOCAL_STATE_OFF, ARM_LOCAL_STATE_OFF,
42e35a3fb5SSoby Mathew ARM_LOCAL_STATE_OFF, ARM_PWR_LVL2, PSTATE_TYPE_POWERDOWN),
432204afdeSSoby Mathew 0,
442204afdeSSoby Mathew };
452204afdeSSoby Mathew #endif
462204afdeSSoby Mathew
473fc4124cSDan Handley /*******************************************************************************
483fc4124cSDan Handley * Function which implements the common FVP specific operations to power down a
493fc4124cSDan Handley * cluster in response to a CPU_OFF or CPU_SUSPEND request.
503fc4124cSDan Handley ******************************************************************************/
fvp_cluster_pwrdwn_common(void)513fc4124cSDan Handley static void fvp_cluster_pwrdwn_common(void)
523fc4124cSDan Handley {
533fc4124cSDan Handley uint64_t mpidr = read_mpidr_el1();
543fc4124cSDan Handley
553fc4124cSDan Handley /* Disable coherency if this cluster is to be turned off */
566355f234SVikram Kanigiri fvp_interconnect_disable();
573fc4124cSDan Handley
583fc4124cSDan Handley /* Program the power controller to turn the cluster off */
593fc4124cSDan Handley fvp_pwrc_write_pcoffr(mpidr);
603fc4124cSDan Handley }
613fc4124cSDan Handley
62e35a3fb5SSoby Mathew /*
63e35a3fb5SSoby Mathew * Empty implementation of these hooks avoid setting the GICR_WAKER.Sleep bit
64e35a3fb5SSoby Mathew * on ARM GICv3 implementations on FVP. This is required, because FVP does not
65e35a3fb5SSoby Mathew * support SYSTEM_SUSPEND and it is `faked` in firmware. Hence, for wake up
66e35a3fb5SSoby Mathew * from `fake` system suspend the GIC must not be powered off.
67e35a3fb5SSoby Mathew */
arm_gicv3_distif_pre_save(unsigned int rdist_proc_num)68dc6aad2eSRoberto Vargas void arm_gicv3_distif_pre_save(unsigned int rdist_proc_num)
69e35a3fb5SSoby Mathew {}
70e35a3fb5SSoby Mathew
arm_gicv3_distif_post_restore(unsigned int rdist_proc_num)71dc6aad2eSRoberto Vargas void arm_gicv3_distif_post_restore(unsigned int rdist_proc_num)
72e35a3fb5SSoby Mathew {}
73e35a3fb5SSoby Mathew
fvp_power_domain_on_finish_common(const psci_power_state_t * target_state)74f14d1886SSoby Mathew static void fvp_power_domain_on_finish_common(const psci_power_state_t *target_state)
75f14d1886SSoby Mathew {
76f14d1886SSoby Mathew unsigned long mpidr;
77f14d1886SSoby Mathew
78f14d1886SSoby Mathew assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
79f14d1886SSoby Mathew ARM_LOCAL_STATE_OFF);
80f14d1886SSoby Mathew
81f14d1886SSoby Mathew /* Get the mpidr for this cpu */
82f14d1886SSoby Mathew mpidr = read_mpidr_el1();
83f14d1886SSoby Mathew
84f14d1886SSoby Mathew /* Perform the common cluster specific operations */
85f14d1886SSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL1] ==
86f14d1886SSoby Mathew ARM_LOCAL_STATE_OFF) {
87f14d1886SSoby Mathew /*
88f14d1886SSoby Mathew * This CPU might have woken up whilst the cluster was
89f14d1886SSoby Mathew * attempting to power down. In this case the FVP power
90f14d1886SSoby Mathew * controller will have a pending cluster power off request
91f14d1886SSoby Mathew * which needs to be cleared by writing to the PPONR register.
92f14d1886SSoby Mathew * This prevents the power controller from interpreting a
93f14d1886SSoby Mathew * subsequent entry of this cpu into a simple wfi as a power
94f14d1886SSoby Mathew * down request.
95f14d1886SSoby Mathew */
96f14d1886SSoby Mathew fvp_pwrc_write_pponr(mpidr);
97f14d1886SSoby Mathew
98f14d1886SSoby Mathew /* Enable coherency if this cluster was off */
996355f234SVikram Kanigiri fvp_interconnect_enable();
100f14d1886SSoby Mathew }
101e35a3fb5SSoby Mathew /* Perform the common system specific operations */
102e35a3fb5SSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL2] ==
103e35a3fb5SSoby Mathew ARM_LOCAL_STATE_OFF)
104e35a3fb5SSoby Mathew arm_system_pwr_domain_resume();
105f14d1886SSoby Mathew
106f14d1886SSoby Mathew /*
107f14d1886SSoby Mathew * Clear PWKUPR.WEN bit to ensure interrupts do not interfere
108f14d1886SSoby Mathew * with a cpu power down unless the bit is set again
109f14d1886SSoby Mathew */
110f14d1886SSoby Mathew fvp_pwrc_clr_wen(mpidr);
111f14d1886SSoby Mathew }
112f14d1886SSoby Mathew
1133fc4124cSDan Handley /*******************************************************************************
11438dce70fSSoby Mathew * FVP handler called when a CPU is about to enter standby.
1153fc4124cSDan Handley ******************************************************************************/
fvp_cpu_standby(plat_local_state_t cpu_state)1161af540efSRoberto Vargas static void fvp_cpu_standby(plat_local_state_t cpu_state)
1173fc4124cSDan Handley {
1183202ce8bSAlexei Fedorov u_register_t scr = read_scr_el3();
11938dce70fSSoby Mathew
12038dce70fSSoby Mathew assert(cpu_state == ARM_LOCAL_STATE_RET);
12138dce70fSSoby Mathew
1223fc4124cSDan Handley /*
1233202ce8bSAlexei Fedorov * Enable the Non-secure interrupt to wake the CPU.
1243202ce8bSAlexei Fedorov * In GICv3 affinity routing mode, the Non-secure Group 1 interrupts
1253202ce8bSAlexei Fedorov * use Physical FIQ at EL3 whereas in GICv2, Physical IRQ is used.
1263202ce8bSAlexei Fedorov * Enabling both the bits works for both GICv2 mode and GICv3 affinity
1273202ce8bSAlexei Fedorov * routing mode.
1283202ce8bSAlexei Fedorov */
1293202ce8bSAlexei Fedorov write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT);
1303202ce8bSAlexei Fedorov isb();
1313202ce8bSAlexei Fedorov
1323202ce8bSAlexei Fedorov /*
1333202ce8bSAlexei Fedorov * Enter standby state.
1343202ce8bSAlexei Fedorov * dsb is good practice before using wfi to enter low power states.
1353fc4124cSDan Handley */
1363fc4124cSDan Handley dsb();
1373fc4124cSDan Handley wfi();
1383202ce8bSAlexei Fedorov
1393202ce8bSAlexei Fedorov /*
1403202ce8bSAlexei Fedorov * Restore SCR_EL3 to the original value, synchronisation of SCR_EL3
1413202ce8bSAlexei Fedorov * is done by eret in el3_exit() to save some execution cycles.
1423202ce8bSAlexei Fedorov */
1433202ce8bSAlexei Fedorov write_scr_el3(scr);
1443fc4124cSDan Handley }
1453fc4124cSDan Handley
1463fc4124cSDan Handley /*******************************************************************************
14738dce70fSSoby Mathew * FVP handler called when a power domain is about to be turned on. The
14838dce70fSSoby Mathew * mpidr determines the CPU to be turned on.
1493fc4124cSDan Handley ******************************************************************************/
fvp_pwr_domain_on(u_register_t mpidr)1501af540efSRoberto Vargas static int fvp_pwr_domain_on(u_register_t mpidr)
1513fc4124cSDan Handley {
1523fc4124cSDan Handley int rc = PSCI_E_SUCCESS;
1533fc4124cSDan Handley unsigned int psysr;
1543fc4124cSDan Handley
1553fc4124cSDan Handley /*
1560f09c8f7SSandrine Bailleux * Ensure that we do not cancel an inflight power off request for the
1570f09c8f7SSandrine Bailleux * target cpu. That would leave it in a zombie wfi. Wait for it to power
1580f09c8f7SSandrine Bailleux * off and then program the power controller to turn that CPU on.
1593fc4124cSDan Handley */
1603fc4124cSDan Handley do {
1613fc4124cSDan Handley psysr = fvp_pwrc_read_psysr(mpidr);
162e02f469fSSathees Balya } while ((psysr & PSYSR_AFF_L0) != 0U);
1633fc4124cSDan Handley
1643fc4124cSDan Handley fvp_pwrc_write_pponr(mpidr);
1653fc4124cSDan Handley return rc;
1663fc4124cSDan Handley }
1673fc4124cSDan Handley
1683fc4124cSDan Handley /*******************************************************************************
16938dce70fSSoby Mathew * FVP handler called when a power domain is about to be turned off. The
17038dce70fSSoby Mathew * target_state encodes the power state that each level should transition to.
1713fc4124cSDan Handley ******************************************************************************/
fvp_pwr_domain_off(const psci_power_state_t * target_state)1721af540efSRoberto Vargas static void fvp_pwr_domain_off(const psci_power_state_t *target_state)
1733fc4124cSDan Handley {
17438dce70fSSoby Mathew assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
17538dce70fSSoby Mathew ARM_LOCAL_STATE_OFF);
1763fc4124cSDan Handley
1773fc4124cSDan Handley /*
17838dce70fSSoby Mathew * If execution reaches this stage then this power domain will be
17938dce70fSSoby Mathew * suspended. Perform at least the cpu specific actions followed
18038dce70fSSoby Mathew * by the cluster specific operations if applicable.
1813fc4124cSDan Handley */
18274a9578cSJeenu Viswambharan
18374a9578cSJeenu Viswambharan /* Program the power controller to power off this cpu. */
18474a9578cSJeenu Viswambharan fvp_pwrc_write_ppoffr(read_mpidr_el1());
1853fc4124cSDan Handley
18638dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL1] ==
18738dce70fSSoby Mathew ARM_LOCAL_STATE_OFF)
1883fc4124cSDan Handley fvp_cluster_pwrdwn_common();
1893fc4124cSDan Handley
1903fc4124cSDan Handley }
1913fc4124cSDan Handley
1923fc4124cSDan Handley /*******************************************************************************
19338dce70fSSoby Mathew * FVP handler called when a power domain is about to be suspended. The
19438dce70fSSoby Mathew * target_state encodes the power state that each level should transition to.
1953fc4124cSDan Handley ******************************************************************************/
fvp_pwr_domain_suspend(const psci_power_state_t * target_state)1961af540efSRoberto Vargas static void fvp_pwr_domain_suspend(const psci_power_state_t *target_state)
1973fc4124cSDan Handley {
1983fc4124cSDan Handley unsigned long mpidr;
1993fc4124cSDan Handley
20038dce70fSSoby Mathew /*
20138dce70fSSoby Mathew * FVP has retention only at cpu level. Just return
20238dce70fSSoby Mathew * as nothing is to be done for retention.
20338dce70fSSoby Mathew */
20438dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL0] ==
20538dce70fSSoby Mathew ARM_LOCAL_STATE_RET)
2063fc4124cSDan Handley return;
2073fc4124cSDan Handley
20838dce70fSSoby Mathew assert(target_state->pwr_domain_state[ARM_PWR_LVL0] ==
20938dce70fSSoby Mathew ARM_LOCAL_STATE_OFF);
21038dce70fSSoby Mathew
2113fc4124cSDan Handley /* Get the mpidr for this cpu */
2123fc4124cSDan Handley mpidr = read_mpidr_el1();
2133fc4124cSDan Handley
2143fc4124cSDan Handley /* Program the power controller to enable wakeup interrupts. */
2153fc4124cSDan Handley fvp_pwrc_set_wen(mpidr);
2163fc4124cSDan Handley
21774a9578cSJeenu Viswambharan /*
21874a9578cSJeenu Viswambharan * The Redistributor is not powered off as it can potentially prevent
21974a9578cSJeenu Viswambharan * wake up events reaching the CPUIF and/or might lead to losing
22074a9578cSJeenu Viswambharan * register context.
22174a9578cSJeenu Viswambharan */
22274a9578cSJeenu Viswambharan
2233fc4124cSDan Handley /* Perform the common cluster specific operations */
22438dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL1] ==
22538dce70fSSoby Mathew ARM_LOCAL_STATE_OFF)
2263fc4124cSDan Handley fvp_cluster_pwrdwn_common();
227e35a3fb5SSoby Mathew
228e35a3fb5SSoby Mathew /* Perform the common system specific operations */
229e35a3fb5SSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL2] ==
230e35a3fb5SSoby Mathew ARM_LOCAL_STATE_OFF)
231e35a3fb5SSoby Mathew arm_system_pwr_domain_save();
232e35a3fb5SSoby Mathew
233e35a3fb5SSoby Mathew /* Program the power controller to power off this cpu. */
234e35a3fb5SSoby Mathew fvp_pwrc_write_ppoffr(read_mpidr_el1());
235e75cc247SWing Li
236e75cc247SWing Li return;
2373fc4124cSDan Handley }
2383fc4124cSDan Handley
2393fc4124cSDan Handley /*******************************************************************************
24038dce70fSSoby Mathew * FVP handler called when a power domain has just been powered on after
24138dce70fSSoby Mathew * being turned off earlier. The target_state encodes the low power state that
24238dce70fSSoby Mathew * each level has woken up from.
2433fc4124cSDan Handley ******************************************************************************/
fvp_pwr_domain_on_finish(const psci_power_state_t * target_state)2441af540efSRoberto Vargas static void fvp_pwr_domain_on_finish(const psci_power_state_t *target_state)
2453fc4124cSDan Handley {
246f14d1886SSoby Mathew fvp_power_domain_on_finish_common(target_state);
2476806cd23SMadhukar Pappireddy }
2486806cd23SMadhukar Pappireddy
2496806cd23SMadhukar Pappireddy /*******************************************************************************
2506806cd23SMadhukar Pappireddy * FVP handler called when a power domain has just been powered on and the cpu
2516806cd23SMadhukar Pappireddy * and its cluster are fully participating in coherent transaction on the
2526806cd23SMadhukar Pappireddy * interconnect. Data cache must be enabled for CPU at this point.
2536806cd23SMadhukar Pappireddy ******************************************************************************/
fvp_pwr_domain_on_finish_late(const psci_power_state_t * target_state)2546806cd23SMadhukar Pappireddy static void fvp_pwr_domain_on_finish_late(const psci_power_state_t *target_state)
2556806cd23SMadhukar Pappireddy {
256c5c54e20SBoyan Karatotev #if USE_GIC_DRIVER == 3
257cb331826SBoyan Karatotev fvp_pcpu_init();
258c5c54e20SBoyan Karatotev #endif
2593fc4124cSDan Handley }
2603fc4124cSDan Handley
2613fc4124cSDan Handley /*******************************************************************************
26238dce70fSSoby Mathew * FVP handler called when a power domain has just been powered on after
26338dce70fSSoby Mathew * having been suspended earlier. The target_state encodes the low power state
26438dce70fSSoby Mathew * that each level has woken up from.
2653fc4124cSDan Handley * TODO: At the moment we reuse the on finisher and reinitialize the secure
2663fc4124cSDan Handley * context. Need to implement a separate suspend finisher.
2673fc4124cSDan Handley ******************************************************************************/
fvp_pwr_domain_suspend_finish(const psci_power_state_t * target_state)2681af540efSRoberto Vargas static void fvp_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
2693fc4124cSDan Handley {
27038dce70fSSoby Mathew /*
27138dce70fSSoby Mathew * Nothing to be done on waking up from retention from CPU level.
27238dce70fSSoby Mathew */
27338dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL0] ==
27438dce70fSSoby Mathew ARM_LOCAL_STATE_RET)
27538dce70fSSoby Mathew return;
27638dce70fSSoby Mathew
277f14d1886SSoby Mathew fvp_power_domain_on_finish_common(target_state);
2783fc4124cSDan Handley }
2793fc4124cSDan Handley
2803fc4124cSDan Handley /*******************************************************************************
2813fc4124cSDan Handley * FVP handlers to shutdown/reboot the system
2823fc4124cSDan Handley ******************************************************************************/
fvp_system_off(void)283da305ec7SBoyan Karatotev static void fvp_system_off(void)
2843fc4124cSDan Handley {
2853fc4124cSDan Handley /* Write the System Configuration Control Register */
2863fc4124cSDan Handley mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL,
2873fc4124cSDan Handley V2M_CFGCTRL_START |
2883fc4124cSDan Handley V2M_CFGCTRL_RW |
2893fc4124cSDan Handley V2M_CFGCTRL_FUNC(V2M_FUNC_SHUTDOWN));
2903fc4124cSDan Handley }
2913fc4124cSDan Handley
fvp_system_reset(void)292da305ec7SBoyan Karatotev static void fvp_system_reset(void)
2933fc4124cSDan Handley {
2943fc4124cSDan Handley /* Write the System Configuration Control Register */
2953fc4124cSDan Handley mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL,
2963fc4124cSDan Handley V2M_CFGCTRL_START |
2973fc4124cSDan Handley V2M_CFGCTRL_RW |
2983fc4124cSDan Handley V2M_CFGCTRL_FUNC(V2M_FUNC_REBOOT));
2993fc4124cSDan Handley }
3003fc4124cSDan Handley
fvp_node_hw_state(u_register_t target_cpu,unsigned int power_level)3011298ae02SJeenu Viswambharan static int fvp_node_hw_state(u_register_t target_cpu,
3021298ae02SJeenu Viswambharan unsigned int power_level)
3031298ae02SJeenu Viswambharan {
3041298ae02SJeenu Viswambharan unsigned int psysr;
305b9c3a8c0SGovindraj Raja int ret = 0;
3061298ae02SJeenu Viswambharan
3071298ae02SJeenu Viswambharan /*
3081298ae02SJeenu Viswambharan * The format of 'power_level' is implementation-defined, but 0 must
3091298ae02SJeenu Viswambharan * mean a CPU. We also allow 1 to denote the cluster
3101298ae02SJeenu Viswambharan */
311*4824e250SGovindraj Raja if (power_level > ARM_PWR_LVL1) {
3121298ae02SJeenu Viswambharan return PSCI_E_INVALID_PARAMS;
313*4824e250SGovindraj Raja }
3141298ae02SJeenu Viswambharan
3151298ae02SJeenu Viswambharan /*
3161298ae02SJeenu Viswambharan * Read the status of the given MPDIR from FVP power controller. The
3171298ae02SJeenu Viswambharan * power controller only gives us on/off status, so map that to expected
3181298ae02SJeenu Viswambharan * return values of the PSCI call
3191298ae02SJeenu Viswambharan */
3201298ae02SJeenu Viswambharan psysr = fvp_pwrc_read_psysr(target_cpu);
3211298ae02SJeenu Viswambharan if (psysr == PSYSR_INVALID)
3221298ae02SJeenu Viswambharan return PSCI_E_INVALID_PARAMS;
3231298ae02SJeenu Viswambharan
324649c48f5SJonathan Wright if (power_level == ARM_PWR_LVL0) {
325e02f469fSSathees Balya ret = ((psysr & PSYSR_AFF_L0) != 0U) ? HW_ON : HW_OFF;
326b9c3a8c0SGovindraj Raja } else if (power_level == ARM_PWR_LVL1) {
327b9c3a8c0SGovindraj Raja /*
328b9c3a8c0SGovindraj Raja * Use L1 affinity if MPIDR_EL1.MT bit is not set else use L2 affinity.
329b9c3a8c0SGovindraj Raja */
330b9c3a8c0SGovindraj Raja if ((read_mpidr_el1() & MPIDR_MT_MASK) == 0U)
331e02f469fSSathees Balya ret = ((psysr & PSYSR_AFF_L1) != 0U) ? HW_ON : HW_OFF;
332b9c3a8c0SGovindraj Raja else
333b9c3a8c0SGovindraj Raja ret = ((psysr & PSYSR_AFF_L2) != 0U) ? HW_ON : HW_OFF;
3341298ae02SJeenu Viswambharan }
3351298ae02SJeenu Viswambharan
3361298ae02SJeenu Viswambharan return ret;
3371298ae02SJeenu Viswambharan }
3381298ae02SJeenu Viswambharan
339e35a3fb5SSoby Mathew /*
340e35a3fb5SSoby Mathew * The FVP doesn't truly support power management at SYSTEM power domain. The
341e35a3fb5SSoby Mathew * SYSTEM_SUSPEND will be down-graded to the cluster level within the platform
342e35a3fb5SSoby Mathew * layer. The `fake` SYSTEM_SUSPEND allows us to validate some of the driver
343e35a3fb5SSoby Mathew * save and restore sequences on FVP.
344e35a3fb5SSoby Mathew */
3451af540efSRoberto Vargas #if !ARM_BL31_IN_DRAM
fvp_get_sys_suspend_power_state(psci_power_state_t * req_state)3461af540efSRoberto Vargas static void fvp_get_sys_suspend_power_state(psci_power_state_t *req_state)
347e35a3fb5SSoby Mathew {
348e35a3fb5SSoby Mathew unsigned int i;
349e35a3fb5SSoby Mathew
350e35a3fb5SSoby Mathew for (i = ARM_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++)
351e35a3fb5SSoby Mathew req_state->pwr_domain_state[i] = ARM_LOCAL_STATE_OFF;
352e0ef05bbSWing Li
353e0ef05bbSWing Li #if PSCI_OS_INIT_MODE
354e0ef05bbSWing Li req_state->last_at_pwrlvl = PLAT_MAX_PWR_LVL;
355e0ef05bbSWing Li #endif
356e35a3fb5SSoby Mathew }
3571af540efSRoberto Vargas #endif
358e35a3fb5SSoby Mathew
359e35a3fb5SSoby Mathew /*******************************************************************************
360e35a3fb5SSoby Mathew * Handler to filter PSCI requests.
361e35a3fb5SSoby Mathew ******************************************************************************/
362e35a3fb5SSoby Mathew /*
363e35a3fb5SSoby Mathew * The system power domain suspend is only supported only via
364e35a3fb5SSoby Mathew * PSCI SYSTEM_SUSPEND API. PSCI CPU_SUSPEND request to system power domain
365e35a3fb5SSoby Mathew * will be downgraded to the lower level.
366e35a3fb5SSoby Mathew */
fvp_validate_power_state(unsigned int power_state,psci_power_state_t * req_state)367e35a3fb5SSoby Mathew static int fvp_validate_power_state(unsigned int power_state,
368e35a3fb5SSoby Mathew psci_power_state_t *req_state)
369e35a3fb5SSoby Mathew {
370e35a3fb5SSoby Mathew int rc;
371e35a3fb5SSoby Mathew rc = arm_validate_power_state(power_state, req_state);
372e35a3fb5SSoby Mathew
373e35a3fb5SSoby Mathew /*
374e35a3fb5SSoby Mathew * Ensure that the system power domain level is never suspended
375e35a3fb5SSoby Mathew * via PSCI CPU SUSPEND API. Currently system suspend is only
376e35a3fb5SSoby Mathew * supported via PSCI SYSTEM SUSPEND API.
377e35a3fb5SSoby Mathew */
378e35a3fb5SSoby Mathew req_state->pwr_domain_state[ARM_PWR_LVL2] = ARM_LOCAL_STATE_RUN;
379e35a3fb5SSoby Mathew return rc;
380e35a3fb5SSoby Mathew }
381e35a3fb5SSoby Mathew
382e35a3fb5SSoby Mathew /*
383e35a3fb5SSoby Mathew * Custom `translate_power_state_by_mpidr` handler for FVP. Unlike in the
384e35a3fb5SSoby Mathew * `fvp_validate_power_state`, we do not downgrade the system power
385e35a3fb5SSoby Mathew * domain level request in `power_state` as it will be used to query the
386e35a3fb5SSoby Mathew * PSCI_STAT_COUNT/RESIDENCY at the system power domain level.
387e35a3fb5SSoby Mathew */
fvp_translate_power_state_by_mpidr(u_register_t mpidr,unsigned int power_state,psci_power_state_t * output_state)388e35a3fb5SSoby Mathew static int fvp_translate_power_state_by_mpidr(u_register_t mpidr,
389e35a3fb5SSoby Mathew unsigned int power_state,
390e35a3fb5SSoby Mathew psci_power_state_t *output_state)
391e35a3fb5SSoby Mathew {
392e35a3fb5SSoby Mathew return arm_validate_power_state(power_state, output_state);
393e35a3fb5SSoby Mathew }
394e35a3fb5SSoby Mathew
3953fc4124cSDan Handley /*******************************************************************************
396785fb92bSSoby Mathew * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard
397785fb92bSSoby Mathew * platform layer will take care of registering the handlers with PSCI.
3983fc4124cSDan Handley ******************************************************************************/
3995486a965SSoby Mathew plat_psci_ops_t plat_arm_psci_pm_ops = {
40038dce70fSSoby Mathew .cpu_standby = fvp_cpu_standby,
40138dce70fSSoby Mathew .pwr_domain_on = fvp_pwr_domain_on,
40238dce70fSSoby Mathew .pwr_domain_off = fvp_pwr_domain_off,
40338dce70fSSoby Mathew .pwr_domain_suspend = fvp_pwr_domain_suspend,
40438dce70fSSoby Mathew .pwr_domain_on_finish = fvp_pwr_domain_on_finish,
4056806cd23SMadhukar Pappireddy .pwr_domain_on_finish_late = fvp_pwr_domain_on_finish_late,
40638dce70fSSoby Mathew .pwr_domain_suspend_finish = fvp_pwr_domain_suspend_finish,
4073fc4124cSDan Handley .system_off = fvp_system_off,
4083fc4124cSDan Handley .system_reset = fvp_system_reset,
409e35a3fb5SSoby Mathew .validate_power_state = fvp_validate_power_state,
41071e7a4e5SJeenu Viswambharan .validate_ns_entrypoint = arm_validate_psci_entrypoint,
411e35a3fb5SSoby Mathew .translate_power_state_by_mpidr = fvp_translate_power_state_by_mpidr,
412f145403cSRoberto Vargas .get_node_hw_state = fvp_node_hw_state,
4137d44ac1eSAntonio Nino Diaz #if !ARM_BL31_IN_DRAM
4147d44ac1eSAntonio Nino Diaz /*
4157d44ac1eSAntonio Nino Diaz * The TrustZone Controller is set up during the warmboot sequence after
4167d44ac1eSAntonio Nino Diaz * resuming the CPU from a SYSTEM_SUSPEND. If BL31 is located in SRAM
4177d44ac1eSAntonio Nino Diaz * this is not a problem but, if it is in TZC-secured DRAM, it tries to
4187d44ac1eSAntonio Nino Diaz * reconfigure the same memory it is running on, causing an exception.
4197d44ac1eSAntonio Nino Diaz */
420e35a3fb5SSoby Mathew .get_sys_suspend_power_state = fvp_get_sys_suspend_power_state,
4217d44ac1eSAntonio Nino Diaz #endif
422f145403cSRoberto Vargas .mem_protect_chk = arm_psci_mem_protect_chk,
423f145403cSRoberto Vargas .read_mem_protect = arm_psci_read_mem_protect,
424f145403cSRoberto Vargas .write_mem_protect = arm_nor_psci_write_mem_protect,
4253fc4124cSDan Handley };
42689f2e589SChandni Cherukuri
plat_arm_psci_override_pm_ops(plat_psci_ops_t * ops)42789f2e589SChandni Cherukuri const plat_psci_ops_t *plat_arm_psci_override_pm_ops(plat_psci_ops_t *ops)
42889f2e589SChandni Cherukuri {
42989f2e589SChandni Cherukuri return ops;
43089f2e589SChandni Cherukuri }
431