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