1 /* 2 * Copyright (c) 2022, Xilinx, Inc. All rights reserved. 3 * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved. 4 * 5 * SPDX-License-Identifier: BSD-3-Clause 6 */ 7 8 #include <assert.h> 9 10 #include <common/debug.h> 11 #include <lib/mmio.h> 12 #include <lib/psci/psci.h> 13 #include <plat/arm/common/plat_arm.h> 14 #include <plat/common/platform.h> 15 #include <plat_arm.h> 16 17 #include <drivers/delay_timer.h> 18 #include <plat_private.h> 19 #include "pm_api_sys.h" 20 #include "pm_client.h" 21 #include <pm_common.h> 22 #include "pm_ipi.h" 23 #include "pm_svc_main.h" 24 #include "versal_net_def.h" 25 26 static uintptr_t versal_net_sec_entry; 27 28 static int32_t versal_net_pwr_domain_on(u_register_t mpidr) 29 { 30 uint32_t cpu_id = plat_core_pos_by_mpidr(mpidr); 31 const struct pm_proc *proc; 32 33 VERBOSE("%s: mpidr: 0x%lx, cpuid: %x\n", 34 __func__, mpidr, cpu_id); 35 36 if (cpu_id == -1) { 37 return PSCI_E_INTERN_FAIL; 38 } 39 40 proc = pm_get_proc(cpu_id); 41 if (!proc) { 42 return PSCI_E_INTERN_FAIL; 43 } 44 45 pm_req_wakeup(proc->node_id, (versal_net_sec_entry & 0xFFFFFFFFU) | 0x1U, 46 versal_net_sec_entry >> 32, 0, 0); 47 48 /* Clear power down request */ 49 pm_client_wakeup(proc); 50 51 return PSCI_E_SUCCESS; 52 } 53 54 /** 55 * versal_net_pwr_domain_off() - This function performs actions to turn off 56 * core. 57 * @target_state: Targeted state. 58 * 59 */ 60 static void versal_net_pwr_domain_off(const psci_power_state_t *target_state) 61 { 62 uint32_t ret, fw_api_version, version[PAYLOAD_ARG_CNT] = {0U}; 63 uint32_t cpu_id = plat_my_core_pos(); 64 const struct pm_proc *proc = pm_get_proc(cpu_id); 65 66 if (!proc) { 67 return; 68 } 69 70 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) { 71 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 72 __func__, i, target_state->pwr_domain_state[i]); 73 } 74 75 /* Prevent interrupts from spuriously waking up this cpu */ 76 plat_arm_gic_cpuif_disable(); 77 78 /* 79 * Send request to PMC to power down the appropriate APU CPU 80 * core. 81 * According to PSCI specification, CPU_off function does not 82 * have resume address and CPU core can only be woken up 83 * invoking CPU_on function, during which resume address will 84 * be set. 85 */ 86 ret = pm_feature_check((uint32_t)PM_SELF_SUSPEND, &version[0], SECURE_FLAG); 87 if (ret == PM_RET_SUCCESS) { 88 fw_api_version = version[0] & 0xFFFFU; 89 if (fw_api_version >= 3U) { 90 (void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_OFF, 0, 91 SECURE_FLAG); 92 } else { 93 (void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0, 94 SECURE_FLAG); 95 } 96 } 97 } 98 99 /** 100 * versal_net_system_reset() - This function sends the reset request to firmware 101 * for the system to reset. This function does not 102 * return. 103 * 104 */ 105 static void __dead2 versal_net_system_reset(void) 106 { 107 uint32_t ret, timeout = 10000U; 108 109 request_cpu_pwrdwn(); 110 111 /* 112 * Send the system reset request to the firmware if power down request 113 * is not received from firmware. 114 */ 115 if (!pwrdwn_req_received) { 116 (void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_RESET, 117 pm_get_shutdown_scope(), SECURE_FLAG); 118 119 /* 120 * Wait for system shutdown request completed and idle callback 121 * not received. 122 */ 123 do { 124 ret = ipi_mb_enquire_status(primary_proc->ipi->local_ipi_id, 125 primary_proc->ipi->remote_ipi_id); 126 udelay(100); 127 timeout--; 128 } while ((ret != IPI_MB_STATUS_RECV_PENDING) && (timeout > 0U)); 129 } 130 131 (void)psci_cpu_off(); 132 133 while (1) { 134 wfi(); 135 } 136 } 137 138 /** 139 * versal_net_pwr_domain_suspend() - This function sends request to PMC to suspend 140 * core. 141 * @target_state: Targeted state. 142 * 143 */ 144 static void versal_net_pwr_domain_suspend(const psci_power_state_t *target_state) 145 { 146 uint32_t state; 147 uint32_t cpu_id = plat_my_core_pos(); 148 const struct pm_proc *proc = pm_get_proc(cpu_id); 149 150 if (!proc) { 151 return; 152 } 153 154 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) { 155 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 156 __func__, i, target_state->pwr_domain_state[i]); 157 } 158 159 plat_arm_gic_cpuif_disable(); 160 161 if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { 162 plat_arm_gic_save(); 163 } 164 165 state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ? 166 PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE; 167 168 /* Send request to PMC to suspend this core */ 169 pm_self_suspend(proc->node_id, MAX_LATENCY, state, versal_net_sec_entry, 170 SECURE_FLAG); 171 172 /* TODO: disable coherency */ 173 } 174 175 static void versal_net_pwr_domain_on_finish(const psci_power_state_t *target_state) 176 { 177 (void)target_state; 178 179 /* Enable the gic cpu interface */ 180 plat_arm_gic_pcpu_init(); 181 182 /* Program the gic per-cpu distributor or re-distributor interface */ 183 plat_arm_gic_cpuif_enable(); 184 } 185 186 /** 187 * versal_net_pwr_domain_suspend_finish() - This function performs actions to finish 188 * suspend procedure. 189 * @target_state: Targeted state. 190 * 191 */ 192 static void versal_net_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 193 { 194 uint32_t cpu_id = plat_my_core_pos(); 195 const struct pm_proc *proc = pm_get_proc(cpu_id); 196 197 if (!proc) { 198 return; 199 } 200 201 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 202 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 203 __func__, i, target_state->pwr_domain_state[i]); 204 205 /* Clear the APU power control register for this cpu */ 206 pm_client_wakeup(proc); 207 208 /* TODO: enable coherency */ 209 210 /* APU was turned off, so restore GIC context */ 211 if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) { 212 plat_arm_gic_resume(); 213 } 214 215 plat_arm_gic_cpuif_enable(); 216 } 217 218 /** 219 * versal_net_system_off() - This function sends the system off request 220 * to firmware. This function does not return. 221 * 222 */ 223 static void __dead2 versal_net_system_off(void) 224 { 225 /* Send the power down request to the PMC */ 226 pm_system_shutdown(XPM_SHUTDOWN_TYPE_SHUTDOWN, 227 pm_get_shutdown_scope(), SECURE_FLAG); 228 229 while (1) { 230 wfi(); 231 } 232 } 233 234 /** 235 * versal_net_validate_power_state() - This function ensures that the power state 236 * parameter in request is valid. 237 * @power_state: Power state of core. 238 * @req_state: Requested state. 239 * 240 * Return: Returns status, either PSCI_E_SUCCESS or reason. 241 * 242 */ 243 static int32_t versal_net_validate_power_state(unsigned int power_state, 244 psci_power_state_t *req_state) 245 { 246 VERBOSE("%s: power_state: 0x%x\n", __func__, power_state); 247 248 int32_t pstate = psci_get_pstate_type(power_state); 249 250 assert(req_state); 251 252 /* Sanity check the requested state */ 253 if (pstate == PSTATE_TYPE_STANDBY) { 254 req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE; 255 } else { 256 req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE; 257 } 258 259 /* We expect the 'state id' to be zero */ 260 if (psci_get_pstate_id(power_state)) { 261 return PSCI_E_INVALID_PARAMS; 262 } 263 264 return PSCI_E_SUCCESS; 265 } 266 267 /** 268 * versal_net_get_sys_suspend_power_state() - Get power state for system 269 * suspend. 270 * @req_state: Requested state. 271 * 272 */ 273 static void versal_net_get_sys_suspend_power_state(psci_power_state_t *req_state) 274 { 275 uint64_t i; 276 277 for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) 278 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE; 279 } 280 281 static const struct plat_psci_ops versal_net_nopmc_psci_ops = { 282 .pwr_domain_on = versal_net_pwr_domain_on, 283 .pwr_domain_off = versal_net_pwr_domain_off, 284 .pwr_domain_on_finish = versal_net_pwr_domain_on_finish, 285 .pwr_domain_suspend = versal_net_pwr_domain_suspend, 286 .pwr_domain_suspend_finish = versal_net_pwr_domain_suspend_finish, 287 .system_off = versal_net_system_off, 288 .system_reset = versal_net_system_reset, 289 .validate_power_state = versal_net_validate_power_state, 290 .get_sys_suspend_power_state = versal_net_get_sys_suspend_power_state, 291 }; 292 293 /******************************************************************************* 294 * Export the platform specific power ops. 295 ******************************************************************************/ 296 int32_t plat_setup_psci_ops(uintptr_t sec_entrypoint, 297 const struct plat_psci_ops **psci_ops) 298 { 299 versal_net_sec_entry = sec_entrypoint; 300 301 VERBOSE("Setting up entry point %lx\n", versal_net_sec_entry); 302 303 *psci_ops = &versal_net_nopmc_psci_ops; 304 305 return 0; 306 } 307 308 int32_t sip_svc_setup_init(void) 309 { 310 return pm_setup(); 311 } 312 313 uint64_t smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4, 314 void *cookie, void *handle, uint64_t flags) 315 { 316 return pm_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags); 317 } 318