13fc4124cSDan Handley /* 26355f234SVikram Kanigiri * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. 33fc4124cSDan Handley * 4*82cb2c1aSdp-arm * SPDX-License-Identifier: BSD-3-Clause 53fc4124cSDan Handley */ 63fc4124cSDan Handley 73fc4124cSDan Handley #include <arch_helpers.h> 83fc4124cSDan Handley #include <arm_config.h> 93fc4124cSDan Handley #include <assert.h> 103fc4124cSDan Handley #include <debug.h> 113fc4124cSDan Handley #include <errno.h> 123fc4124cSDan Handley #include <mmio.h> 133fc4124cSDan Handley #include <platform.h> 143fc4124cSDan Handley #include <plat_arm.h> 153fc4124cSDan Handley #include <psci.h> 163fc4124cSDan Handley #include <v2m_def.h> 173fc4124cSDan Handley #include "drivers/pwrc/fvp_pwrc.h" 183fc4124cSDan Handley #include "fvp_def.h" 193fc4124cSDan Handley #include "fvp_private.h" 203fc4124cSDan Handley 213fc4124cSDan Handley 222204afdeSSoby Mathew #if ARM_RECOM_STATE_ID_ENC 232204afdeSSoby Mathew /* 242204afdeSSoby Mathew * The table storing the valid idle power states. Ensure that the 252204afdeSSoby Mathew * array entries are populated in ascending order of state-id to 262204afdeSSoby Mathew * enable us to use binary search during power state validation. 272204afdeSSoby Mathew * The table must be terminated by a NULL entry. 282204afdeSSoby Mathew */ 292204afdeSSoby Mathew const unsigned int arm_pm_idle_states[] = { 302204afdeSSoby Mathew /* State-id - 0x01 */ 312204afdeSSoby Mathew arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_RET, 322204afdeSSoby Mathew ARM_PWR_LVL0, PSTATE_TYPE_STANDBY), 332204afdeSSoby Mathew /* State-id - 0x02 */ 342204afdeSSoby Mathew arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_OFF, 352204afdeSSoby Mathew ARM_PWR_LVL0, PSTATE_TYPE_POWERDOWN), 362204afdeSSoby Mathew /* State-id - 0x22 */ 372204afdeSSoby Mathew arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_OFF, ARM_LOCAL_STATE_OFF, 382204afdeSSoby Mathew ARM_PWR_LVL1, PSTATE_TYPE_POWERDOWN), 392204afdeSSoby Mathew 0, 402204afdeSSoby Mathew }; 412204afdeSSoby Mathew #endif 422204afdeSSoby Mathew 433fc4124cSDan Handley /******************************************************************************* 443fc4124cSDan Handley * Function which implements the common FVP specific operations to power down a 453fc4124cSDan Handley * cluster in response to a CPU_OFF or CPU_SUSPEND request. 463fc4124cSDan Handley ******************************************************************************/ 473fc4124cSDan Handley static void fvp_cluster_pwrdwn_common(void) 483fc4124cSDan Handley { 493fc4124cSDan Handley uint64_t mpidr = read_mpidr_el1(); 503fc4124cSDan Handley 513fc4124cSDan Handley /* Disable coherency if this cluster is to be turned off */ 526355f234SVikram Kanigiri fvp_interconnect_disable(); 533fc4124cSDan Handley 543fc4124cSDan Handley /* Program the power controller to turn the cluster off */ 553fc4124cSDan Handley fvp_pwrc_write_pcoffr(mpidr); 563fc4124cSDan Handley } 573fc4124cSDan Handley 58f14d1886SSoby Mathew static void fvp_power_domain_on_finish_common(const psci_power_state_t *target_state) 59f14d1886SSoby Mathew { 60f14d1886SSoby Mathew unsigned long mpidr; 61f14d1886SSoby Mathew 62f14d1886SSoby Mathew assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == 63f14d1886SSoby Mathew ARM_LOCAL_STATE_OFF); 64f14d1886SSoby Mathew 65f14d1886SSoby Mathew /* Get the mpidr for this cpu */ 66f14d1886SSoby Mathew mpidr = read_mpidr_el1(); 67f14d1886SSoby Mathew 68f14d1886SSoby Mathew /* Perform the common cluster specific operations */ 69f14d1886SSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL1] == 70f14d1886SSoby Mathew ARM_LOCAL_STATE_OFF) { 71f14d1886SSoby Mathew /* 72f14d1886SSoby Mathew * This CPU might have woken up whilst the cluster was 73f14d1886SSoby Mathew * attempting to power down. In this case the FVP power 74f14d1886SSoby Mathew * controller will have a pending cluster power off request 75f14d1886SSoby Mathew * which needs to be cleared by writing to the PPONR register. 76f14d1886SSoby Mathew * This prevents the power controller from interpreting a 77f14d1886SSoby Mathew * subsequent entry of this cpu into a simple wfi as a power 78f14d1886SSoby Mathew * down request. 79f14d1886SSoby Mathew */ 80f14d1886SSoby Mathew fvp_pwrc_write_pponr(mpidr); 81f14d1886SSoby Mathew 82f14d1886SSoby Mathew /* Enable coherency if this cluster was off */ 836355f234SVikram Kanigiri fvp_interconnect_enable(); 84f14d1886SSoby Mathew } 85f14d1886SSoby Mathew 86f14d1886SSoby Mathew /* 87f14d1886SSoby Mathew * Clear PWKUPR.WEN bit to ensure interrupts do not interfere 88f14d1886SSoby Mathew * with a cpu power down unless the bit is set again 89f14d1886SSoby Mathew */ 90f14d1886SSoby Mathew fvp_pwrc_clr_wen(mpidr); 91f14d1886SSoby Mathew } 92f14d1886SSoby Mathew 93f14d1886SSoby Mathew 943fc4124cSDan Handley /******************************************************************************* 9538dce70fSSoby Mathew * FVP handler called when a CPU is about to enter standby. 963fc4124cSDan Handley ******************************************************************************/ 9738dce70fSSoby Mathew void fvp_cpu_standby(plat_local_state_t cpu_state) 983fc4124cSDan Handley { 9938dce70fSSoby Mathew 10038dce70fSSoby Mathew assert(cpu_state == ARM_LOCAL_STATE_RET); 10138dce70fSSoby Mathew 1023fc4124cSDan Handley /* 1033fc4124cSDan Handley * Enter standby state 1043fc4124cSDan Handley * dsb is good practice before using wfi to enter low power states 1053fc4124cSDan Handley */ 1063fc4124cSDan Handley dsb(); 1073fc4124cSDan Handley wfi(); 1083fc4124cSDan Handley } 1093fc4124cSDan Handley 1103fc4124cSDan Handley /******************************************************************************* 11138dce70fSSoby Mathew * FVP handler called when a power domain is about to be turned on. The 11238dce70fSSoby Mathew * mpidr determines the CPU to be turned on. 1133fc4124cSDan Handley ******************************************************************************/ 11438dce70fSSoby Mathew int fvp_pwr_domain_on(u_register_t mpidr) 1153fc4124cSDan Handley { 1163fc4124cSDan Handley int rc = PSCI_E_SUCCESS; 1173fc4124cSDan Handley unsigned int psysr; 1183fc4124cSDan Handley 1193fc4124cSDan Handley /* 1200f09c8f7SSandrine Bailleux * Ensure that we do not cancel an inflight power off request for the 1210f09c8f7SSandrine Bailleux * target cpu. That would leave it in a zombie wfi. Wait for it to power 1220f09c8f7SSandrine Bailleux * off and then program the power controller to turn that CPU on. 1233fc4124cSDan Handley */ 1243fc4124cSDan Handley do { 1253fc4124cSDan Handley psysr = fvp_pwrc_read_psysr(mpidr); 1263fc4124cSDan Handley } while (psysr & PSYSR_AFF_L0); 1273fc4124cSDan Handley 1283fc4124cSDan Handley fvp_pwrc_write_pponr(mpidr); 1293fc4124cSDan Handley return rc; 1303fc4124cSDan Handley } 1313fc4124cSDan Handley 1323fc4124cSDan Handley /******************************************************************************* 13338dce70fSSoby Mathew * FVP handler called when a power domain is about to be turned off. The 13438dce70fSSoby Mathew * target_state encodes the power state that each level should transition to. 1353fc4124cSDan Handley ******************************************************************************/ 13638dce70fSSoby Mathew void fvp_pwr_domain_off(const psci_power_state_t *target_state) 1373fc4124cSDan Handley { 13838dce70fSSoby Mathew assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == 13938dce70fSSoby Mathew ARM_LOCAL_STATE_OFF); 1403fc4124cSDan Handley 1413fc4124cSDan Handley /* 14238dce70fSSoby Mathew * If execution reaches this stage then this power domain will be 14338dce70fSSoby Mathew * suspended. Perform at least the cpu specific actions followed 14438dce70fSSoby Mathew * by the cluster specific operations if applicable. 1453fc4124cSDan Handley */ 14674a9578cSJeenu Viswambharan 14774a9578cSJeenu Viswambharan /* Prevent interrupts from spuriously waking up this cpu */ 14874a9578cSJeenu Viswambharan plat_arm_gic_cpuif_disable(); 14974a9578cSJeenu Viswambharan 15074a9578cSJeenu Viswambharan /* Turn redistributor off */ 15174a9578cSJeenu Viswambharan plat_arm_gic_redistif_off(); 15274a9578cSJeenu Viswambharan 15374a9578cSJeenu Viswambharan /* Program the power controller to power off this cpu. */ 15474a9578cSJeenu Viswambharan fvp_pwrc_write_ppoffr(read_mpidr_el1()); 1553fc4124cSDan Handley 15638dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL1] == 15738dce70fSSoby Mathew ARM_LOCAL_STATE_OFF) 1583fc4124cSDan Handley fvp_cluster_pwrdwn_common(); 1593fc4124cSDan Handley 1603fc4124cSDan Handley } 1613fc4124cSDan Handley 1623fc4124cSDan Handley /******************************************************************************* 16338dce70fSSoby Mathew * FVP handler called when a power domain is about to be suspended. The 16438dce70fSSoby Mathew * target_state encodes the power state that each level should transition to. 1653fc4124cSDan Handley ******************************************************************************/ 16638dce70fSSoby Mathew void fvp_pwr_domain_suspend(const psci_power_state_t *target_state) 1673fc4124cSDan Handley { 1683fc4124cSDan Handley unsigned long mpidr; 1693fc4124cSDan Handley 17038dce70fSSoby Mathew /* 17138dce70fSSoby Mathew * FVP has retention only at cpu level. Just return 17238dce70fSSoby Mathew * as nothing is to be done for retention. 17338dce70fSSoby Mathew */ 17438dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL0] == 17538dce70fSSoby Mathew ARM_LOCAL_STATE_RET) 1763fc4124cSDan Handley return; 1773fc4124cSDan Handley 17838dce70fSSoby Mathew assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == 17938dce70fSSoby Mathew ARM_LOCAL_STATE_OFF); 18038dce70fSSoby Mathew 1813fc4124cSDan Handley /* Get the mpidr for this cpu */ 1823fc4124cSDan Handley mpidr = read_mpidr_el1(); 1833fc4124cSDan Handley 1843fc4124cSDan Handley /* Program the power controller to enable wakeup interrupts. */ 1853fc4124cSDan Handley fvp_pwrc_set_wen(mpidr); 1863fc4124cSDan Handley 18774a9578cSJeenu Viswambharan /* Prevent interrupts from spuriously waking up this cpu */ 18874a9578cSJeenu Viswambharan plat_arm_gic_cpuif_disable(); 18974a9578cSJeenu Viswambharan 19074a9578cSJeenu Viswambharan /* 19174a9578cSJeenu Viswambharan * The Redistributor is not powered off as it can potentially prevent 19274a9578cSJeenu Viswambharan * wake up events reaching the CPUIF and/or might lead to losing 19374a9578cSJeenu Viswambharan * register context. 19474a9578cSJeenu Viswambharan */ 19574a9578cSJeenu Viswambharan 19674a9578cSJeenu Viswambharan /* Program the power controller to power off this cpu. */ 19774a9578cSJeenu Viswambharan fvp_pwrc_write_ppoffr(read_mpidr_el1()); 1983fc4124cSDan Handley 1993fc4124cSDan Handley /* Perform the common cluster specific operations */ 20038dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL1] == 20138dce70fSSoby Mathew ARM_LOCAL_STATE_OFF) 2023fc4124cSDan Handley fvp_cluster_pwrdwn_common(); 2033fc4124cSDan Handley } 2043fc4124cSDan Handley 2053fc4124cSDan Handley /******************************************************************************* 20638dce70fSSoby Mathew * FVP handler called when a power domain has just been powered on after 20738dce70fSSoby Mathew * being turned off earlier. The target_state encodes the low power state that 20838dce70fSSoby Mathew * each level has woken up from. 2093fc4124cSDan Handley ******************************************************************************/ 21038dce70fSSoby Mathew void fvp_pwr_domain_on_finish(const psci_power_state_t *target_state) 2113fc4124cSDan Handley { 212f14d1886SSoby Mathew fvp_power_domain_on_finish_common(target_state); 2133fc4124cSDan Handley 2143fc4124cSDan Handley /* Enable the gic cpu interface */ 21527573c59SAchin Gupta plat_arm_gic_pcpu_init(); 21627573c59SAchin Gupta 21727573c59SAchin Gupta /* Program the gic per-cpu distributor or re-distributor interface */ 21827573c59SAchin Gupta plat_arm_gic_cpuif_enable(); 2193fc4124cSDan Handley } 2203fc4124cSDan Handley 2213fc4124cSDan Handley /******************************************************************************* 22238dce70fSSoby Mathew * FVP handler called when a power domain has just been powered on after 22338dce70fSSoby Mathew * having been suspended earlier. The target_state encodes the low power state 22438dce70fSSoby Mathew * that each level has woken up from. 2253fc4124cSDan Handley * TODO: At the moment we reuse the on finisher and reinitialize the secure 2263fc4124cSDan Handley * context. Need to implement a separate suspend finisher. 2273fc4124cSDan Handley ******************************************************************************/ 22838dce70fSSoby Mathew void fvp_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 2293fc4124cSDan Handley { 23038dce70fSSoby Mathew /* 23138dce70fSSoby Mathew * Nothing to be done on waking up from retention from CPU level. 23238dce70fSSoby Mathew */ 23338dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL0] == 23438dce70fSSoby Mathew ARM_LOCAL_STATE_RET) 23538dce70fSSoby Mathew return; 23638dce70fSSoby Mathew 237f14d1886SSoby Mathew fvp_power_domain_on_finish_common(target_state); 238f14d1886SSoby Mathew 239f14d1886SSoby Mathew /* Enable the gic cpu interface */ 24027573c59SAchin Gupta plat_arm_gic_cpuif_enable(); 2413fc4124cSDan Handley } 2423fc4124cSDan Handley 2433fc4124cSDan Handley /******************************************************************************* 2443fc4124cSDan Handley * FVP handlers to shutdown/reboot the system 2453fc4124cSDan Handley ******************************************************************************/ 2463fc4124cSDan Handley static void __dead2 fvp_system_off(void) 2473fc4124cSDan Handley { 2483fc4124cSDan Handley /* Write the System Configuration Control Register */ 2493fc4124cSDan Handley mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL, 2503fc4124cSDan Handley V2M_CFGCTRL_START | 2513fc4124cSDan Handley V2M_CFGCTRL_RW | 2523fc4124cSDan Handley V2M_CFGCTRL_FUNC(V2M_FUNC_SHUTDOWN)); 2533fc4124cSDan Handley wfi(); 2543fc4124cSDan Handley ERROR("FVP System Off: operation not handled.\n"); 2553fc4124cSDan Handley panic(); 2563fc4124cSDan Handley } 2573fc4124cSDan Handley 2583fc4124cSDan Handley static void __dead2 fvp_system_reset(void) 2593fc4124cSDan Handley { 2603fc4124cSDan Handley /* Write the System Configuration Control Register */ 2613fc4124cSDan Handley mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL, 2623fc4124cSDan Handley V2M_CFGCTRL_START | 2633fc4124cSDan Handley V2M_CFGCTRL_RW | 2643fc4124cSDan Handley V2M_CFGCTRL_FUNC(V2M_FUNC_REBOOT)); 2653fc4124cSDan Handley wfi(); 2663fc4124cSDan Handley ERROR("FVP System Reset: operation not handled.\n"); 2673fc4124cSDan Handley panic(); 2683fc4124cSDan Handley } 2693fc4124cSDan Handley 2701298ae02SJeenu Viswambharan static int fvp_node_hw_state(u_register_t target_cpu, 2711298ae02SJeenu Viswambharan unsigned int power_level) 2721298ae02SJeenu Viswambharan { 2731298ae02SJeenu Viswambharan unsigned int psysr; 2741298ae02SJeenu Viswambharan int ret; 2751298ae02SJeenu Viswambharan 2761298ae02SJeenu Viswambharan /* 2771298ae02SJeenu Viswambharan * The format of 'power_level' is implementation-defined, but 0 must 2781298ae02SJeenu Viswambharan * mean a CPU. We also allow 1 to denote the cluster 2791298ae02SJeenu Viswambharan */ 2801298ae02SJeenu Viswambharan if (power_level != ARM_PWR_LVL0 && power_level != ARM_PWR_LVL1) 2811298ae02SJeenu Viswambharan return PSCI_E_INVALID_PARAMS; 2821298ae02SJeenu Viswambharan 2831298ae02SJeenu Viswambharan /* 2841298ae02SJeenu Viswambharan * Read the status of the given MPDIR from FVP power controller. The 2851298ae02SJeenu Viswambharan * power controller only gives us on/off status, so map that to expected 2861298ae02SJeenu Viswambharan * return values of the PSCI call 2871298ae02SJeenu Viswambharan */ 2881298ae02SJeenu Viswambharan psysr = fvp_pwrc_read_psysr(target_cpu); 2891298ae02SJeenu Viswambharan if (psysr == PSYSR_INVALID) 2901298ae02SJeenu Viswambharan return PSCI_E_INVALID_PARAMS; 2911298ae02SJeenu Viswambharan 2921298ae02SJeenu Viswambharan switch (power_level) { 2931298ae02SJeenu Viswambharan case ARM_PWR_LVL0: 2941298ae02SJeenu Viswambharan ret = (psysr & PSYSR_AFF_L0) ? HW_ON : HW_OFF; 2951298ae02SJeenu Viswambharan break; 2961298ae02SJeenu Viswambharan case ARM_PWR_LVL1: 2971298ae02SJeenu Viswambharan ret = (psysr & PSYSR_AFF_L1) ? HW_ON : HW_OFF; 2981298ae02SJeenu Viswambharan break; 2991298ae02SJeenu Viswambharan default: 3001298ae02SJeenu Viswambharan assert(0); 3011298ae02SJeenu Viswambharan } 3021298ae02SJeenu Viswambharan 3031298ae02SJeenu Viswambharan return ret; 3041298ae02SJeenu Viswambharan } 3051298ae02SJeenu Viswambharan 3063fc4124cSDan Handley /******************************************************************************* 307785fb92bSSoby Mathew * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard 308785fb92bSSoby Mathew * platform layer will take care of registering the handlers with PSCI. 3093fc4124cSDan Handley ******************************************************************************/ 3105486a965SSoby Mathew plat_psci_ops_t plat_arm_psci_pm_ops = { 31138dce70fSSoby Mathew .cpu_standby = fvp_cpu_standby, 31238dce70fSSoby Mathew .pwr_domain_on = fvp_pwr_domain_on, 31338dce70fSSoby Mathew .pwr_domain_off = fvp_pwr_domain_off, 31438dce70fSSoby Mathew .pwr_domain_suspend = fvp_pwr_domain_suspend, 31538dce70fSSoby Mathew .pwr_domain_on_finish = fvp_pwr_domain_on_finish, 31638dce70fSSoby Mathew .pwr_domain_suspend_finish = fvp_pwr_domain_suspend_finish, 3173fc4124cSDan Handley .system_off = fvp_system_off, 3183fc4124cSDan Handley .system_reset = fvp_system_reset, 319f9e858b1SSoby Mathew .validate_power_state = arm_validate_power_state, 3201298ae02SJeenu Viswambharan .validate_ns_entrypoint = arm_validate_ns_entrypoint, 3211298ae02SJeenu Viswambharan .get_node_hw_state = fvp_node_hw_state 3223fc4124cSDan Handley }; 323