1 /* 2 * Copyright (c) 2019-2023, ARM Limited and Contributors. All rights reserved. 3 * Copyright (c) 2019-2023, Intel Corporation. All rights reserved. 4 * Copyright (c) 2024-2025, Altera Corporation. All rights reserved. 5 * 6 * SPDX-License-Identifier: BSD-3-Clause 7 */ 8 9 #include <arch_helpers.h> 10 #include <common/debug.h> 11 12 #ifndef GICV3_SUPPORT_GIC600 13 #include <drivers/arm/gicv2.h> 14 #else 15 #include <drivers/arm/gicv3.h> 16 #endif 17 #include <lib/mmio.h> 18 #include <lib/psci/psci.h> 19 #include <plat/common/platform.h> 20 #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 21 #include "agilex5_cache.h" 22 #include "agilex5_power_manager.h" 23 #endif 24 #include "ccu/ncore_ccu.h" 25 #include "socfpga_mailbox.h" 26 #include "socfpga_plat_def.h" 27 #include "socfpga_private.h" 28 #include "socfpga_reset_manager.h" 29 #include "socfpga_sip_svc.h" 30 #include "socfpga_system_manager.h" 31 32 #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 33 void socfpga_wakeup_secondary_cpu(unsigned int cpu_id); 34 extern void plat_secondary_cold_boot_setup(void); 35 #endif 36 37 /******************************************************************************* 38 * plat handler called when a CPU is about to enter standby. 39 ******************************************************************************/ 40 void socfpga_cpu_standby(plat_local_state_t cpu_state) 41 { 42 /* 43 * Enter standby state 44 * dsb is good practice before using wfi to enter low power states 45 */ 46 VERBOSE("%s: cpu_state: 0x%x\n", __func__, cpu_state); 47 dsb(); 48 wfi(); 49 } 50 51 /******************************************************************************* 52 * plat handler called when a power domain is about to be turned on. The 53 * mpidr determines the CPU to be turned on. 54 ******************************************************************************/ 55 int socfpga_pwr_domain_on(u_register_t mpidr) 56 { 57 unsigned int cpu_id = plat_core_pos_by_mpidr(mpidr); 58 #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 59 unsigned int pch_cpu = 0x0; 60 /* TODO: Add in CPU FUSE from SDM */ 61 #else 62 uint32_t psci_boot = 0x00; 63 64 VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr); 65 #endif 66 67 if (cpu_id == -1) 68 return PSCI_E_INTERN_FAIL; 69 70 #if PLATFORM_MODEL != PLAT_SOCFPGA_AGILEX5 71 if (cpu_id == 0x00) { 72 psci_boot = mmio_read_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_8)); 73 psci_boot |= 0x80000; /* bit 19 */ 74 mmio_write_32(SOCFPGA_SYSMGR(BOOT_SCRATCH_COLD_8), psci_boot); 75 } 76 77 mmio_write_64(PLAT_CPUID_RELEASE, cpu_id); 78 #endif 79 80 /* release core reset */ 81 #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 82 pch_cpu = mmio_read_32(AGX5_PWRMGR(MPU_PCHCTLR)) & 83 AGX5_PWRMGR_CPU_POWER_STATE_MASK; 84 85 /* Check if the CPU ON Request is post POR */ 86 if ((AGX5_PWRMGR_MPU_TRIGGER_PCH_CPU(1 << cpu_id) & (pch_cpu)) != 0) 87 bl31_plat_reset_secondary_cpu(cpu_id); 88 89 bl31_plat_set_secondary_cpu_entrypoint(cpu_id); 90 #else 91 mmio_setbits_32(SOCFPGA_RSTMGR(MPUMODRST), 1 << cpu_id); 92 mmio_write_64(PLAT_CPUID_RELEASE, cpu_id); 93 #endif 94 95 return PSCI_E_SUCCESS; 96 } 97 98 /******************************************************************************* 99 * plat handler called when a power domain is about to be turned off. The 100 * target_state encodes the power state that each level should transition to. 101 ******************************************************************************/ 102 void socfpga_pwr_domain_off(const psci_power_state_t *target_state) 103 { 104 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 105 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 106 __func__, i, target_state->pwr_domain_state[i]); 107 108 /* Prevent interrupts from spuriously waking up this cpu */ 109 #ifdef GICV3_SUPPORT_GIC600 110 gicv3_cpuif_disable(plat_my_core_pos()); 111 #else 112 gicv2_cpuif_disable(); 113 #endif 114 115 } 116 117 /******************************************************************************* 118 * plat handler called when a power domain is about to be suspended. The 119 * target_state encodes the power state that each level should transition to. 120 ******************************************************************************/ 121 void socfpga_pwr_domain_suspend(const psci_power_state_t *target_state) 122 { 123 #if PLATFORM_MODEL != PLAT_SOCFPGA_AGILEX5 124 unsigned int cpu_id = plat_my_core_pos(); 125 #endif 126 127 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 128 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 129 __func__, i, target_state->pwr_domain_state[i]); 130 131 #if PLATFORM_MODEL != PLAT_SOCFPGA_AGILEX5 132 /* assert core reset */ 133 mmio_setbits_32(SOCFPGA_RSTMGR(MPUMODRST), 1 << cpu_id); 134 #endif 135 } 136 137 /******************************************************************************* 138 * plat handler called when a power domain has just been powered on after 139 * being turned off earlier. The target_state encodes the low power state that 140 * each level has woken up from. 141 ******************************************************************************/ 142 void socfpga_pwr_domain_on_finish(const psci_power_state_t *target_state) 143 { 144 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 145 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 146 __func__, i, target_state->pwr_domain_state[i]); 147 148 /* Enable the gic cpu interface */ 149 #ifdef GICV3_SUPPORT_GIC600 150 gicv3_rdistif_init(plat_my_core_pos()); 151 gicv3_cpuif_enable(plat_my_core_pos()); 152 #else 153 /* Program the gic per-cpu distributor or re-distributor interface */ 154 gicv2_pcpu_distif_init(); 155 gicv2_set_pe_target_mask(plat_my_core_pos()); 156 157 /* Enable the gic cpu interface */ 158 gicv2_cpuif_enable(); 159 #endif 160 } 161 162 /******************************************************************************* 163 * plat handler called when a power domain has just been powered on after 164 * having been suspended earlier. The target_state encodes the low power state 165 * that each level has woken up from. 166 * TODO: At the moment we reuse the on finisher and reinitialize the secure 167 * context. Need to implement a separate suspend finisher. 168 ******************************************************************************/ 169 void socfpga_pwr_domain_suspend_finish(const psci_power_state_t *target_state) 170 { 171 #if PLATFORM_MODEL != PLAT_SOCFPGA_AGILEX5 172 unsigned int cpu_id = plat_my_core_pos(); 173 #endif 174 175 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) 176 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n", 177 __func__, i, target_state->pwr_domain_state[i]); 178 179 #if PLATFORM_MODEL != PLAT_SOCFPGA_AGILEX5 180 /* release core reset */ 181 mmio_clrbits_32(SOCFPGA_RSTMGR(MPUMODRST), 1 << cpu_id); 182 #endif 183 } 184 185 /******************************************************************************* 186 * plat handlers to shutdown/reboot the system 187 ******************************************************************************/ 188 static void __dead2 socfpga_system_off(void) 189 { 190 wfi(); 191 ERROR("System Off: operation not handled.\n"); 192 panic(); 193 } 194 195 extern uint64_t intel_rsu_update_address; 196 197 static void __dead2 socfpga_system_reset(void) 198 { 199 uint32_t addr_buf[2]; 200 201 memcpy_s(addr_buf, sizeof(intel_rsu_update_address), 202 &intel_rsu_update_address, sizeof(intel_rsu_update_address)); 203 204 if (intel_rsu_update_address) { 205 mailbox_rsu_update(addr_buf); 206 } else { 207 #if CACHE_FLUSH 208 /* ATF Flush and Invalidate Cache */ 209 dcsw_op_all(DCCISW); 210 invalidate_cache_low_el(); 211 #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 212 flush_l3_dcache(); 213 #endif 214 #endif 215 mailbox_reset_cold(); 216 } 217 218 while (1) 219 wfi(); 220 } 221 222 static int socfpga_system_reset2(int is_vendor, int reset_type, 223 u_register_t cookie) 224 { 225 226 #if CACHE_FLUSH 227 /* 228 * ATF Flush and Invalidate Cache due to hardware limitation 229 * of auto Flush and Invalidate Cache. 230 */ 231 dcsw_op_all(DCCISW); 232 invalidate_cache_low_el(); 233 #endif 234 235 /* Set warm reset request bit before issuing the command to SDM. */ 236 mmio_clrsetbits_32(L2_RESET_DONE_REG, BS_REG_MAGIC_KEYS_MASK, 237 L2_RESET_DONE_STATUS); 238 239 #if PLATFORM_MODEL == PLAT_SOCFPGA_AGILEX5 240 mailbox_reset_warm(reset_type); 241 #else 242 if (cold_reset_for_ecc_dbe()) { 243 mailbox_reset_cold(); 244 } 245 #endif 246 247 /* disable cpuif */ 248 #ifdef GICV3_SUPPORT_GIC600 249 gicv3_cpuif_disable(plat_my_core_pos()); 250 #else 251 gicv2_cpuif_disable(); 252 #endif 253 254 /* Increase timeout */ 255 mmio_write_32(SOCFPGA_RSTMGR(HDSKTIMEOUT), 0xffffff); 256 257 /* Enable handshakes */ 258 mmio_setbits_32(SOCFPGA_RSTMGR(HDSKEN), RSTMGR_HDSKEN_SET); 259 260 #if PLATFORM_MODEL != PLAT_SOCFPGA_AGILEX5 261 /* Reset L2 module */ 262 mmio_setbits_32(SOCFPGA_RSTMGR(COLDMODRST), 0x100); 263 #endif 264 265 while (1) 266 wfi(); 267 268 /* Should not reach here */ 269 return 0; 270 } 271 272 int socfpga_validate_power_state(unsigned int power_state, 273 psci_power_state_t *req_state) 274 { 275 VERBOSE("%s: power_state: 0x%x\n", __func__, power_state); 276 277 return PSCI_E_SUCCESS; 278 } 279 280 int socfpga_validate_ns_entrypoint(unsigned long ns_entrypoint) 281 { 282 VERBOSE("%s: ns_entrypoint: 0x%lx\n", __func__, ns_entrypoint); 283 return PSCI_E_SUCCESS; 284 } 285 286 void socfpga_get_sys_suspend_power_state(psci_power_state_t *req_state) 287 { 288 req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE; 289 req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE; 290 } 291 292 /******************************************************************************* 293 * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard 294 * platform layer will take care of registering the handlers with PSCI. 295 ******************************************************************************/ 296 const plat_psci_ops_t socfpga_psci_pm_ops = { 297 .cpu_standby = socfpga_cpu_standby, 298 .pwr_domain_on = socfpga_pwr_domain_on, 299 .pwr_domain_off = socfpga_pwr_domain_off, 300 .pwr_domain_suspend = socfpga_pwr_domain_suspend, 301 .pwr_domain_on_finish = socfpga_pwr_domain_on_finish, 302 .pwr_domain_suspend_finish = socfpga_pwr_domain_suspend_finish, 303 .system_off = socfpga_system_off, 304 .system_reset = socfpga_system_reset, 305 .system_reset2 = socfpga_system_reset2, 306 .validate_power_state = socfpga_validate_power_state, 307 .validate_ns_entrypoint = socfpga_validate_ns_entrypoint, 308 .get_sys_suspend_power_state = socfpga_get_sys_suspend_power_state 309 }; 310 311 /******************************************************************************* 312 * Export the platform specific power ops. 313 ******************************************************************************/ 314 int plat_setup_psci_ops(uintptr_t sec_entrypoint, 315 const struct plat_psci_ops **psci_ops) 316 { 317 /* Save warm boot entrypoint.*/ 318 mmio_write_64(PLAT_SEC_ENTRY, sec_entrypoint); 319 *psci_ops = &socfpga_psci_pm_ops; 320 321 return 0; 322 } 323