xref: /rk3399_ARM-atf/plat/xilinx/versal/plat_psci.c (revision ee656609c8e9292f65ad82100f4ca190b7882a05)
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 	/* Enable the gic cpu interface */
140 	plat_versal_gic_pcpu_init();
141 
142 	/* Program the gic per-cpu distributor or re-distributor interface */
143 	plat_versal_gic_cpuif_enable();
144 }
145 
146 /**
147  * versal_system_off() - This function sends the system off request to firmware.
148  *                       This function does not return.
149  *
150  */
151 static void __dead2 versal_system_off(void)
152 {
153 	/* Send the power down request to the PMC */
154 	(void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_SHUTDOWN,
155 				 pm_get_shutdown_scope(), SECURE_FLAG);
156 
157 	while (true) {
158 		wfi();
159 	}
160 }
161 
162 /**
163  * versal_system_reset() - This function sends the reset request to firmware
164  *                         for the system to reset.  This function does not
165  *			   return.
166  *
167  */
168 static void __dead2 versal_system_reset(void)
169 {
170 	uint32_t ret, timeout = 10000U;
171 
172 	request_cpu_pwrdwn();
173 
174 	/*
175 	 * Send the system reset request to the firmware if power down request
176 	 * is not received from firmware.
177 	 */
178 	if (!pwrdwn_req_received) {
179 		(void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_RESET,
180 					 pm_get_shutdown_scope(), SECURE_FLAG);
181 
182 		/*
183 		 * Wait for system shutdown request completed and idle callback
184 		 * not received.
185 		 */
186 		do {
187 			ret = ipi_mb_enquire_status(primary_proc->ipi->local_ipi_id,
188 						    primary_proc->ipi->remote_ipi_id);
189 			udelay(100);
190 			timeout--;
191 		} while ((ret != IPI_MB_STATUS_RECV_PENDING) && (timeout > 0U));
192 	}
193 
194 	(void)psci_cpu_off();
195 
196 	while (true) {
197 		wfi();
198 	}
199 }
200 
201 static int32_t versal_validate_ns_entrypoint(uint64_t ns_entrypoint)
202 {
203 	int32_t ret = PSCI_E_SUCCESS;
204 
205 	if (((ns_entrypoint >= PLAT_DDR_LOWMEM_MAX) && (ns_entrypoint <= PLAT_DDR_HIGHMEM_MAX)) ||
206 		((ns_entrypoint >= BL31_BASE) && (ns_entrypoint <= BL31_LIMIT))) {
207 		ret = PSCI_E_INVALID_ADDRESS;
208 	}
209 
210 	return ret;
211 }
212 
213 /**
214  * versal_pwr_domain_off() - This function performs actions to turn off core.
215  * @target_state: Targated state.
216  *
217  */
218 static void versal_pwr_domain_off(const psci_power_state_t *target_state)
219 {
220 	uint32_t ret, fw_api_version, version_type[RET_PAYLOAD_ARG_CNT] = {0U};
221 	uint32_t cpu_id = plat_my_core_pos();
222 	const struct pm_proc *proc = pm_get_proc(cpu_id);
223 
224 	if (proc == NULL) {
225 		return;
226 	}
227 
228 	for (size_t i = 0U; i <= PLAT_MAX_PWR_LVL; i++) {
229 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
230 			__func__, i, target_state->pwr_domain_state[i]);
231 	}
232 
233 	/* Prevent interrupts from spuriously waking up this cpu */
234 	plat_versal_gic_cpuif_disable();
235 
236 	/*
237 	 * Send request to PMC to power down the appropriate APU CPU
238 	 * core.
239 	 * According to PSCI specification, CPU_off function does not
240 	 * have resume address and CPU core can only be woken up
241 	 * invoking CPU_on function, during which resume address will
242 	 * be set.
243 	 */
244 	ret = (uint32_t)pm_feature_check((uint32_t)PM_SELF_SUSPEND,
245 					 &version_type[0], SECURE_FLAG);
246 	if (ret == (uint32_t)PM_RET_SUCCESS) {
247 		fw_api_version = version_type[0] & 0xFFFFU;
248 		if (fw_api_version >= 3U) {
249 			(void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_OFF, 0,
250 					      SECURE_FLAG);
251 		} else {
252 			(void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0,
253 					      SECURE_FLAG);
254 		}
255 	}
256 }
257 
258 /**
259  * versal_validate_power_state() - This function ensures that the power state
260  *                                 parameter in request is valid.
261  * @power_state: Power state of core.
262  * @req_state: Requested state.
263  *
264  * Return: Returns status, either success or reason.
265  *
266  */
267 static int32_t versal_validate_power_state(uint32_t power_state,
268 				       psci_power_state_t *req_state)
269 {
270 	int32_t ret = PSCI_E_SUCCESS;
271 	VERBOSE("%s: power_state: 0x%x\n", __func__, power_state);
272 
273 	uint32_t pstate = psci_get_pstate_type(power_state);
274 
275 	assert(req_state != NULL);
276 
277 	/* Sanity check the requested state */
278 	if (pstate == PSTATE_TYPE_STANDBY) {
279 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE;
280 	} else {
281 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE;
282 	}
283 
284 	/* We expect the 'state id' to be zero */
285 	if (psci_get_pstate_id(power_state) != 0U) {
286 		ret = PSCI_E_INVALID_PARAMS;
287 	}
288 
289 	return ret;
290 }
291 
292 /**
293  * versal_get_sys_suspend_power_state() - Get power state for system suspend.
294  * @req_state: Requested state.
295  *
296  */
297 static void versal_get_sys_suspend_power_state(psci_power_state_t *req_state)
298 {
299 	req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE;
300 	req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE;
301 }
302 
303 static const struct plat_psci_ops versal_nopmc_psci_ops = {
304 	.pwr_domain_on			= versal_pwr_domain_on,
305 	.pwr_domain_off			= versal_pwr_domain_off,
306 	.pwr_domain_on_finish		= versal_pwr_domain_on_finish,
307 	.pwr_domain_suspend		= versal_pwr_domain_suspend,
308 	.pwr_domain_suspend_finish	= versal_pwr_domain_suspend_finish,
309 	.system_off			= versal_system_off,
310 	.system_reset			= versal_system_reset,
311 	.validate_ns_entrypoint		= versal_validate_ns_entrypoint,
312 	.validate_power_state		= versal_validate_power_state,
313 	.get_sys_suspend_power_state	= versal_get_sys_suspend_power_state,
314 };
315 
316 /*******************************************************************************
317  * Export the platform specific power ops.
318  ******************************************************************************/
319 int plat_setup_psci_ops(uintptr_t sec_entrypoint,
320 			const struct plat_psci_ops **psci_ops)
321 {
322 	versal_sec_entry = sec_entrypoint;
323 
324 	*psci_ops = &versal_nopmc_psci_ops;
325 
326 	return 0;
327 }
328