xref: /rk3399_ARM-atf/plat/xilinx/versal/plat_psci.c (revision 06f3c7058c42a9f1a9f7df75ea2de71a000855e8)
1 /*
2  * Copyright (c) 2018-2021, Arm Limited and Contributors. All rights reserved.
3  * Copyright (c) 2022-2025, 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 
25 #define SEC_ENTRY_ADDRESS_MASK		0xFFFFFFFFUL
26 #define RESUME_ADDR_SET			0x1UL
27 
28 static uintptr_t versal_sec_entry;
29 
30 static int32_t versal_pwr_domain_on(u_register_t mpidr)
31 {
32 	int32_t cpu_id = plat_core_pos_by_mpidr(mpidr);
33 	const struct pm_proc *proc;
34 	int32_t ret = PSCI_E_INTERN_FAIL;
35 
36 	VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr);
37 
38 	if (cpu_id == -1) {
39 		goto exit_label;
40 	}
41 
42 	proc = pm_get_proc((uint32_t)cpu_id);
43 	if (proc == NULL) {
44 		goto exit_label;
45 	}
46 
47 	/* Send request to PMC to wake up selected ACPU core */
48 	(void)pm_req_wakeup(proc->node_id,
49 			    (uint32_t)((versal_sec_entry & SEC_ENTRY_ADDRESS_MASK) |
50 			    RESUME_ADDR_SET), versal_sec_entry >> 32, 0, SECURE_FLAG);
51 
52 	/* Clear power down request */
53 	pm_client_wakeup(proc);
54 
55 	ret = PSCI_E_SUCCESS;
56 
57 exit_label:
58 	return ret;
59 }
60 
61 /**
62  * versal_pwr_domain_suspend() - This function sends request to PMC to suspend
63  *                               core.
64  * @target_state: Targated state.
65  *
66  */
67 static void versal_pwr_domain_suspend(const psci_power_state_t *target_state)
68 {
69 	uint32_t state;
70 	uint32_t cpu_id = plat_my_core_pos();
71 	const struct pm_proc *proc = pm_get_proc(cpu_id);
72 
73 	if (proc == NULL) {
74 		return;
75 	}
76 
77 	for (size_t i = 0U; i <= PLAT_MAX_PWR_LVL; i++) {
78 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
79 			__func__, i, target_state->pwr_domain_state[i]);
80 	}
81 
82 	plat_versal_gic_cpuif_disable();
83 
84 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
85 		plat_versal_gic_save();
86 	}
87 
88 	state = (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) ?
89 		PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE;
90 
91 	/* Send request to PMC to suspend this core */
92 	(void)pm_self_suspend(proc->node_id, MAX_LATENCY, state, versal_sec_entry,
93 			      SECURE_FLAG);
94 
95 	/* APU is to be turned off */
96 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
97 		/* disable coherency */
98 		plat_arm_interconnect_exit_coherency();
99 	}
100 }
101 
102 /**
103  * versal_pwr_domain_suspend_finish() - This function performs actions to finish
104  *                                      suspend procedure.
105  * @target_state: Targated state.
106  *
107  */
108 static void versal_pwr_domain_suspend_finish(
109 					const psci_power_state_t *target_state)
110 {
111 	uint32_t cpu_id = plat_my_core_pos();
112 	const struct pm_proc *proc = pm_get_proc(cpu_id);
113 
114 	if (proc == NULL) {
115 		return;
116 	}
117 
118 	for (size_t i = 0U; i <= PLAT_MAX_PWR_LVL; i++) {
119 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
120 			__func__, i, target_state->pwr_domain_state[i]);
121 	}
122 
123 	/* Clear the APU power control register for this cpu */
124 	pm_client_wakeup(proc);
125 
126 	/* enable coherency */
127 	plat_arm_interconnect_enter_coherency();
128 
129 	/* APU was turned off, so restore GIC context */
130 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
131 		plat_versal_gic_resume();
132 	}
133 
134 	plat_versal_gic_cpuif_enable();
135 }
136 
137 static void versal_pwr_domain_on_finish(const psci_power_state_t *target_state)
138 {
139 	/*
140 	 * Typecasting to void to intentionally retain the variable and avoid
141 	 * MISRA violation for unused parameters. This may be used in the
142 	 * future if specific action is required based on CPU power state.
143 	 */
144 	(void)target_state;
145 
146 	/* Enable the gic cpu interface */
147 	plat_versal_gic_pcpu_init();
148 
149 	/* Program the gic per-cpu distributor or re-distributor interface */
150 	plat_versal_gic_cpuif_enable();
151 }
152 
153 /**
154  * versal_system_off() - This function sends the system off request to firmware.
155  *                       This function does not return.
156  *
157  */
158 static void __dead2 versal_system_off(void)
159 {
160 	/* Send the power down request to the PMC */
161 	(void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_SHUTDOWN,
162 				 pm_get_shutdown_scope(), SECURE_FLAG);
163 
164 	while (true) {
165 		wfi();
166 	}
167 }
168 
169 /**
170  * versal_system_reset() - This function sends the reset request to firmware
171  *                         for the system to reset.  This function does not
172  *			   return.
173  *
174  */
175 static void __dead2 versal_system_reset(void)
176 {
177 	uint32_t ret, timeout = 10000U;
178 
179 	request_cpu_pwrdwn();
180 
181 	/*
182 	 * Send the system reset request to the firmware if power down request
183 	 * is not received from firmware.
184 	 */
185 	if (!pwrdwn_req_received) {
186 		(void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_RESET,
187 					 pm_get_shutdown_scope(), SECURE_FLAG);
188 
189 		/*
190 		 * Wait for system shutdown request completed and idle callback
191 		 * not received.
192 		 */
193 		do {
194 			ret = ipi_mb_enquire_status(primary_proc->ipi->local_ipi_id,
195 						    primary_proc->ipi->remote_ipi_id);
196 			udelay(100);
197 			timeout--;
198 		} while ((ret != IPI_MB_STATUS_RECV_PENDING) && (timeout > 0U));
199 	}
200 
201 	(void)psci_cpu_off();
202 
203 	while (true) {
204 		wfi();
205 	}
206 }
207 
208 static int32_t versal_validate_ns_entrypoint(uint64_t ns_entrypoint)
209 {
210 	int32_t ret = PSCI_E_SUCCESS;
211 
212 	if (((ns_entrypoint >= PLAT_DDR_LOWMEM_MAX) && (ns_entrypoint <= PLAT_DDR_HIGHMEM_MAX)) ||
213 		((ns_entrypoint >= BL31_BASE) && (ns_entrypoint <= BL31_LIMIT))) {
214 		ret = PSCI_E_INVALID_ADDRESS;
215 	}
216 
217 	return ret;
218 }
219 
220 /**
221  * versal_pwr_domain_off() - This function performs actions to turn off core.
222  * @target_state: Targated state.
223  *
224  */
225 static void versal_pwr_domain_off(const psci_power_state_t *target_state)
226 {
227 	uint32_t ret, fw_api_version, version_type[RET_PAYLOAD_ARG_CNT] = {0U};
228 	uint32_t cpu_id = plat_my_core_pos();
229 	const struct pm_proc *proc = pm_get_proc(cpu_id);
230 
231 	if (proc == NULL) {
232 		return;
233 	}
234 
235 	for (size_t i = 0U; i <= PLAT_MAX_PWR_LVL; i++) {
236 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
237 			__func__, i, target_state->pwr_domain_state[i]);
238 	}
239 
240 	/* Prevent interrupts from spuriously waking up this cpu */
241 	plat_versal_gic_cpuif_disable();
242 
243 	/*
244 	 * Send request to PMC to power down the appropriate APU CPU
245 	 * core.
246 	 * According to PSCI specification, CPU_off function does not
247 	 * have resume address and CPU core can only be woken up
248 	 * invoking CPU_on function, during which resume address will
249 	 * be set.
250 	 */
251 	ret = (uint32_t)pm_feature_check((uint32_t)PM_SELF_SUSPEND,
252 					 &version_type[0], SECURE_FLAG);
253 	if (ret == (uint32_t)PM_RET_SUCCESS) {
254 		fw_api_version = version_type[0] & 0xFFFFU;
255 		if (fw_api_version >= 3U) {
256 			(void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_OFF, 0,
257 					      SECURE_FLAG);
258 		} else {
259 			(void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0,
260 					      SECURE_FLAG);
261 		}
262 	}
263 }
264 
265 /**
266  * versal_validate_power_state() - This function ensures that the power state
267  *                                 parameter in request is valid.
268  * @power_state: Power state of core.
269  * @req_state: Requested state.
270  *
271  * Return: Returns status, either success or reason.
272  *
273  */
274 static int32_t versal_validate_power_state(uint32_t power_state,
275 				       psci_power_state_t *req_state)
276 {
277 	int32_t ret = PSCI_E_SUCCESS;
278 	VERBOSE("%s: power_state: 0x%x\n", __func__, power_state);
279 
280 	uint32_t pstate = psci_get_pstate_type(power_state);
281 
282 	assert(req_state != NULL);
283 
284 	/* Sanity check the requested state */
285 	if (pstate == PSTATE_TYPE_STANDBY) {
286 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE;
287 	} else {
288 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE;
289 	}
290 
291 	/* We expect the 'state id' to be zero */
292 	if (psci_get_pstate_id(power_state) != 0U) {
293 		ret = PSCI_E_INVALID_PARAMS;
294 	}
295 
296 	return ret;
297 }
298 
299 /**
300  * versal_get_sys_suspend_power_state() - Get power state for system suspend.
301  * @req_state: Requested state.
302  *
303  */
304 static void versal_get_sys_suspend_power_state(psci_power_state_t *req_state)
305 {
306 	req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE;
307 	req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE;
308 }
309 
310 static const struct plat_psci_ops versal_nopmc_psci_ops = {
311 	.pwr_domain_on			= versal_pwr_domain_on,
312 	.pwr_domain_off			= versal_pwr_domain_off,
313 	.pwr_domain_on_finish		= versal_pwr_domain_on_finish,
314 	.pwr_domain_suspend		= versal_pwr_domain_suspend,
315 	.pwr_domain_suspend_finish	= versal_pwr_domain_suspend_finish,
316 	.system_off			= versal_system_off,
317 	.system_reset			= versal_system_reset,
318 	.validate_ns_entrypoint		= versal_validate_ns_entrypoint,
319 	.validate_power_state		= versal_validate_power_state,
320 	.get_sys_suspend_power_state	= versal_get_sys_suspend_power_state,
321 };
322 
323 /*******************************************************************************
324  * Export the platform specific power ops.
325  ******************************************************************************/
326 int plat_setup_psci_ops(uintptr_t sec_entrypoint,
327 			const struct plat_psci_ops **psci_ops)
328 {
329 	versal_sec_entry = sec_entrypoint;
330 
331 	*psci_ops = &versal_nopmc_psci_ops;
332 
333 	return 0;
334 }
335