13fc4124cSDan Handley /* 26355f234SVikram Kanigiri * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. 33fc4124cSDan Handley * 43fc4124cSDan Handley * Redistribution and use in source and binary forms, with or without 53fc4124cSDan Handley * modification, are permitted provided that the following conditions are met: 63fc4124cSDan Handley * 73fc4124cSDan Handley * Redistributions of source code must retain the above copyright notice, this 83fc4124cSDan Handley * list of conditions and the following disclaimer. 93fc4124cSDan Handley * 103fc4124cSDan Handley * Redistributions in binary form must reproduce the above copyright notice, 113fc4124cSDan Handley * this list of conditions and the following disclaimer in the documentation 123fc4124cSDan Handley * and/or other materials provided with the distribution. 133fc4124cSDan Handley * 143fc4124cSDan Handley * Neither the name of ARM nor the names of its contributors may be used 153fc4124cSDan Handley * to endorse or promote products derived from this software without specific 163fc4124cSDan Handley * prior written permission. 173fc4124cSDan Handley * 183fc4124cSDan Handley * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 193fc4124cSDan Handley * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 203fc4124cSDan Handley * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 213fc4124cSDan Handley * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 223fc4124cSDan Handley * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 233fc4124cSDan Handley * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 243fc4124cSDan Handley * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 253fc4124cSDan Handley * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 263fc4124cSDan Handley * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 273fc4124cSDan Handley * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 283fc4124cSDan Handley * POSSIBILITY OF SUCH DAMAGE. 293fc4124cSDan Handley */ 303fc4124cSDan Handley 313fc4124cSDan Handley #include <arch_helpers.h> 323fc4124cSDan Handley #include <arm_config.h> 333fc4124cSDan Handley #include <assert.h> 343fc4124cSDan Handley #include <debug.h> 353fc4124cSDan Handley #include <errno.h> 363fc4124cSDan Handley #include <mmio.h> 373fc4124cSDan Handley #include <platform.h> 383fc4124cSDan Handley #include <plat_arm.h> 393fc4124cSDan Handley #include <psci.h> 403fc4124cSDan Handley #include <v2m_def.h> 413fc4124cSDan Handley #include "drivers/pwrc/fvp_pwrc.h" 423fc4124cSDan Handley #include "fvp_def.h" 433fc4124cSDan Handley #include "fvp_private.h" 443fc4124cSDan Handley 453fc4124cSDan Handley 462204afdeSSoby Mathew #if ARM_RECOM_STATE_ID_ENC 472204afdeSSoby Mathew /* 482204afdeSSoby Mathew * The table storing the valid idle power states. Ensure that the 492204afdeSSoby Mathew * array entries are populated in ascending order of state-id to 502204afdeSSoby Mathew * enable us to use binary search during power state validation. 512204afdeSSoby Mathew * The table must be terminated by a NULL entry. 522204afdeSSoby Mathew */ 532204afdeSSoby Mathew const unsigned int arm_pm_idle_states[] = { 542204afdeSSoby Mathew /* State-id - 0x01 */ 552204afdeSSoby Mathew arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_RET, 562204afdeSSoby Mathew ARM_PWR_LVL0, PSTATE_TYPE_STANDBY), 572204afdeSSoby Mathew /* State-id - 0x02 */ 582204afdeSSoby Mathew arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_OFF, 592204afdeSSoby Mathew ARM_PWR_LVL0, PSTATE_TYPE_POWERDOWN), 602204afdeSSoby Mathew /* State-id - 0x22 */ 612204afdeSSoby Mathew arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_OFF, ARM_LOCAL_STATE_OFF, 622204afdeSSoby Mathew ARM_PWR_LVL1, PSTATE_TYPE_POWERDOWN), 632204afdeSSoby Mathew 0, 642204afdeSSoby Mathew }; 652204afdeSSoby Mathew #endif 662204afdeSSoby Mathew 673fc4124cSDan Handley /******************************************************************************* 683fc4124cSDan Handley * Function which implements the common FVP specific operations to power down a 693fc4124cSDan Handley * cluster in response to a CPU_OFF or CPU_SUSPEND request. 703fc4124cSDan Handley ******************************************************************************/ 713fc4124cSDan Handley static void fvp_cluster_pwrdwn_common(void) 723fc4124cSDan Handley { 733fc4124cSDan Handley uint64_t mpidr = read_mpidr_el1(); 743fc4124cSDan Handley 753fc4124cSDan Handley /* Disable coherency if this cluster is to be turned off */ 766355f234SVikram Kanigiri fvp_interconnect_disable(); 773fc4124cSDan Handley 783fc4124cSDan Handley /* Program the power controller to turn the cluster off */ 793fc4124cSDan Handley fvp_pwrc_write_pcoffr(mpidr); 803fc4124cSDan Handley } 813fc4124cSDan Handley 82f14d1886SSoby Mathew static void fvp_power_domain_on_finish_common(const psci_power_state_t *target_state) 83f14d1886SSoby Mathew { 84f14d1886SSoby Mathew unsigned long mpidr; 85f14d1886SSoby Mathew 86f14d1886SSoby Mathew assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == 87f14d1886SSoby Mathew ARM_LOCAL_STATE_OFF); 88f14d1886SSoby Mathew 89f14d1886SSoby Mathew /* Get the mpidr for this cpu */ 90f14d1886SSoby Mathew mpidr = read_mpidr_el1(); 91f14d1886SSoby Mathew 92f14d1886SSoby Mathew /* Perform the common cluster specific operations */ 93f14d1886SSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL1] == 94f14d1886SSoby Mathew ARM_LOCAL_STATE_OFF) { 95f14d1886SSoby Mathew /* 96f14d1886SSoby Mathew * This CPU might have woken up whilst the cluster was 97f14d1886SSoby Mathew * attempting to power down. In this case the FVP power 98f14d1886SSoby Mathew * controller will have a pending cluster power off request 99f14d1886SSoby Mathew * which needs to be cleared by writing to the PPONR register. 100f14d1886SSoby Mathew * This prevents the power controller from interpreting a 101f14d1886SSoby Mathew * subsequent entry of this cpu into a simple wfi as a power 102f14d1886SSoby Mathew * down request. 103f14d1886SSoby Mathew */ 104f14d1886SSoby Mathew fvp_pwrc_write_pponr(mpidr); 105f14d1886SSoby Mathew 106f14d1886SSoby Mathew /* Enable coherency if this cluster was off */ 1076355f234SVikram Kanigiri fvp_interconnect_enable(); 108f14d1886SSoby Mathew } 109f14d1886SSoby Mathew 110f14d1886SSoby Mathew /* 111f14d1886SSoby Mathew * Clear PWKUPR.WEN bit to ensure interrupts do not interfere 112f14d1886SSoby Mathew * with a cpu power down unless the bit is set again 113f14d1886SSoby Mathew */ 114f14d1886SSoby Mathew fvp_pwrc_clr_wen(mpidr); 115f14d1886SSoby Mathew } 116f14d1886SSoby Mathew 117f14d1886SSoby Mathew 1183fc4124cSDan Handley /******************************************************************************* 11938dce70fSSoby Mathew * FVP handler called when a CPU is about to enter standby. 1203fc4124cSDan Handley ******************************************************************************/ 12138dce70fSSoby Mathew void fvp_cpu_standby(plat_local_state_t cpu_state) 1223fc4124cSDan Handley { 12338dce70fSSoby Mathew 12438dce70fSSoby Mathew assert(cpu_state == ARM_LOCAL_STATE_RET); 12538dce70fSSoby Mathew 1263fc4124cSDan Handley /* 1273fc4124cSDan Handley * Enter standby state 1283fc4124cSDan Handley * dsb is good practice before using wfi to enter low power states 1293fc4124cSDan Handley */ 1303fc4124cSDan Handley dsb(); 1313fc4124cSDan Handley wfi(); 1323fc4124cSDan Handley } 1333fc4124cSDan Handley 1343fc4124cSDan Handley /******************************************************************************* 13538dce70fSSoby Mathew * FVP handler called when a power domain is about to be turned on. The 13638dce70fSSoby Mathew * mpidr determines the CPU to be turned on. 1373fc4124cSDan Handley ******************************************************************************/ 13838dce70fSSoby Mathew int fvp_pwr_domain_on(u_register_t mpidr) 1393fc4124cSDan Handley { 1403fc4124cSDan Handley int rc = PSCI_E_SUCCESS; 1413fc4124cSDan Handley unsigned int psysr; 1423fc4124cSDan Handley 1433fc4124cSDan Handley /* 1440f09c8f7SSandrine Bailleux * Ensure that we do not cancel an inflight power off request for the 1450f09c8f7SSandrine Bailleux * target cpu. That would leave it in a zombie wfi. Wait for it to power 1460f09c8f7SSandrine Bailleux * off and then program the power controller to turn that CPU on. 1473fc4124cSDan Handley */ 1483fc4124cSDan Handley do { 1493fc4124cSDan Handley psysr = fvp_pwrc_read_psysr(mpidr); 1503fc4124cSDan Handley } while (psysr & PSYSR_AFF_L0); 1513fc4124cSDan Handley 1523fc4124cSDan Handley fvp_pwrc_write_pponr(mpidr); 1533fc4124cSDan Handley return rc; 1543fc4124cSDan Handley } 1553fc4124cSDan Handley 1563fc4124cSDan Handley /******************************************************************************* 15738dce70fSSoby Mathew * FVP handler called when a power domain is about to be turned off. The 15838dce70fSSoby Mathew * target_state encodes the power state that each level should transition to. 1593fc4124cSDan Handley ******************************************************************************/ 16038dce70fSSoby Mathew void fvp_pwr_domain_off(const psci_power_state_t *target_state) 1613fc4124cSDan Handley { 16238dce70fSSoby Mathew assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == 16338dce70fSSoby Mathew ARM_LOCAL_STATE_OFF); 1643fc4124cSDan Handley 1653fc4124cSDan Handley /* 16638dce70fSSoby Mathew * If execution reaches this stage then this power domain will be 16738dce70fSSoby Mathew * suspended. Perform at least the cpu specific actions followed 16838dce70fSSoby Mathew * by the cluster specific operations if applicable. 1693fc4124cSDan Handley */ 170*74a9578cSJeenu Viswambharan 171*74a9578cSJeenu Viswambharan /* Prevent interrupts from spuriously waking up this cpu */ 172*74a9578cSJeenu Viswambharan plat_arm_gic_cpuif_disable(); 173*74a9578cSJeenu Viswambharan 174*74a9578cSJeenu Viswambharan /* Turn redistributor off */ 175*74a9578cSJeenu Viswambharan plat_arm_gic_redistif_off(); 176*74a9578cSJeenu Viswambharan 177*74a9578cSJeenu Viswambharan /* Program the power controller to power off this cpu. */ 178*74a9578cSJeenu Viswambharan fvp_pwrc_write_ppoffr(read_mpidr_el1()); 1793fc4124cSDan Handley 18038dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL1] == 18138dce70fSSoby Mathew ARM_LOCAL_STATE_OFF) 1823fc4124cSDan Handley fvp_cluster_pwrdwn_common(); 1833fc4124cSDan Handley 1843fc4124cSDan Handley } 1853fc4124cSDan Handley 1863fc4124cSDan Handley /******************************************************************************* 18738dce70fSSoby Mathew * FVP handler called when a power domain is about to be suspended. The 18838dce70fSSoby Mathew * target_state encodes the power state that each level should transition to. 1893fc4124cSDan Handley ******************************************************************************/ 19038dce70fSSoby Mathew void fvp_pwr_domain_suspend(const psci_power_state_t *target_state) 1913fc4124cSDan Handley { 1923fc4124cSDan Handley unsigned long mpidr; 1933fc4124cSDan Handley 19438dce70fSSoby Mathew /* 19538dce70fSSoby Mathew * FVP has retention only at cpu level. Just return 19638dce70fSSoby Mathew * as nothing is to be done for retention. 19738dce70fSSoby Mathew */ 19838dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL0] == 19938dce70fSSoby Mathew ARM_LOCAL_STATE_RET) 2003fc4124cSDan Handley return; 2013fc4124cSDan Handley 20238dce70fSSoby Mathew assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == 20338dce70fSSoby Mathew ARM_LOCAL_STATE_OFF); 20438dce70fSSoby Mathew 2053fc4124cSDan Handley /* Get the mpidr for this cpu */ 2063fc4124cSDan Handley mpidr = read_mpidr_el1(); 2073fc4124cSDan Handley 2083fc4124cSDan Handley /* Program the power controller to enable wakeup interrupts. */ 2093fc4124cSDan Handley fvp_pwrc_set_wen(mpidr); 2103fc4124cSDan Handley 211*74a9578cSJeenu Viswambharan /* Prevent interrupts from spuriously waking up this cpu */ 212*74a9578cSJeenu Viswambharan plat_arm_gic_cpuif_disable(); 213*74a9578cSJeenu Viswambharan 214*74a9578cSJeenu Viswambharan /* 215*74a9578cSJeenu Viswambharan * The Redistributor is not powered off as it can potentially prevent 216*74a9578cSJeenu Viswambharan * wake up events reaching the CPUIF and/or might lead to losing 217*74a9578cSJeenu Viswambharan * register context. 218*74a9578cSJeenu Viswambharan */ 219*74a9578cSJeenu Viswambharan 220*74a9578cSJeenu Viswambharan /* Program the power controller to power off this cpu. */ 221*74a9578cSJeenu Viswambharan fvp_pwrc_write_ppoffr(read_mpidr_el1()); 2223fc4124cSDan Handley 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(); 2273fc4124cSDan Handley } 2283fc4124cSDan Handley 2293fc4124cSDan Handley /******************************************************************************* 23038dce70fSSoby Mathew * FVP handler called when a power domain has just been powered on after 23138dce70fSSoby Mathew * being turned off earlier. The target_state encodes the low power state that 23238dce70fSSoby Mathew * each level has woken up from. 2333fc4124cSDan Handley ******************************************************************************/ 23438dce70fSSoby Mathew void fvp_pwr_domain_on_finish(const psci_power_state_t *target_state) 2353fc4124cSDan Handley { 236f14d1886SSoby Mathew fvp_power_domain_on_finish_common(target_state); 2373fc4124cSDan Handley 2383fc4124cSDan Handley /* Enable the gic cpu interface */ 23927573c59SAchin Gupta plat_arm_gic_pcpu_init(); 24027573c59SAchin Gupta 24127573c59SAchin Gupta /* Program the gic per-cpu distributor or re-distributor interface */ 24227573c59SAchin Gupta plat_arm_gic_cpuif_enable(); 2433fc4124cSDan Handley } 2443fc4124cSDan Handley 2453fc4124cSDan Handley /******************************************************************************* 24638dce70fSSoby Mathew * FVP handler called when a power domain has just been powered on after 24738dce70fSSoby Mathew * having been suspended earlier. The target_state encodes the low power state 24838dce70fSSoby Mathew * that each level has woken up from. 2493fc4124cSDan Handley * TODO: At the moment we reuse the on finisher and reinitialize the secure 2503fc4124cSDan Handley * context. Need to implement a separate suspend finisher. 2513fc4124cSDan Handley ******************************************************************************/ 25238dce70fSSoby Mathew void fvp_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 2533fc4124cSDan Handley { 25438dce70fSSoby Mathew /* 25538dce70fSSoby Mathew * Nothing to be done on waking up from retention from CPU level. 25638dce70fSSoby Mathew */ 25738dce70fSSoby Mathew if (target_state->pwr_domain_state[ARM_PWR_LVL0] == 25838dce70fSSoby Mathew ARM_LOCAL_STATE_RET) 25938dce70fSSoby Mathew return; 26038dce70fSSoby Mathew 261f14d1886SSoby Mathew fvp_power_domain_on_finish_common(target_state); 262f14d1886SSoby Mathew 263f14d1886SSoby Mathew /* Enable the gic cpu interface */ 26427573c59SAchin Gupta plat_arm_gic_cpuif_enable(); 2653fc4124cSDan Handley } 2663fc4124cSDan Handley 2673fc4124cSDan Handley /******************************************************************************* 2683fc4124cSDan Handley * FVP handlers to shutdown/reboot the system 2693fc4124cSDan Handley ******************************************************************************/ 2703fc4124cSDan Handley static void __dead2 fvp_system_off(void) 2713fc4124cSDan Handley { 2723fc4124cSDan Handley /* Write the System Configuration Control Register */ 2733fc4124cSDan Handley mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL, 2743fc4124cSDan Handley V2M_CFGCTRL_START | 2753fc4124cSDan Handley V2M_CFGCTRL_RW | 2763fc4124cSDan Handley V2M_CFGCTRL_FUNC(V2M_FUNC_SHUTDOWN)); 2773fc4124cSDan Handley wfi(); 2783fc4124cSDan Handley ERROR("FVP System Off: operation not handled.\n"); 2793fc4124cSDan Handley panic(); 2803fc4124cSDan Handley } 2813fc4124cSDan Handley 2823fc4124cSDan Handley static void __dead2 fvp_system_reset(void) 2833fc4124cSDan Handley { 2843fc4124cSDan Handley /* Write the System Configuration Control Register */ 2853fc4124cSDan Handley mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL, 2863fc4124cSDan Handley V2M_CFGCTRL_START | 2873fc4124cSDan Handley V2M_CFGCTRL_RW | 2883fc4124cSDan Handley V2M_CFGCTRL_FUNC(V2M_FUNC_REBOOT)); 2893fc4124cSDan Handley wfi(); 2903fc4124cSDan Handley ERROR("FVP System Reset: operation not handled.\n"); 2913fc4124cSDan Handley panic(); 2923fc4124cSDan Handley } 2933fc4124cSDan Handley 2941298ae02SJeenu Viswambharan static int fvp_node_hw_state(u_register_t target_cpu, 2951298ae02SJeenu Viswambharan unsigned int power_level) 2961298ae02SJeenu Viswambharan { 2971298ae02SJeenu Viswambharan unsigned int psysr; 2981298ae02SJeenu Viswambharan int ret; 2991298ae02SJeenu Viswambharan 3001298ae02SJeenu Viswambharan /* 3011298ae02SJeenu Viswambharan * The format of 'power_level' is implementation-defined, but 0 must 3021298ae02SJeenu Viswambharan * mean a CPU. We also allow 1 to denote the cluster 3031298ae02SJeenu Viswambharan */ 3041298ae02SJeenu Viswambharan if (power_level != ARM_PWR_LVL0 && power_level != ARM_PWR_LVL1) 3051298ae02SJeenu Viswambharan return PSCI_E_INVALID_PARAMS; 3061298ae02SJeenu Viswambharan 3071298ae02SJeenu Viswambharan /* 3081298ae02SJeenu Viswambharan * Read the status of the given MPDIR from FVP power controller. The 3091298ae02SJeenu Viswambharan * power controller only gives us on/off status, so map that to expected 3101298ae02SJeenu Viswambharan * return values of the PSCI call 3111298ae02SJeenu Viswambharan */ 3121298ae02SJeenu Viswambharan psysr = fvp_pwrc_read_psysr(target_cpu); 3131298ae02SJeenu Viswambharan if (psysr == PSYSR_INVALID) 3141298ae02SJeenu Viswambharan return PSCI_E_INVALID_PARAMS; 3151298ae02SJeenu Viswambharan 3161298ae02SJeenu Viswambharan switch (power_level) { 3171298ae02SJeenu Viswambharan case ARM_PWR_LVL0: 3181298ae02SJeenu Viswambharan ret = (psysr & PSYSR_AFF_L0) ? HW_ON : HW_OFF; 3191298ae02SJeenu Viswambharan break; 3201298ae02SJeenu Viswambharan case ARM_PWR_LVL1: 3211298ae02SJeenu Viswambharan ret = (psysr & PSYSR_AFF_L1) ? HW_ON : HW_OFF; 3221298ae02SJeenu Viswambharan break; 3231298ae02SJeenu Viswambharan default: 3241298ae02SJeenu Viswambharan assert(0); 3251298ae02SJeenu Viswambharan } 3261298ae02SJeenu Viswambharan 3271298ae02SJeenu Viswambharan return ret; 3281298ae02SJeenu Viswambharan } 3291298ae02SJeenu Viswambharan 3303fc4124cSDan Handley /******************************************************************************* 331785fb92bSSoby Mathew * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard 332785fb92bSSoby Mathew * platform layer will take care of registering the handlers with PSCI. 3333fc4124cSDan Handley ******************************************************************************/ 3345486a965SSoby Mathew plat_psci_ops_t plat_arm_psci_pm_ops = { 33538dce70fSSoby Mathew .cpu_standby = fvp_cpu_standby, 33638dce70fSSoby Mathew .pwr_domain_on = fvp_pwr_domain_on, 33738dce70fSSoby Mathew .pwr_domain_off = fvp_pwr_domain_off, 33838dce70fSSoby Mathew .pwr_domain_suspend = fvp_pwr_domain_suspend, 33938dce70fSSoby Mathew .pwr_domain_on_finish = fvp_pwr_domain_on_finish, 34038dce70fSSoby Mathew .pwr_domain_suspend_finish = fvp_pwr_domain_suspend_finish, 3413fc4124cSDan Handley .system_off = fvp_system_off, 3423fc4124cSDan Handley .system_reset = fvp_system_reset, 343f9e858b1SSoby Mathew .validate_power_state = arm_validate_power_state, 3441298ae02SJeenu Viswambharan .validate_ns_entrypoint = arm_validate_ns_entrypoint, 3451298ae02SJeenu Viswambharan .get_node_hw_state = fvp_node_hw_state 3463fc4124cSDan Handley }; 347