1 /* 2 * Copyright (c) 2015, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <arch_helpers.h> 8 #include <assert.h> 9 #include <errno.h> 10 #include <platform.h> 11 #include <psci.h> 12 13 /* 14 * The platform hooks exported by the platform using the earlier version of 15 * platform interface 16 */ 17 const plat_pm_ops_t *pm_ops; 18 19 /* 20 * The hooks exported by the compatibility layer 21 */ 22 static plat_psci_ops_t compat_psci_ops; 23 24 /* 25 * The secure entry point to be used on warm reset. 26 */ 27 static unsigned long secure_entrypoint; 28 29 /* 30 * This array stores the 'power_state' requests of each CPU during 31 * CPU_SUSPEND and SYSTEM_SUSPEND to support querying of state-ID 32 * by the platform. 33 */ 34 unsigned int psci_power_state_compat[PLATFORM_CORE_COUNT]; 35 36 /******************************************************************************* 37 * The PSCI compatibility helper to parse the power state and populate the 38 * 'pwr_domain_state' for each power level. It is assumed that, when in 39 * compatibility mode, the PSCI generic layer need to know only whether the 40 * affinity level will be OFF or in RETENTION and if the platform supports 41 * multiple power down and retention states, it will be taken care within 42 * the platform layer. 43 ******************************************************************************/ 44 static int parse_power_state(unsigned int power_state, 45 psci_power_state_t *req_state) 46 { 47 int i; 48 int pstate = psci_get_pstate_type(power_state); 49 int aff_lvl = psci_get_pstate_pwrlvl(power_state); 50 51 if (aff_lvl > PLATFORM_MAX_AFFLVL) 52 return PSCI_E_INVALID_PARAMS; 53 54 /* Sanity check the requested state */ 55 if (pstate == PSTATE_TYPE_STANDBY) { 56 /* 57 * Set the CPU local state as retention and ignore the higher 58 * levels. This allows the generic PSCI layer to invoke 59 * plat_psci_ops 'cpu_standby' hook and the compatibility 60 * layer invokes the 'affinst_standby' handler with the 61 * correct power_state parameter thus preserving the correct 62 * behavior. 63 */ 64 req_state->pwr_domain_state[0] = 65 PLAT_MAX_RET_STATE; 66 } else { 67 for (i = 0; i <= aff_lvl; i++) 68 req_state->pwr_domain_state[i] = 69 PLAT_MAX_OFF_STATE; 70 } 71 72 return PSCI_E_SUCCESS; 73 } 74 75 /******************************************************************************* 76 * The PSCI compatibility helper to set the 'power_state' in 77 * psci_power_state_compat[] at index corresponding to the current core. 78 ******************************************************************************/ 79 static void set_psci_power_state_compat(unsigned int power_state) 80 { 81 unsigned int my_core_pos = plat_my_core_pos(); 82 83 psci_power_state_compat[my_core_pos] = power_state; 84 flush_dcache_range((uintptr_t) &psci_power_state_compat[my_core_pos], 85 sizeof(psci_power_state_compat[my_core_pos])); 86 } 87 88 /******************************************************************************* 89 * The PSCI compatibility helper for plat_pm_ops_t 'validate_power_state' 90 * hook. 91 ******************************************************************************/ 92 static int validate_power_state_compat(unsigned int power_state, 93 psci_power_state_t *req_state) 94 { 95 int rc; 96 assert(req_state); 97 98 if (pm_ops->validate_power_state) { 99 rc = pm_ops->validate_power_state(power_state); 100 if (rc != PSCI_E_SUCCESS) 101 return rc; 102 } 103 104 /* Store the 'power_state' parameter for the current CPU. */ 105 set_psci_power_state_compat(power_state); 106 107 return parse_power_state(power_state, req_state); 108 } 109 110 /******************************************************************************* 111 * The PSCI compatibility helper for plat_pm_ops_t 112 * 'get_sys_suspend_power_state' hook. 113 ******************************************************************************/ 114 void get_sys_suspend_power_state_compat(psci_power_state_t *req_state) 115 { 116 unsigned int power_state; 117 assert(req_state); 118 119 power_state = pm_ops->get_sys_suspend_power_state(); 120 121 /* Store the 'power_state' parameter for the current CPU. */ 122 set_psci_power_state_compat(power_state); 123 124 if (parse_power_state(power_state, req_state) != PSCI_E_SUCCESS) 125 assert(0); 126 } 127 128 /******************************************************************************* 129 * The PSCI compatibility helper for plat_pm_ops_t 'validate_ns_entrypoint' 130 * hook. 131 ******************************************************************************/ 132 static int validate_ns_entrypoint_compat(uintptr_t ns_entrypoint) 133 { 134 return pm_ops->validate_ns_entrypoint(ns_entrypoint); 135 } 136 137 /******************************************************************************* 138 * The PSCI compatibility helper for plat_pm_ops_t 'affinst_standby' hook. 139 ******************************************************************************/ 140 static void cpu_standby_compat(plat_local_state_t cpu_state) 141 { 142 unsigned int powerstate = psci_get_suspend_powerstate(); 143 144 assert(powerstate != PSCI_INVALID_DATA); 145 146 pm_ops->affinst_standby(powerstate); 147 } 148 149 /******************************************************************************* 150 * The PSCI compatibility helper for plat_pm_ops_t 'affinst_on' hook. 151 ******************************************************************************/ 152 static int pwr_domain_on_compat(u_register_t mpidr) 153 { 154 int level, rc; 155 156 /* 157 * The new PSCI framework does not hold the locks for higher level 158 * power domain nodes when this hook is invoked. Hence figuring out the 159 * target state of the parent power domains does not make much sense. 160 * Hence we hard-code the state as PSCI_STATE_OFF for all the levels. 161 * We expect the platform to perform the necessary CPU_ON operations 162 * when the 'affinst_on' is invoked only for level 0. 163 */ 164 for (level = PLATFORM_MAX_AFFLVL; level >= 0; level--) { 165 rc = pm_ops->affinst_on((unsigned long)mpidr, secure_entrypoint, 166 level, PSCI_STATE_OFF); 167 if (rc != PSCI_E_SUCCESS) 168 break; 169 } 170 171 return rc; 172 } 173 174 /******************************************************************************* 175 * The PSCI compatibility helper for plat_pm_ops_t 'affinst_off' hook. 176 ******************************************************************************/ 177 static void pwr_domain_off_compat(const psci_power_state_t *target_state) 178 { 179 int level; 180 unsigned int plat_state; 181 182 for (level = 0; level <= PLATFORM_MAX_AFFLVL; level++) { 183 plat_state = (is_local_state_run( 184 target_state->pwr_domain_state[level]) ? 185 PSCI_STATE_ON : PSCI_STATE_OFF); 186 pm_ops->affinst_off(level, plat_state); 187 } 188 } 189 190 /******************************************************************************* 191 * The PSCI compatibility helper for plat_pm_ops_t 'affinst_suspend' hook. 192 ******************************************************************************/ 193 static void pwr_domain_suspend_compat(const psci_power_state_t *target_state) 194 { 195 int level; 196 unsigned int plat_state; 197 198 for (level = 0; level <= psci_get_suspend_afflvl(); level++) { 199 plat_state = (is_local_state_run( 200 target_state->pwr_domain_state[level]) ? 201 PSCI_STATE_ON : PSCI_STATE_OFF); 202 pm_ops->affinst_suspend(secure_entrypoint, level, plat_state); 203 } 204 } 205 206 /******************************************************************************* 207 * The PSCI compatibility helper for plat_pm_ops_t 'affinst_on_finish' 208 * hook. 209 ******************************************************************************/ 210 static void pwr_domain_on_finish_compat(const psci_power_state_t *target_state) 211 { 212 int level; 213 unsigned int plat_state; 214 215 for (level = PLATFORM_MAX_AFFLVL; level >= 0; level--) { 216 plat_state = (is_local_state_run( 217 target_state->pwr_domain_state[level]) ? 218 PSCI_STATE_ON : PSCI_STATE_OFF); 219 pm_ops->affinst_on_finish(level, plat_state); 220 } 221 } 222 223 /******************************************************************************* 224 * The PSCI compatibility helper for plat_pm_ops_t 225 * 'affinst_suspend_finish' hook. 226 ******************************************************************************/ 227 static void pwr_domain_suspend_finish_compat( 228 const psci_power_state_t *target_state) 229 { 230 int level; 231 unsigned int plat_state; 232 233 for (level = psci_get_suspend_afflvl(); level >= 0; level--) { 234 plat_state = (is_local_state_run( 235 target_state->pwr_domain_state[level]) ? 236 PSCI_STATE_ON : PSCI_STATE_OFF); 237 pm_ops->affinst_suspend_finish(level, plat_state); 238 } 239 } 240 241 /******************************************************************************* 242 * The PSCI compatibility helper for plat_pm_ops_t 'system_off' hook. 243 ******************************************************************************/ 244 static void __dead2 system_off_compat(void) 245 { 246 pm_ops->system_off(); 247 } 248 249 /******************************************************************************* 250 * The PSCI compatibility helper for plat_pm_ops_t 'system_reset' hook. 251 ******************************************************************************/ 252 static void __dead2 system_reset_compat(void) 253 { 254 pm_ops->system_reset(); 255 } 256 257 /******************************************************************************* 258 * Export the compatibility compat_psci_ops. The assumption made is that the 259 * power domains correspond to affinity instances on the platform. 260 ******************************************************************************/ 261 int plat_setup_psci_ops(uintptr_t sec_entrypoint, 262 const plat_psci_ops_t **psci_ops) 263 { 264 platform_setup_pm(&pm_ops); 265 266 secure_entrypoint = (unsigned long) sec_entrypoint; 267 268 /* 269 * It is compulsory for the platform ports using the new porting 270 * interface to export a hook to validate the power state parameter 271 */ 272 compat_psci_ops.validate_power_state = validate_power_state_compat; 273 274 /* 275 * Populate the compatibility plat_psci_ops_t hooks if available 276 */ 277 if (pm_ops->validate_ns_entrypoint) 278 compat_psci_ops.validate_ns_entrypoint = 279 validate_ns_entrypoint_compat; 280 281 if (pm_ops->affinst_standby) 282 compat_psci_ops.cpu_standby = cpu_standby_compat; 283 284 if (pm_ops->affinst_on) 285 compat_psci_ops.pwr_domain_on = pwr_domain_on_compat; 286 287 if (pm_ops->affinst_off) 288 compat_psci_ops.pwr_domain_off = pwr_domain_off_compat; 289 290 if (pm_ops->affinst_suspend) 291 compat_psci_ops.pwr_domain_suspend = pwr_domain_suspend_compat; 292 293 if (pm_ops->affinst_on_finish) 294 compat_psci_ops.pwr_domain_on_finish = 295 pwr_domain_on_finish_compat; 296 297 if (pm_ops->affinst_suspend_finish) 298 compat_psci_ops.pwr_domain_suspend_finish = 299 pwr_domain_suspend_finish_compat; 300 301 if (pm_ops->system_off) 302 compat_psci_ops.system_off = system_off_compat; 303 304 if (pm_ops->system_reset) 305 compat_psci_ops.system_reset = system_reset_compat; 306 307 if (pm_ops->get_sys_suspend_power_state) 308 compat_psci_ops.get_sys_suspend_power_state = 309 get_sys_suspend_power_state_compat; 310 311 *psci_ops = &compat_psci_ops; 312 return 0; 313 } 314