1 /* 2 * Copyright (c) 2023, ARM Limited and Contributors. All rights reserved. 3 * 4 * SPDX-License-Identifier: BSD-3-Clause 5 */ 6 7 #include <assert.h> 8 #include <platform_def.h> 9 #include <sigi_def.h> 10 11 #include <arch_helpers.h> 12 #include <common/debug.h> 13 #include <lib/psci/psci.h> 14 #include <lib/el3_runtime/cpu_data.h> 15 #include <plat/common/platform.h> 16 #include <drivers/arm/gicv3.h> 17 #include <drivers/arm/gic_common.h> 18 #include <lib/el3_runtime/context_mgmt.h> 19 #include <lib/mmio.h> 20 21 struct cpu_offset_map { 22 uint64_t offset; 23 u_register_t cpu_idx; 24 } cpu_map [] = { 25 {CPU_CL0_C0_0, 0x000}, 26 {CPU_CL0_C1_0, 0x100}, 27 {CPU_CL0_C2_0, 0x200}, 28 {CPU_CL0_C3_0, 0x300}, 29 {CPU_CL1_C0_0, 0x10000}, 30 {CPU_CL1_C1_0, 0x10100}, 31 }; 32 33 /* 34 * The secure entry point to be used on warm reset. 35 */ 36 static unsigned long secure_entrypoint; 37 38 /* Make composite power state parameter till power level 0 */ 39 #if PSCI_EXTENDED_STATE_ID 40 41 #define sigi_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \ 42 (((lvl0_state) << PSTATE_ID_SHIFT) | \ 43 ((type) << PSTATE_TYPE_SHIFT)) 44 #else 45 #define sigi_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type) \ 46 (((lvl0_state) << PSTATE_ID_SHIFT) | \ 47 ((pwr_lvl) << PSTATE_PWR_LVL_SHIFT) | \ 48 ((type) << PSTATE_TYPE_SHIFT)) 49 #endif /* PSCI_EXTENDED_STATE_ID */ 50 51 52 #define sigi_make_pwrstate_lvl1(lvl1_state, lvl0_state, pwr_lvl, type) \ 53 (((lvl1_state) << SIGI_LOCAL_PSTATE_WIDTH) | \ 54 sigi_make_pwrstate_lvl0(lvl0_state, pwr_lvl, type)) 55 56 57 /* 58 * The table storing the valid idle power states. Ensure that the 59 * array entries are populated in ascending order of state-id to 60 * enable us to use binary search during power state validation. 61 * The table must be terminated by a NULL entry. 62 */ 63 static const unsigned int sigi_pm_idle_states[] = { 64 /* State-id - 0x01 */ 65 sigi_make_pwrstate_lvl1(SIGI_LOCAL_STATE_RUN, SIGI_LOCAL_STATE_RET, 66 MPIDR_AFFLVL0, PSTATE_TYPE_STANDBY), 67 /* State-id - 0x02 */ 68 sigi_make_pwrstate_lvl1(SIGI_LOCAL_STATE_RUN, SIGI_LOCAL_STATE_OFF, 69 MPIDR_AFFLVL0, PSTATE_TYPE_POWERDOWN), 70 /* State-id - 0x22 */ 71 sigi_make_pwrstate_lvl1(SIGI_LOCAL_STATE_OFF, SIGI_LOCAL_STATE_OFF, 72 MPIDR_AFFLVL1, PSTATE_TYPE_POWERDOWN), 73 0, 74 }; 75 76 /** 77 * sigi_validate_power_state() - Platform handler to check if power state is valid 78 * 79 * @power_state: power state prepares to go to. 80 * @req_state: power domain level specific local states 81 * 82 * This function is called by the PSCI implementation during the ``CPU_SUSPEND`` 83 * call to validate the ``power_state`` parameter of the PSCI API and if valid, 84 * populate it in ``req_state`` (second argument) array as power domain level 85 * specific local states. If the ``power_state`` is invalid, the platform must 86 * return PSCI_E_INVALID_PARAMS as error, which is propagated back to the 87 * normal world PSCI client. 88 * 89 * Return: PSCI_E_SUCCESS if success, error code otherwise. 90 */ 91 int sigi_validate_power_state(unsigned int power_state, 92 psci_power_state_t *req_state) 93 { 94 unsigned int state_id; 95 int i; 96 97 assert(req_state); 98 99 /* 100 * Currently we are using a linear search for finding the matching 101 * entry in the idle power state array. This can be made a binary 102 * search if the number of entries justify the additional complexity. 103 */ 104 for (i = 0; !!sigi_pm_idle_states[i]; i++) { 105 if (power_state == sigi_pm_idle_states[i]) 106 break; 107 } 108 109 /* Return error if entry not found in the idle state array */ 110 if (!sigi_pm_idle_states[i]) 111 return PSCI_E_INVALID_PARAMS; 112 113 i = 0; 114 state_id = psci_get_pstate_id(power_state); 115 116 /* Parse the State ID and populate the state info parameter */ 117 while (state_id) { 118 req_state->pwr_domain_state[i++] = state_id & 119 SIGI_LOCAL_PSTATE_MASK; 120 state_id >>= SIGI_LOCAL_PSTATE_WIDTH; 121 } 122 123 return PSCI_E_SUCCESS; 124 } 125 126 /** 127 * sigi_validate_ns_entrypoint() - Platform handler to check ns_entrypoint 128 * 129 * @ns_entrypoint: entrypoint address to check 130 * 131 * This function need to check if the ns_entrypoint is in non-secure world. 132 * 133 * Return: PSCI_E_SUCCESS if success, error code otherwise. 134 */ 135 static int sigi_validate_ns_entrypoint(uintptr_t entrypoint) 136 { 137 /* 138 * Check if the non secure entrypoint lies within the non 139 * secure DRAM. 140 */ 141 if ((entrypoint >= SIGI_INTERLEAVE_DRAM_BASE) && 142 (entrypoint < (SIGI_INTERLEAVE_DRAM_BASE + SIGI_NS_DDR_SIZE))) 143 return PSCI_E_SUCCESS; 144 else if ((entrypoint >= SIGI_NON_INTER_DRAM_BASE) && 145 (entrypoint < (SIGI_NON_INTER_DRAM_BASE + SIGI_NS_DDR_SIZE))) 146 return PSCI_E_SUCCESS; 147 return PSCI_E_INVALID_ADDRESS; 148 } 149 150 /** 151 * sigi_cpu_standby() - Put CPU to standy mode 152 * 153 * @cpu_state: local cpu state 154 * 155 * This function will put cpu into idle state, and will return when cpu wakeup. 156 * 157 * Return: void 158 */ 159 void sigi_cpu_standby(plat_local_state_t cpu_state) 160 { 161 assert(cpu_state == SIGI_LOCAL_STATE_RET); 162 163 /* 164 * Enter standby state 165 * dsb is good practice before using wfi to enter low power states 166 */ 167 dsb(); 168 wfi(); 169 } 170 171 static uint64_t get_offset_addr_by_mpidr(u_register_t mpidr) 172 { 173 int i; 174 175 for (i = 0; i < ARRAY_SIZE(cpu_map); i++) { 176 if (cpu_map[i].cpu_idx == mpidr) 177 return cpu_map[i].offset; 178 } 179 180 return ULONG_MAX; 181 } 182 183 /** 184 * sigi_pwr_domain_on() - Power on a cpu. 185 * 186 * @mpidr: mpidr value for this cpu 187 * 188 * This will be call before cpu power on, need to take action to bring cpu on 189 * 190 * Return: PSCI_E_SUCCESS if success, error code otherwise 191 */ 192 static int sigi_pwr_domain_on(u_register_t mpidr) 193 { 194 int rc = PSCI_E_SUCCESS; 195 unsigned pos = plat_core_pos_by_mpidr(mpidr); 196 uint64_t *hold_base = (uint64_t *)PLAT_SIGI_HOLD_BASE; 197 uint64_t offset = get_offset_addr_by_mpidr(mpidr); 198 199 hold_base[pos] = PLAT_SIGI_HOLD_STATE_GO; 200 sev(); 201 202 mmio_setbits_32(SIGI_PMU_BASE + offset, BIT(12)); 203 204 return rc; 205 } 206 207 /** 208 * sigi_pwr_domain_off() - Power off a cpu. 209 * 210 * @target_state: CPU topological state 211 * 212 * This will be call before cpu power off, need to take action to put cpu off 213 * 214 * Return: void 215 */ 216 static void sigi_pwr_domain_off(const psci_power_state_t *target_state) 217 { 218 sigi_pwr_gic_off(); 219 } 220 221 /** 222 * sigi_pwr_domain_suspend() - Put system into suspend state. 223 * 224 * @target_state: Platform coordinated target local power states 225 * 226 * Perform the platform specific actions to prepare to suspend the calling 227 * CPU and its higher parent power domain levels as indicated by the 228 * ``target_state`` (first argument). It is called by the PSCI ``CPU_SUSPEND`` 229 * API implementation. 230 * 231 * Return: void 232 */ 233 static void sigi_pwr_domain_suspend(const psci_power_state_t *target_state) 234 { 235 assert(0); 236 } 237 238 void __dead2 plat_secondary_cold_boot_setup(void); 239 /** 240 * sigi_pwr_down_wfi() - Powerdown the CPU 241 * 242 * @target_state: Platform coordinated target local power states 243 * 244 * Perform platform specific actions including the ``wfi`` invocation which 245 * allows the CPU to powerdown. 246 * 247 * Return: void 248 */ 249 static void __dead2 sigi_pwr_down_wfi(const psci_power_state_t *target_state) 250 { 251 disable_mmu_el3(); 252 plat_secondary_cold_boot_setup(); 253 } 254 255 /** 256 * sigi_pwr_domain_on_finish() - Platform handler when cpu is powered on. 257 * 258 * @target_state: Platform coordinated target local power states 259 * 260 * This function is called by the PSCI implementation after the calling CPU is 261 * powered on and released from reset in response to an earlier PSCI ``CPU_ON`` 262 * call. It performs the platform-specific setup required to initialize enough 263 * state for this CPU to enter the normal world and also provide secure runtime 264 * firmware services. 265 * 266 * Return: void 267 */ 268 static void sigi_pwr_domain_on_finish(const psci_power_state_t *target_state) 269 { 270 assert(target_state->pwr_domain_state[MPIDR_AFFLVL0] == 271 SIGI_LOCAL_STATE_OFF); 272 273 sigi_pwr_gic_on_finish(); 274 } 275 276 /** 277 * sigi_pwr_domain_suspend_finish() - Platform handler after wakeup. 278 * 279 * @target_state: Platform coordinated target local power states 280 * 281 * Performs the platform-specific setup required to restore the saved state for 282 * this CPU to resume execution in the normal world and also provide secure 283 * runtime firmware services. This function will need to restore Distributor, 284 * Redistributors or ITS context. 285 * 286 * Return: void 287 */ 288 static void sigi_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 289 { 290 assert(0); 291 } 292 293 /** 294 * sigi_system_off() - Platform handler to poweroff the system. 295 * 296 * Performs the platform-specific setup to power off the whole system. 297 * Due to we use fake shutdown for J5, this function just while (1) to wfi(). 298 * 299 * Return: void 300 */ 301 static void __dead2 sigi_system_off(void) 302 { 303 panic(); 304 } 305 306 /** 307 * sigi_system_reset() - Platform handler to reboot the system. 308 * 309 * Performs the platform-specific setup to reboot the whole system. 310 * We need to generated a wdt reset to reset the whole soc. In case of wdt 311 * trigger failed, going to panic(). 312 * 313 * Return: void 314 */ 315 static void __dead2 sigi_system_reset(void) 316 { 317 panic(); 318 } 319 320 /** 321 * sigi_psci_ops - j6 platform psci ops 322 */ 323 static plat_psci_ops_t sigi_psci_ops = { 324 .cpu_standby = sigi_cpu_standby, 325 .pwr_domain_on = sigi_pwr_domain_on, 326 .pwr_domain_off = sigi_pwr_domain_off, 327 .pwr_domain_suspend = sigi_pwr_domain_suspend, 328 .pwr_domain_pwr_down_wfi = sigi_pwr_down_wfi, 329 .pwr_domain_on_finish = sigi_pwr_domain_on_finish, 330 .pwr_domain_suspend_finish = sigi_pwr_domain_suspend_finish, 331 .system_off = sigi_system_off, 332 .system_reset = sigi_system_reset, 333 .validate_power_state = sigi_validate_power_state, 334 .validate_ns_entrypoint = sigi_validate_ns_entrypoint, 335 }; 336 337 /** 338 * plat_setup_psci_ops() - Platform handler to setup psci ops 339 * 340 * @sec_entrypoint: entry to going to after cpu powered on. 341 * @psci_ops: pointer to store psci ops. 342 * 343 * This function need to initialze 344 * 345 * Return: void. 346 */ 347 int plat_setup_psci_ops(uintptr_t sec_entrypoint, 348 const plat_psci_ops_t **psci_ops) 349 { 350 uintptr_t *mailbox = (void *) PLAT_SIGI_TRUSTED_MAILBOX_BASE; 351 352 *mailbox = sec_entrypoint; 353 assert(psci_ops); 354 secure_entrypoint = (unsigned long) sec_entrypoint; 355 *psci_ops = &sigi_psci_ops; 356 357 return 0; 358 } 359