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 ******************************************************************************/
socfpga_cpu_standby(plat_local_state_t cpu_state)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 ******************************************************************************/
socfpga_pwr_domain_on(u_register_t mpidr)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 ******************************************************************************/
socfpga_pwr_domain_off(const psci_power_state_t * target_state)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 ******************************************************************************/
socfpga_pwr_domain_suspend(const psci_power_state_t * target_state)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 ******************************************************************************/
socfpga_pwr_domain_on_finish(const psci_power_state_t * target_state)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 ******************************************************************************/
socfpga_pwr_domain_suspend_finish(const psci_power_state_t * target_state)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 ******************************************************************************/
socfpga_system_off(void)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
socfpga_system_reset(void)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
socfpga_system_reset2(int is_vendor,int reset_type,u_register_t cookie)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
socfpga_validate_power_state(unsigned int power_state,psci_power_state_t * req_state)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
socfpga_validate_ns_entrypoint(unsigned long ns_entrypoint)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
socfpga_get_sys_suspend_power_state(psci_power_state_t * req_state)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 ******************************************************************************/
plat_setup_psci_ops(uintptr_t sec_entrypoint,const struct plat_psci_ops ** psci_ops)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