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