xref: /rk3399_ARM-atf/plat/intel/soc/common/socfpga_psci.c (revision cbf956ad0b4d62f7f93fd33d975a4d961009d83f)
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