xref: /rk3399_ARM-atf/plat/amd/versal2/plat_psci_pm.c (revision b67e984664a8644d6cfd1812cabaa02cf24f09c9)
1 /*
2  * Copyright (c) 2022, Xilinx, Inc. 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 <drivers/delay_timer.h>
12 #include <lib/mmio.h>
13 #include <lib/psci/psci.h>
14 #include <plat/arm/common/plat_arm.h>
15 #include <plat/common/platform.h>
16 #include <plat_arm.h>
17 #include <plat_fdt.h>
18 
19 #include "def.h"
20 #include <ipi.h>
21 #include <plat_private.h>
22 #include "pm_api_sys.h"
23 #include "pm_client.h"
24 #include <pm_common.h>
25 #include "pm_defs.h"
26 #include "pm_svc_main.h"
27 
28 static uintptr_t sec_entry;
29 
30 static int32_t versal2_pwr_domain_on(u_register_t mpidr)
31 {
32 	int32_t cpu_id = plat_core_pos_by_mpidr(mpidr);
33 	int32_t ret = (int32_t) PSCI_E_INTERN_FAIL;
34 	enum pm_ret_status pm_ret;
35 	const struct pm_proc *proc;
36 
37 	if (cpu_id != -1) {
38 		proc = pm_get_proc((uint32_t)cpu_id);
39 		if (proc != NULL) {
40 			pm_ret = pm_req_wakeup(proc->node_id,
41 					       (uint32_t)
42 					       ((sec_entry & 0xFFFFFFFFU) | 0x1U),
43 					       sec_entry >> 32, 0, 0);
44 
45 			if (pm_ret == PM_RET_SUCCESS) {
46 				/* Clear power down request */
47 				pm_client_wakeup(proc);
48 				ret = (int32_t) PSCI_E_SUCCESS;
49 			}
50 		}
51 	}
52 
53 	return ret;
54 }
55 
56 /**
57  * versal2_pwr_domain_off() - Turn off core.
58  * @target_state: Targeted state.
59  */
60 static void versal2_pwr_domain_off(const psci_power_state_t *target_state)
61 {
62 	const struct pm_proc *proc;
63 	uint32_t cpu_id = plat_my_core_pos();
64 	enum pm_ret_status pm_ret;
65 	size_t i;
66 
67 	proc = pm_get_proc(cpu_id);
68 	if (proc == NULL) {
69 		ERROR("Failed to get proc %d\n", cpu_id);
70 		goto err;
71 	}
72 
73 	for (i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
74 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
75 			__func__, i, target_state->pwr_domain_state[i]);
76 	}
77 
78 	plat_gic_cpuif_disable();
79 	/*
80 	 * Send request to PMC to power down the appropriate APU CPU
81 	 * core.
82 	 * According to PSCI specification, CPU_off function does not
83 	 * have resume address and CPU core can only be woken up
84 	 * invoking CPU_on function, during which resume address will
85 	 * be set.
86 	 */
87 	pm_ret = pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0,
88 			      SECURE_FLAG);
89 
90 	if (pm_ret != PM_RET_SUCCESS) {
91 		ERROR("Failed to power down CPU %d\n", cpu_id);
92 	}
93 err:
94 	return;
95 }
96 
97 /**
98  * versal2_system_reset() - Send the reset request to firmware for the
99  *                          system to reset. This function does not
100  *                          return as it resets system.
101  */
102 static void __dead2 versal2_system_reset(void)
103 {
104 	uint32_t timeout = 10000U;
105 	enum pm_ret_status pm_ret;
106 	int32_t ret;
107 
108 	request_cpu_pwrdwn();
109 
110 	/*
111 	 * Send the system reset request to the firmware if power down request
112 	 * is not received from firmware.
113 	 */
114 	if (pm_pwrdwn_req_status() == false) {
115 		/*
116 		 * TODO: shutdown scope for this reset needs be revised once
117 		 * we have a clearer understanding of the overall reset scoping
118 		 * including the implementation of SYSTEM_RESET2.
119 		 */
120 		pm_ret = pm_system_shutdown(XPM_SHUTDOWN_TYPE_RESET,
121 					 pm_get_shutdown_scope(), SECURE_FLAG);
122 
123 		if (pm_ret != PM_RET_SUCCESS) {
124 			WARN("System shutdown failed\n");
125 		}
126 
127 		/*
128 		 * Wait for system shutdown request completed and idle callback
129 		 * not received.
130 		 */
131 		do {
132 			ret = ipi_mb_enquire_status(primary_proc->ipi->local_ipi_id,
133 						    primary_proc->ipi->remote_ipi_id);
134 			udelay(100);
135 			timeout--;
136 		} while ((ret != (int32_t)IPI_MB_STATUS_RECV_PENDING) && (timeout > 0U));
137 	}
138 
139 	(void)psci_cpu_off();
140 
141 	while (true) {
142 		wfi();
143 	}
144 }
145 
146 /**
147  * versal2_pwr_domain_suspend() - Send request to PMC to suspend core.
148  * @target_state: Targeted state.
149  */
150 static void versal2_pwr_domain_suspend(const psci_power_state_t *target_state)
151 {
152 	const struct pm_proc *proc;
153 	uint32_t cpu_id = plat_my_core_pos();
154 	uint32_t state;
155 	enum pm_ret_status ret;
156 	size_t i;
157 
158 	proc = pm_get_proc(cpu_id);
159 	if (proc == NULL) {
160 		ERROR("Failed to get proc %d\n", cpu_id);
161 		goto err;
162 	}
163 
164 	for (i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
165 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
166 			__func__, i, target_state->pwr_domain_state[i]);
167 	}
168 
169 	plat_gic_cpuif_disable();
170 
171 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
172 		plat_gic_save();
173 	}
174 
175 	state = (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) ?
176 		PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE;
177 
178 	/* Send request to PMC to suspend this core */
179 	ret = pm_self_suspend(proc->node_id, MAX_LATENCY, state, sec_entry,
180 			      SECURE_FLAG);
181 
182 	if (ret != PM_RET_SUCCESS) {
183 		ERROR("Failed to power down CPU %d\n", cpu_id);
184 	}
185 
186 err:
187 	return;
188 }
189 
190 static int32_t versal2_validate_ns_entrypoint(uint64_t ns_entrypoint)
191 {
192 	int32_t ret = PSCI_E_SUCCESS;
193 	struct reserve_mem_range *rmr;
194 	uint32_t index = 0, counter = 0;
195 
196 	rmr = get_reserved_entries_fdt(&counter);
197 
198 	VERBOSE("Validate ns_entry point %lx\n", ns_entrypoint);
199 
200 	if (counter != 0) {
201 		while (index < counter) {
202 			if ((ns_entrypoint >= rmr[index].base) &&
203 				       (ns_entrypoint <= rmr[index].size)) {
204 				ret = PSCI_E_INVALID_ADDRESS;
205 				break;
206 			}
207 			index++;
208 		}
209 	} else {
210 		if ((ns_entrypoint >= BL31_BASE) && (ns_entrypoint <= BL31_LIMIT)) {
211 			ret = PSCI_E_INVALID_ADDRESS;
212 		}
213 	}
214 
215 	return ret;
216 }
217 
218 static void versal2_pwr_domain_on_finish(const psci_power_state_t *target_state)
219 {
220 	(void)target_state;
221 
222 	/* Enable the gic cpu interface */
223 	plat_gic_pcpu_init();
224 
225 	/* Program the gic per-cpu distributor or re-distributor interface */
226 	plat_gic_cpuif_enable();
227 }
228 
229 /**
230  * versal2_pwr_domain_suspend_finish() - Performs actions to finish
231  *                                       suspend procedure.
232  * @target_state: Targeted state.
233  */
234 static void versal2_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
235 {
236 	const struct pm_proc *proc;
237 	uint32_t cpu_id = plat_my_core_pos();
238 	size_t i;
239 
240 	proc = pm_get_proc(cpu_id);
241 	if (proc == NULL) {
242 		ERROR("Failed to get proc %d\n", cpu_id);
243 		goto err;
244 	}
245 
246 	for (i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
247 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
248 			__func__, i, target_state->pwr_domain_state[i]);
249 	}
250 
251 	/* Clear the APU power control register for this cpu */
252 	pm_client_wakeup(proc);
253 
254 	/* APU was turned off, so restore GIC context */
255 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
256 		plat_gic_resume();
257 	}
258 
259 	plat_gic_cpuif_enable();
260 
261 err:
262 	return;
263 }
264 
265 /**
266  * versal2_system_off() - Send the system off request to firmware.
267  *                        This function does not return as it puts core into WFI
268  */
269 static void __dead2 versal2_system_off(void)
270 {
271 	enum pm_ret_status ret;
272 
273 	/* Send the power down request to the PMC */
274 	ret = pm_system_shutdown(XPM_SHUTDOWN_TYPE_SHUTDOWN,
275 				 pm_get_shutdown_scope(), SECURE_FLAG);
276 
277 	if (ret != PM_RET_SUCCESS) {
278 		ERROR("System shutdown failed\n");
279 	}
280 
281 	while (true) {
282 		wfi();
283 	}
284 }
285 
286 /**
287  * versal2_validate_power_state() - Ensure that the power state
288  *                                  parameter in request is valid.
289  * @power_state: Power state of core.
290  * @req_state: Requested state.
291  *
292  * Return: Returns status, either PSCI_E_SUCCESS or reason.
293  */
294 static int32_t versal2_validate_power_state(unsigned int power_state,
295 					       psci_power_state_t *req_state)
296 {
297 	uint32_t pstate = psci_get_pstate_type(power_state);
298 	int32_t ret = PSCI_E_SUCCESS;
299 
300 	VERBOSE("%s: power_state: 0x%x\n", __func__, power_state);
301 
302 	assert(req_state);
303 
304 	/* Sanity check the requested state */
305 	if (pstate == PSTATE_TYPE_STANDBY) {
306 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE;
307 	} else {
308 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE;
309 	}
310 
311 	/* The 'state_id' is expected to be zero */
312 	if (psci_get_pstate_id(power_state) != 0U) {
313 		ret = PSCI_E_INVALID_PARAMS;
314 	}
315 
316 	return ret;
317 }
318 
319 /**
320  * versal2_get_sys_suspend_power_state() - Get power state for system
321  *                                            suspend.
322  * @req_state: Requested state.
323  */
324 static void versal2_get_sys_suspend_power_state(psci_power_state_t *req_state)
325 {
326 	uint64_t i;
327 
328 	for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) {
329 		req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
330 	}
331 }
332 
333 /**
334  * Export the platform specific power ops.
335  */
336 static const struct plat_psci_ops versal2_nopmc_psci_ops = {
337 	.pwr_domain_on                  = versal2_pwr_domain_on,
338 	.pwr_domain_off                 = versal2_pwr_domain_off,
339 	.pwr_domain_on_finish           = versal2_pwr_domain_on_finish,
340 	.pwr_domain_suspend             = versal2_pwr_domain_suspend,
341 	.pwr_domain_suspend_finish      = versal2_pwr_domain_suspend_finish,
342 	.system_off                     = versal2_system_off,
343 	.system_reset                   = versal2_system_reset,
344 	.validate_ns_entrypoint		= versal2_validate_ns_entrypoint,
345 	.validate_power_state           = versal2_validate_power_state,
346 	.get_sys_suspend_power_state    = versal2_get_sys_suspend_power_state,
347 };
348 
349 int plat_setup_psci_ops(uintptr_t sec_entrypoint,
350 			    const struct plat_psci_ops **psci_ops)
351 {
352 	sec_entry = sec_entrypoint;
353 
354 	VERBOSE("Setting up entry point %lx\n", sec_entry);
355 
356 	*psci_ops = &versal2_nopmc_psci_ops;
357 
358 	return 0;
359 }
360 
361 int32_t sip_svc_setup_init(void)
362 {
363 	return pm_setup();
364 }
365 
366 uint64_t smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4,
367 		     const void *cookie, void *handle, uint64_t flags)
368 {
369 	return pm_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags);
370 }
371