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 } else { 245 /* Store magic number */ 246 mmio_write_32(L2_RESET_DONE_REG, L2_RESET_DONE_STATUS); 247 } 248 #endif 249 250 /* disable cpuif */ 251 #ifdef GICV3_SUPPORT_GIC600 252 gicv3_cpuif_disable(plat_my_core_pos()); 253 #else 254 gicv2_cpuif_disable(); 255 #endif 256 257 /* Increase timeout */ 258 mmio_write_32(SOCFPGA_RSTMGR(HDSKTIMEOUT), 0xffffff); 259 260 /* Enable handshakes */ 261 mmio_setbits_32(SOCFPGA_RSTMGR(HDSKEN), RSTMGR_HDSKEN_SET); 262 263 #if PLATFORM_MODEL != PLAT_SOCFPGA_AGILEX5 264 /* Reset L2 module */ 265 mmio_setbits_32(SOCFPGA_RSTMGR(COLDMODRST), 0x100); 266 #endif 267 268 while (1) 269 wfi(); 270 271 /* Should not reach here */ 272 return 0; 273 } 274 275 int socfpga_validate_power_state(unsigned int power_state, 276 psci_power_state_t *req_state) 277 { 278 VERBOSE("%s: power_state: 0x%x\n", __func__, power_state); 279 280 return PSCI_E_SUCCESS; 281 } 282 283 int socfpga_validate_ns_entrypoint(unsigned long ns_entrypoint) 284 { 285 VERBOSE("%s: ns_entrypoint: 0x%lx\n", __func__, ns_entrypoint); 286 return PSCI_E_SUCCESS; 287 } 288 289 void socfpga_get_sys_suspend_power_state(psci_power_state_t *req_state) 290 { 291 req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE; 292 req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE; 293 } 294 295 /******************************************************************************* 296 * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard 297 * platform layer will take care of registering the handlers with PSCI. 298 ******************************************************************************/ 299 const plat_psci_ops_t socfpga_psci_pm_ops = { 300 .cpu_standby = socfpga_cpu_standby, 301 .pwr_domain_on = socfpga_pwr_domain_on, 302 .pwr_domain_off = socfpga_pwr_domain_off, 303 .pwr_domain_suspend = socfpga_pwr_domain_suspend, 304 .pwr_domain_on_finish = socfpga_pwr_domain_on_finish, 305 .pwr_domain_suspend_finish = socfpga_pwr_domain_suspend_finish, 306 .system_off = socfpga_system_off, 307 .system_reset = socfpga_system_reset, 308 .system_reset2 = socfpga_system_reset2, 309 .validate_power_state = socfpga_validate_power_state, 310 .validate_ns_entrypoint = socfpga_validate_ns_entrypoint, 311 .get_sys_suspend_power_state = socfpga_get_sys_suspend_power_state 312 }; 313 314 /******************************************************************************* 315 * Export the platform specific power ops. 316 ******************************************************************************/ 317 int plat_setup_psci_ops(uintptr_t sec_entrypoint, 318 const struct plat_psci_ops **psci_ops) 319 { 320 /* Save warm boot entrypoint.*/ 321 mmio_write_64(PLAT_SEC_ENTRY, sec_entrypoint); 322 *psci_ops = &socfpga_psci_pm_ops; 323 324 return 0; 325 } 326