1 /* 2 * Copyright (c) 2015-2017, ARM Limited and Contributors. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions are met: 6 * 7 * Redistributions of source code must retain the above copyright notice, this 8 * list of conditions and the following disclaimer. 9 * 10 * Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 14 * Neither the name of ARM nor the names of its contributors may be used 15 * to endorse or promote products derived from this software without specific 16 * prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 28 * POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <arch_helpers.h> 32 #include <assert.h> 33 #include <cassert.h> 34 #include <css_pm.h> 35 #include <debug.h> 36 #include <errno.h> 37 #include <plat_arm.h> 38 #include <platform.h> 39 #include <platform_def.h> 40 #include "../drivers/scp/css_scp.h" 41 42 /* Allow CSS platforms to override `plat_arm_psci_pm_ops` */ 43 #pragma weak plat_arm_psci_pm_ops 44 45 #if ARM_RECOM_STATE_ID_ENC 46 /* 47 * The table storing the valid idle power states. Ensure that the 48 * array entries are populated in ascending order of state-id to 49 * enable us to use binary search during power state validation. 50 * The table must be terminated by a NULL entry. 51 */ 52 const unsigned int arm_pm_idle_states[] = { 53 /* State-id - 0x001 */ 54 arm_make_pwrstate_lvl2(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_RUN, 55 ARM_LOCAL_STATE_RET, ARM_PWR_LVL0, PSTATE_TYPE_STANDBY), 56 /* State-id - 0x002 */ 57 arm_make_pwrstate_lvl2(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_RUN, 58 ARM_LOCAL_STATE_OFF, ARM_PWR_LVL0, PSTATE_TYPE_POWERDOWN), 59 /* State-id - 0x022 */ 60 arm_make_pwrstate_lvl2(ARM_LOCAL_STATE_RUN, ARM_LOCAL_STATE_OFF, 61 ARM_LOCAL_STATE_OFF, ARM_PWR_LVL1, PSTATE_TYPE_POWERDOWN), 62 #if PLAT_MAX_PWR_LVL > ARM_PWR_LVL1 63 /* State-id - 0x222 */ 64 arm_make_pwrstate_lvl2(ARM_LOCAL_STATE_OFF, ARM_LOCAL_STATE_OFF, 65 ARM_LOCAL_STATE_OFF, ARM_PWR_LVL2, PSTATE_TYPE_POWERDOWN), 66 #endif 67 0, 68 }; 69 #endif /* __ARM_RECOM_STATE_ID_ENC__ */ 70 71 /* 72 * All the power management helpers in this file assume at least cluster power 73 * level is supported. 74 */ 75 CASSERT(PLAT_MAX_PWR_LVL >= ARM_PWR_LVL1, 76 assert_max_pwr_lvl_supported_mismatch); 77 78 /* 79 * Ensure that the PLAT_MAX_PWR_LVL is not greater than CSS_SYSTEM_PWR_DMN_LVL 80 * assumed by the CSS layer. 81 */ 82 CASSERT(PLAT_MAX_PWR_LVL <= CSS_SYSTEM_PWR_DMN_LVL, 83 assert_max_pwr_lvl_higher_than_css_sys_lvl); 84 85 /******************************************************************************* 86 * Handler called when a power domain is about to be turned on. The 87 * level and mpidr determine the affinity instance. 88 ******************************************************************************/ 89 int css_pwr_domain_on(u_register_t mpidr) 90 { 91 css_scp_on(mpidr); 92 93 return PSCI_E_SUCCESS; 94 } 95 96 static void css_pwr_domain_on_finisher_common( 97 const psci_power_state_t *target_state) 98 { 99 assert(CSS_CORE_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF); 100 101 /* 102 * Perform the common cluster specific operations i.e enable coherency 103 * if this cluster was off. 104 */ 105 if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) 106 plat_arm_interconnect_enter_coherency(); 107 } 108 109 /******************************************************************************* 110 * Handler called when a power level has just been powered on after 111 * being turned off earlier. The target_state encodes the low power state that 112 * each level has woken up from. This handler would never be invoked with 113 * the system power domain uninitialized as either the primary would have taken 114 * care of it as part of cold boot or the first core awakened from system 115 * suspend would have already initialized it. 116 ******************************************************************************/ 117 void css_pwr_domain_on_finish(const psci_power_state_t *target_state) 118 { 119 /* Assert that the system power domain need not be initialized */ 120 assert(CSS_SYSTEM_PWR_STATE(target_state) == ARM_LOCAL_STATE_RUN); 121 122 css_pwr_domain_on_finisher_common(target_state); 123 124 /* Program the gic per-cpu distributor or re-distributor interface */ 125 plat_arm_gic_pcpu_init(); 126 127 /* Enable the gic cpu interface */ 128 plat_arm_gic_cpuif_enable(); 129 } 130 131 /******************************************************************************* 132 * Common function called while turning a cpu off or suspending it. It is called 133 * from css_off() or css_suspend() when these functions in turn are called for 134 * power domain at the highest power level which will be powered down. It 135 * performs the actions common to the OFF and SUSPEND calls. 136 ******************************************************************************/ 137 static void css_power_down_common(const psci_power_state_t *target_state) 138 { 139 /* Prevent interrupts from spuriously waking up this cpu */ 140 plat_arm_gic_cpuif_disable(); 141 142 /* Cluster is to be turned off, so disable coherency */ 143 if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) 144 plat_arm_interconnect_exit_coherency(); 145 } 146 147 /******************************************************************************* 148 * Handler called when a power domain is about to be turned off. The 149 * target_state encodes the power state that each level should transition to. 150 ******************************************************************************/ 151 void css_pwr_domain_off(const psci_power_state_t *target_state) 152 { 153 assert(CSS_CORE_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF); 154 css_power_down_common(target_state); 155 css_scp_off(target_state); 156 } 157 158 /******************************************************************************* 159 * Handler called when a power domain is about to be suspended. The 160 * target_state encodes the power state that each level should transition to. 161 ******************************************************************************/ 162 void css_pwr_domain_suspend(const psci_power_state_t *target_state) 163 { 164 /* 165 * CSS currently supports retention only at cpu level. Just return 166 * as nothing is to be done for retention. 167 */ 168 if (CSS_CORE_PWR_STATE(target_state) == ARM_LOCAL_STATE_RET) 169 return; 170 171 assert(CSS_CORE_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF); 172 css_power_down_common(target_state); 173 css_scp_suspend(target_state); 174 } 175 176 /******************************************************************************* 177 * Handler called when a power domain has just been powered on after 178 * having been suspended earlier. The target_state encodes the low power state 179 * that each level has woken up from. 180 * TODO: At the moment we reuse the on finisher and reinitialize the secure 181 * context. Need to implement a separate suspend finisher. 182 ******************************************************************************/ 183 void css_pwr_domain_suspend_finish( 184 const psci_power_state_t *target_state) 185 { 186 /* Return as nothing is to be done on waking up from retention. */ 187 if (CSS_CORE_PWR_STATE(target_state) == ARM_LOCAL_STATE_RET) 188 return; 189 190 /* Perform system domain restore if woken up from system suspend */ 191 if (CSS_SYSTEM_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) 192 arm_system_pwr_domain_resume(); 193 else 194 /* Enable the gic cpu interface */ 195 plat_arm_gic_cpuif_enable(); 196 197 css_pwr_domain_on_finisher_common(target_state); 198 } 199 200 /******************************************************************************* 201 * Handlers to shutdown/reboot the system 202 ******************************************************************************/ 203 void __dead2 css_system_off(void) 204 { 205 css_scp_sys_shutdown(); 206 } 207 208 void __dead2 css_system_reset(void) 209 { 210 css_scp_sys_reboot(); 211 } 212 213 /******************************************************************************* 214 * Handler called when the CPU power domain is about to enter standby. 215 ******************************************************************************/ 216 void css_cpu_standby(plat_local_state_t cpu_state) 217 { 218 unsigned int scr; 219 220 assert(cpu_state == ARM_LOCAL_STATE_RET); 221 222 scr = read_scr_el3(); 223 /* 224 * Enable the Non secure interrupt to wake the CPU. 225 * In GICv3 affinity routing mode, the non secure group1 interrupts use 226 * the PhysicalFIQ at EL3 whereas in GICv2, it uses the PhysicalIRQ. 227 * Enabling both the bits works for both GICv2 mode and GICv3 affinity 228 * routing mode. 229 */ 230 write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT); 231 isb(); 232 dsb(); 233 wfi(); 234 235 /* 236 * Restore SCR to the original value, synchronisation of scr_el3 is 237 * done by eret while el3_exit to save some execution cycles. 238 */ 239 write_scr_el3(scr); 240 } 241 242 /******************************************************************************* 243 * Handler called to return the 'req_state' for system suspend. 244 ******************************************************************************/ 245 void css_get_sys_suspend_power_state(psci_power_state_t *req_state) 246 { 247 unsigned int i; 248 249 /* 250 * System Suspend is supported only if the system power domain node 251 * is implemented. 252 */ 253 assert(PLAT_MAX_PWR_LVL == CSS_SYSTEM_PWR_DMN_LVL); 254 255 for (i = ARM_PWR_LVL0; i <= PLAT_MAX_PWR_LVL; i++) 256 req_state->pwr_domain_state[i] = ARM_LOCAL_STATE_OFF; 257 } 258 259 /******************************************************************************* 260 * Handler to query CPU/cluster power states from SCP 261 ******************************************************************************/ 262 int css_node_hw_state(u_register_t mpidr, unsigned int power_level) 263 { 264 return css_scp_get_power_state(mpidr, power_level); 265 } 266 267 /* 268 * The system power domain suspend is only supported only via 269 * PSCI SYSTEM_SUSPEND API. PSCI CPU_SUSPEND request to system power domain 270 * will be downgraded to the lower level. 271 */ 272 static int css_validate_power_state(unsigned int power_state, 273 psci_power_state_t *req_state) 274 { 275 int rc; 276 rc = arm_validate_power_state(power_state, req_state); 277 278 /* 279 * Ensure that the system power domain level is never suspended 280 * via PSCI CPU SUSPEND API. Currently system suspend is only 281 * supported via PSCI SYSTEM SUSPEND API. 282 */ 283 req_state->pwr_domain_state[CSS_SYSTEM_PWR_DMN_LVL] = ARM_LOCAL_STATE_RUN; 284 return rc; 285 } 286 287 /* 288 * Custom `translate_power_state_by_mpidr` handler for CSS. Unlike in the 289 * `css_validate_power_state`, we do not downgrade the system power 290 * domain level request in `power_state` as it will be used to query the 291 * PSCI_STAT_COUNT/RESIDENCY at the system power domain level. 292 */ 293 static int css_translate_power_state_by_mpidr(u_register_t mpidr, 294 unsigned int power_state, 295 psci_power_state_t *output_state) 296 { 297 return arm_validate_power_state(power_state, output_state); 298 } 299 300 /******************************************************************************* 301 * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard 302 * platform will take care of registering the handlers with PSCI. 303 ******************************************************************************/ 304 plat_psci_ops_t plat_arm_psci_pm_ops = { 305 .pwr_domain_on = css_pwr_domain_on, 306 .pwr_domain_on_finish = css_pwr_domain_on_finish, 307 .pwr_domain_off = css_pwr_domain_off, 308 .cpu_standby = css_cpu_standby, 309 .pwr_domain_suspend = css_pwr_domain_suspend, 310 .pwr_domain_suspend_finish = css_pwr_domain_suspend_finish, 311 .system_off = css_system_off, 312 .system_reset = css_system_reset, 313 .validate_power_state = css_validate_power_state, 314 .validate_ns_entrypoint = arm_validate_ns_entrypoint, 315 .translate_power_state_by_mpidr = css_translate_power_state_by_mpidr, 316 .get_node_hw_state = css_node_hw_state, 317 .get_sys_suspend_power_state = css_get_sys_suspend_power_state 318 }; 319