1 /* 2 * Copyright (c) 2013-2016, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <arch_helpers.h> 8 #include <arm_config.h> 9 #include <assert.h> 10 #include <debug.h> 11 #include <errno.h> 12 #include <mmio.h> 13 #include <plat_arm.h> 14 #include <platform.h> 15 #include <psci.h> 16 #include <v2m_def.h> 17 #include "drivers/pwrc/fvp_pwrc.h" 18 #include "fvp_def.h" 19 #include "fvp_private.h" 20 21 22 #if ARM_RECOM_STATE_ID_ENC 23 /* 24 * The table storing the valid idle power states. Ensure that the 25 * array entries are populated in ascending order of state-id to 26 * enable us to use binary search during power state validation. 27 * The table must be terminated by a NULL entry. 28 */ 29 const unsigned int arm_pm_idle_states[] = { 30 /* State-id - 0x01 */ 31 arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_RET, 32 ARM_PWR_LVL0, PSTATE_TYPE_STANDBY), 33 /* State-id - 0x02 */ 34 arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_OFF, 35 ARM_PWR_LVL0, PSTATE_TYPE_POWERDOWN), 36 /* State-id - 0x22 */ 37 arm_make_pwrstate_lvl1(ARM_LOCAL_STATE_OFF, ARM_LOCAL_STATE_OFF, 38 ARM_PWR_LVL1, PSTATE_TYPE_POWERDOWN), 39 0, 40 }; 41 #endif 42 43 /******************************************************************************* 44 * Function which implements the common FVP specific operations to power down a 45 * cluster in response to a CPU_OFF or CPU_SUSPEND request. 46 ******************************************************************************/ 47 static void fvp_cluster_pwrdwn_common(void) 48 { 49 uint64_t mpidr = read_mpidr_el1(); 50 51 #if ENABLE_SPE_FOR_LOWER_ELS 52 /* 53 * On power down we need to disable statistical profiling extensions 54 * before exiting coherency. 55 */ 56 arm_disable_spe(); 57 #endif 58 59 /* Disable coherency if this cluster is to be turned off */ 60 fvp_interconnect_disable(); 61 62 /* Program the power controller to turn the cluster off */ 63 fvp_pwrc_write_pcoffr(mpidr); 64 } 65 66 static void fvp_power_domain_on_finish_common(const psci_power_state_t *target_state) 67 { 68 unsigned long mpidr; 69 70 assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == 71 ARM_LOCAL_STATE_OFF); 72 73 /* Get the mpidr for this cpu */ 74 mpidr = read_mpidr_el1(); 75 76 /* Perform the common cluster specific operations */ 77 if (target_state->pwr_domain_state[ARM_PWR_LVL1] == 78 ARM_LOCAL_STATE_OFF) { 79 /* 80 * This CPU might have woken up whilst the cluster was 81 * attempting to power down. In this case the FVP power 82 * controller will have a pending cluster power off request 83 * which needs to be cleared by writing to the PPONR register. 84 * This prevents the power controller from interpreting a 85 * subsequent entry of this cpu into a simple wfi as a power 86 * down request. 87 */ 88 fvp_pwrc_write_pponr(mpidr); 89 90 /* Enable coherency if this cluster was off */ 91 fvp_interconnect_enable(); 92 } 93 94 /* 95 * Clear PWKUPR.WEN bit to ensure interrupts do not interfere 96 * with a cpu power down unless the bit is set again 97 */ 98 fvp_pwrc_clr_wen(mpidr); 99 } 100 101 102 /******************************************************************************* 103 * FVP handler called when a CPU is about to enter standby. 104 ******************************************************************************/ 105 void fvp_cpu_standby(plat_local_state_t cpu_state) 106 { 107 108 assert(cpu_state == ARM_LOCAL_STATE_RET); 109 110 /* 111 * Enter standby state 112 * dsb is good practice before using wfi to enter low power states 113 */ 114 dsb(); 115 wfi(); 116 } 117 118 /******************************************************************************* 119 * FVP handler called when a power domain is about to be turned on. The 120 * mpidr determines the CPU to be turned on. 121 ******************************************************************************/ 122 int fvp_pwr_domain_on(u_register_t mpidr) 123 { 124 int rc = PSCI_E_SUCCESS; 125 unsigned int psysr; 126 127 /* 128 * Ensure that we do not cancel an inflight power off request for the 129 * target cpu. That would leave it in a zombie wfi. Wait for it to power 130 * off and then program the power controller to turn that CPU on. 131 */ 132 do { 133 psysr = fvp_pwrc_read_psysr(mpidr); 134 } while (psysr & PSYSR_AFF_L0); 135 136 fvp_pwrc_write_pponr(mpidr); 137 return rc; 138 } 139 140 /******************************************************************************* 141 * FVP handler called when a power domain is about to be turned off. The 142 * target_state encodes the power state that each level should transition to. 143 ******************************************************************************/ 144 void fvp_pwr_domain_off(const psci_power_state_t *target_state) 145 { 146 assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == 147 ARM_LOCAL_STATE_OFF); 148 149 /* 150 * If execution reaches this stage then this power domain will be 151 * suspended. Perform at least the cpu specific actions followed 152 * by the cluster specific operations if applicable. 153 */ 154 155 /* Prevent interrupts from spuriously waking up this cpu */ 156 plat_arm_gic_cpuif_disable(); 157 158 /* Turn redistributor off */ 159 plat_arm_gic_redistif_off(); 160 161 /* Program the power controller to power off this cpu. */ 162 fvp_pwrc_write_ppoffr(read_mpidr_el1()); 163 164 if (target_state->pwr_domain_state[ARM_PWR_LVL1] == 165 ARM_LOCAL_STATE_OFF) 166 fvp_cluster_pwrdwn_common(); 167 168 } 169 170 /******************************************************************************* 171 * FVP handler called when a power domain is about to be suspended. The 172 * target_state encodes the power state that each level should transition to. 173 ******************************************************************************/ 174 void fvp_pwr_domain_suspend(const psci_power_state_t *target_state) 175 { 176 unsigned long mpidr; 177 178 /* 179 * FVP has retention only at cpu level. Just return 180 * as nothing is to be done for retention. 181 */ 182 if (target_state->pwr_domain_state[ARM_PWR_LVL0] == 183 ARM_LOCAL_STATE_RET) 184 return; 185 186 assert(target_state->pwr_domain_state[ARM_PWR_LVL0] == 187 ARM_LOCAL_STATE_OFF); 188 189 /* Get the mpidr for this cpu */ 190 mpidr = read_mpidr_el1(); 191 192 /* Program the power controller to enable wakeup interrupts. */ 193 fvp_pwrc_set_wen(mpidr); 194 195 /* Prevent interrupts from spuriously waking up this cpu */ 196 plat_arm_gic_cpuif_disable(); 197 198 /* 199 * The Redistributor is not powered off as it can potentially prevent 200 * wake up events reaching the CPUIF and/or might lead to losing 201 * register context. 202 */ 203 204 /* Program the power controller to power off this cpu. */ 205 fvp_pwrc_write_ppoffr(read_mpidr_el1()); 206 207 /* Perform the common cluster specific operations */ 208 if (target_state->pwr_domain_state[ARM_PWR_LVL1] == 209 ARM_LOCAL_STATE_OFF) 210 fvp_cluster_pwrdwn_common(); 211 } 212 213 /******************************************************************************* 214 * FVP handler called when a power domain has just been powered on after 215 * being turned off earlier. The target_state encodes the low power state that 216 * each level has woken up from. 217 ******************************************************************************/ 218 void fvp_pwr_domain_on_finish(const psci_power_state_t *target_state) 219 { 220 fvp_power_domain_on_finish_common(target_state); 221 222 /* Enable the gic cpu interface */ 223 plat_arm_gic_pcpu_init(); 224 225 /* Program the gic per-cpu distributor or re-distributor interface */ 226 plat_arm_gic_cpuif_enable(); 227 } 228 229 /******************************************************************************* 230 * FVP handler called when a power domain has just been powered on after 231 * having been suspended earlier. The target_state encodes the low power state 232 * that each level has woken up from. 233 * TODO: At the moment we reuse the on finisher and reinitialize the secure 234 * context. Need to implement a separate suspend finisher. 235 ******************************************************************************/ 236 void fvp_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 237 { 238 /* 239 * Nothing to be done on waking up from retention from CPU level. 240 */ 241 if (target_state->pwr_domain_state[ARM_PWR_LVL0] == 242 ARM_LOCAL_STATE_RET) 243 return; 244 245 fvp_power_domain_on_finish_common(target_state); 246 247 /* Enable the gic cpu interface */ 248 plat_arm_gic_cpuif_enable(); 249 } 250 251 /******************************************************************************* 252 * FVP handlers to shutdown/reboot the system 253 ******************************************************************************/ 254 static void __dead2 fvp_system_off(void) 255 { 256 /* Write the System Configuration Control Register */ 257 mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL, 258 V2M_CFGCTRL_START | 259 V2M_CFGCTRL_RW | 260 V2M_CFGCTRL_FUNC(V2M_FUNC_SHUTDOWN)); 261 wfi(); 262 ERROR("FVP System Off: operation not handled.\n"); 263 panic(); 264 } 265 266 static void __dead2 fvp_system_reset(void) 267 { 268 /* Write the System Configuration Control Register */ 269 mmio_write_32(V2M_SYSREGS_BASE + V2M_SYS_CFGCTRL, 270 V2M_CFGCTRL_START | 271 V2M_CFGCTRL_RW | 272 V2M_CFGCTRL_FUNC(V2M_FUNC_REBOOT)); 273 wfi(); 274 ERROR("FVP System Reset: operation not handled.\n"); 275 panic(); 276 } 277 278 static int fvp_node_hw_state(u_register_t target_cpu, 279 unsigned int power_level) 280 { 281 unsigned int psysr; 282 int ret; 283 284 /* 285 * The format of 'power_level' is implementation-defined, but 0 must 286 * mean a CPU. We also allow 1 to denote the cluster 287 */ 288 if (power_level != ARM_PWR_LVL0 && power_level != ARM_PWR_LVL1) 289 return PSCI_E_INVALID_PARAMS; 290 291 /* 292 * Read the status of the given MPDIR from FVP power controller. The 293 * power controller only gives us on/off status, so map that to expected 294 * return values of the PSCI call 295 */ 296 psysr = fvp_pwrc_read_psysr(target_cpu); 297 if (psysr == PSYSR_INVALID) 298 return PSCI_E_INVALID_PARAMS; 299 300 switch (power_level) { 301 case ARM_PWR_LVL0: 302 ret = (psysr & PSYSR_AFF_L0) ? HW_ON : HW_OFF; 303 break; 304 case ARM_PWR_LVL1: 305 ret = (psysr & PSYSR_AFF_L1) ? HW_ON : HW_OFF; 306 break; 307 } 308 309 return ret; 310 } 311 312 /******************************************************************************* 313 * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard 314 * platform layer will take care of registering the handlers with PSCI. 315 ******************************************************************************/ 316 plat_psci_ops_t plat_arm_psci_pm_ops = { 317 .cpu_standby = fvp_cpu_standby, 318 .pwr_domain_on = fvp_pwr_domain_on, 319 .pwr_domain_off = fvp_pwr_domain_off, 320 .pwr_domain_suspend = fvp_pwr_domain_suspend, 321 .pwr_domain_on_finish = fvp_pwr_domain_on_finish, 322 .pwr_domain_suspend_finish = fvp_pwr_domain_suspend_finish, 323 .system_off = fvp_system_off, 324 .system_reset = fvp_system_reset, 325 .validate_power_state = arm_validate_power_state, 326 .validate_ns_entrypoint = arm_validate_ns_entrypoint, 327 .get_node_hw_state = fvp_node_hw_state 328 }; 329