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 <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
18 #include <drivers/delay_timer.h>
19 #include <plat_private.h>
20 #include "pm_api_sys.h"
21 #include "pm_client.h"
22 #include <pm_common.h>
23 #include "pm_ipi.h"
24 #include "pm_svc_main.h"
25 #include "versal_net_def.h"
26
27 static uintptr_t versal_net_sec_entry;
28
versal_net_pwr_domain_on(u_register_t mpidr)29 static int32_t versal_net_pwr_domain_on(u_register_t mpidr)
30 {
31 int32_t cpu_id = plat_core_pos_by_mpidr(mpidr);
32 const struct pm_proc *proc;
33 int32_t ret = PSCI_E_INTERN_FAIL;
34
35 VERBOSE("%s: mpidr: 0x%lx, cpuid: %x\n",
36 __func__, mpidr, cpu_id);
37
38 if (cpu_id == -1) {
39 goto exit_label;
40 }
41
42 proc = pm_get_proc(cpu_id);
43 if (proc == NULL) {
44 goto exit_label;
45 }
46
47 (void)pm_req_wakeup(proc->node_id, (versal_net_sec_entry & 0xFFFFFFFFU) | 0x1U,
48 versal_net_sec_entry >> 32, 0, 0);
49
50 /* Clear power down request */
51 pm_client_wakeup(proc);
52
53 ret = PSCI_E_SUCCESS;
54
55 exit_label:
56 return ret;
57 }
58
59 /**
60 * versal_net_pwr_domain_off() - This function performs actions to turn off
61 * core.
62 * @target_state: Targeted state.
63 *
64 */
versal_net_pwr_domain_off(const psci_power_state_t * target_state)65 static void versal_net_pwr_domain_off(const psci_power_state_t *target_state)
66 {
67 uint32_t ret, fw_api_version, version_type[RET_PAYLOAD_ARG_CNT] = {0U};
68 uint32_t cpu_id = plat_my_core_pos();
69 const struct pm_proc *proc = pm_get_proc(cpu_id);
70
71 if (proc == NULL) {
72 goto exit_label;
73 }
74
75 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
76 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
77 __func__, i, target_state->pwr_domain_state[i]);
78 }
79
80 /*
81 * Send request to PMC to power down the appropriate APU CPU
82 * core.
83 * According to PSCI specification, CPU_off function does not
84 * have resume address and CPU core can only be woken up
85 * invoking CPU_on function, during which resume address will
86 * be set.
87 */
88 ret = pm_feature_check((uint32_t)PM_SELF_SUSPEND, &version_type[0], NON_SECURE);
89 if (ret == (uint32_t)PM_RET_SUCCESS) {
90 fw_api_version = version_type[0] & 0xFFFFU;
91 if (fw_api_version >= 3U) {
92 (void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_OFF, 0,
93 NON_SECURE);
94 } else {
95 (void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0,
96 NON_SECURE);
97 }
98 }
99
100 exit_label:
101 return;
102 }
103
versal_net_validate_ns_entrypoint(uint64_t ns_entrypoint)104 static int32_t versal_net_validate_ns_entrypoint(uint64_t ns_entrypoint)
105 {
106 int32_t ret = PSCI_E_SUCCESS;
107
108 if (((ns_entrypoint >= PLAT_DDR_LOWMEM_MAX) && (ns_entrypoint <= PLAT_DDR_HIGHMEM_MAX)) ||
109 ((ns_entrypoint >= BL31_BASE) && (ns_entrypoint <= BL31_LIMIT))) {
110 ret = PSCI_E_INVALID_ADDRESS;
111 }
112
113 return ret;
114 }
115
116 /**
117 * versal_net_system_reset_scope() - Sends the reset request to firmware for
118 * the system to reset.
119 * @scope : scope of reset which could be SYSTEM/SUBSYSTEM/PS-ONLY
120 *
121 * Return:
122 * Does not return if system resets, none if there is a failure.
123 */
versal_net_system_reset_scope(uint32_t scope)124 static void __dead2 versal_net_system_reset_scope(uint32_t scope)
125 {
126 uint32_t ret, timeout = 10000U;
127
128 request_cpu_pwrdwn();
129
130 /*
131 * Send the system reset request to the firmware if power down request
132 * is not received from firmware.
133 */
134 if (!pm_pwrdwn_req_status()) {
135 (void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_RESET,
136 scope, NON_SECURE);
137
138 /*
139 * Wait for system shutdown request completed and idle callback
140 * not received.
141 */
142 do {
143 ret = ipi_mb_enquire_status(primary_proc->ipi->local_ipi_id,
144 primary_proc->ipi->remote_ipi_id);
145 udelay(100);
146 timeout--;
147 } while ((ret != IPI_MB_STATUS_RECV_PENDING) && (timeout > 0U));
148 }
149
150 (void)psci_cpu_off();
151
152 while (true) {
153 wfi();
154 }
155 }
156
157 /**
158 * versal_net_system_reset() - This function sends the reset request to firmware
159 * for the system to reset in response to SYSTEM_RESET call
160 *
161 * Return:
162 * Does not return if system resets, none if there is a failure.
163 */
versal_net_system_reset(void)164 static void __dead2 versal_net_system_reset(void)
165 {
166 /*
167 * Any platform-specific actions for handling a cold reset
168 * should be performed here before invoking
169 * versal_net_system_reset_scope.
170 */
171 versal_net_system_reset_scope(XPM_SHUTDOWN_SUBTYPE_RST_SUBSYSTEM);
172 }
173
174 /**
175 * versal_net_system_reset2() - Handles warm / vendor-specific system reset
176 * in response to SYSTEM_RESET2 call.
177 * @is_vendor: Flag indicating if this is a vendor-specific reset
178 * @reset_type: Type of reset requested
179 * @cookie: Additional reset data
180 *
181 * This function initiates a controlled system reset by requesting it
182 * through the PM firmware.
183 *
184 * Return:
185 * Does not return if system resets, PSCI_E_INTERN_FAIL
186 * if there is a failure.
187 */
versal_net_system_reset2(int is_vendor,int reset_type,u_register_t cookie)188 static int versal_net_system_reset2(int is_vendor, int reset_type, u_register_t cookie)
189 {
190 if (is_vendor == 0 && reset_type == PSCI_RESET2_SYSTEM_WARM_RESET) {
191 /*
192 * Any platform-specific actions for handling a warm reset
193 * should be performed here before invoking
194 * versal_net_system_reset_scope.
195 */
196 versal_net_system_reset_scope(XPM_SHUTDOWN_SUBTYPE_RST_SUBSYSTEM);
197 } else {
198 /* Vendor specific reset */
199 versal_net_system_reset_scope(pm_get_shutdown_scope());
200 }
201
202 return PSCI_E_INTERN_FAIL;
203 }
204
205 /**
206 * versal_net_pwr_domain_suspend() - This function sends request to PMC to suspend
207 * core.
208 * @target_state: Targeted state.
209 *
210 */
versal_net_pwr_domain_suspend(const psci_power_state_t * target_state)211 static void versal_net_pwr_domain_suspend(const psci_power_state_t *target_state)
212 {
213 uint32_t state;
214 uint32_t cpu_id = plat_my_core_pos();
215 const struct pm_proc *proc = pm_get_proc(cpu_id);
216
217 if (proc == NULL) {
218 goto exit_label;
219 }
220
221 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
222 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
223 __func__, i, target_state->pwr_domain_state[i]);
224 }
225
226 if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
227 gic_save();
228 }
229
230 state = (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) ?
231 PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE;
232
233 /* Send request to PMC to suspend this core */
234 (void)pm_self_suspend(proc->node_id, MAX_LATENCY, state, versal_net_sec_entry,
235 NON_SECURE);
236
237 /* TODO: disable coherency */
238
239 exit_label:
240 return;
241 }
242
versal_net_pwr_domain_on_finish(const psci_power_state_t * target_state)243 static void versal_net_pwr_domain_on_finish(const psci_power_state_t *target_state)
244 {
245 (void)target_state;
246 }
247
248 /**
249 * versal_net_pwr_domain_suspend_finish() - This function performs actions to finish
250 * suspend procedure.
251 * @target_state: Targeted state.
252 *
253 */
versal_net_pwr_domain_suspend_finish(const psci_power_state_t * target_state)254 static void versal_net_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
255 {
256 uint32_t cpu_id = plat_my_core_pos();
257 const struct pm_proc *proc = pm_get_proc(cpu_id);
258
259 if (proc == NULL) {
260 goto exit_label;
261 }
262
263 for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
264 VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
265 __func__, i, target_state->pwr_domain_state[i]);
266 }
267
268 /* Clear the APU power control register for this cpu */
269 pm_client_wakeup(proc);
270
271 /* TODO: enable coherency */
272
273 /* APU was turned off, so restore GIC context */
274 if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
275 gic_resume();
276 }
277
278 exit_label:
279 return;
280 }
281
282 /**
283 * versal_net_system_off() - This function sends the system off request
284 * to firmware. This function does not return.
285 *
286 */
versal_net_system_off(void)287 static void __dead2 versal_net_system_off(void)
288 {
289 /* Send the power down request to the PMC */
290 (void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_SHUTDOWN,
291 pm_get_shutdown_scope(), NON_SECURE);
292
293 while (true) {
294 wfi();
295 }
296 }
297
298 /**
299 * versal_net_validate_power_state() - This function ensures that the power state
300 * parameter in request is valid.
301 * @power_state: Power state of core.
302 * @req_state: Requested state.
303 *
304 * Return: Returns status, either PSCI_E_SUCCESS or reason.
305 *
306 */
versal_net_validate_power_state(unsigned int power_state,psci_power_state_t * req_state)307 static int32_t versal_net_validate_power_state(unsigned int power_state,
308 psci_power_state_t *req_state)
309 {
310 int32_t ret = PSCI_E_INVALID_PARAMS;
311
312 VERBOSE("%s: power_state: 0x%x\n", __func__, power_state);
313
314 uint32_t pstate = psci_get_pstate_type(power_state);
315
316 assert(req_state != NULL);
317
318 /* Sanity check the requested state */
319 if (pstate == PSTATE_TYPE_STANDBY) {
320 req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE;
321 } else {
322 req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE;
323 }
324
325 /* We expect the 'state id' to be zero */
326 if (psci_get_pstate_id(power_state) == 0U) {
327 ret = PSCI_E_SUCCESS;
328 }
329
330 return ret;
331 }
332
333 /**
334 * versal_net_get_sys_suspend_power_state() - Get power state for system
335 * suspend.
336 * @req_state: Requested state.
337 *
338 */
versal_net_get_sys_suspend_power_state(psci_power_state_t * req_state)339 static void versal_net_get_sys_suspend_power_state(psci_power_state_t *req_state)
340 {
341 uint64_t i;
342
343 for (i = MPIDR_AFFLVL0; i <= PLAT_MAX_PWR_LVL; i++) {
344 req_state->pwr_domain_state[i] = PLAT_MAX_OFF_STATE;
345 }
346 }
347
348 static const struct plat_psci_ops versal_net_nopmc_psci_ops = {
349 .pwr_domain_on = versal_net_pwr_domain_on,
350 .pwr_domain_off = versal_net_pwr_domain_off,
351 .pwr_domain_on_finish = versal_net_pwr_domain_on_finish,
352 .pwr_domain_suspend = versal_net_pwr_domain_suspend,
353 .pwr_domain_suspend_finish = versal_net_pwr_domain_suspend_finish,
354 .system_off = versal_net_system_off,
355 .system_reset = versal_net_system_reset,
356 .system_reset2 = versal_net_system_reset2,
357 .validate_ns_entrypoint = versal_net_validate_ns_entrypoint,
358 .validate_power_state = versal_net_validate_power_state,
359 .get_sys_suspend_power_state = versal_net_get_sys_suspend_power_state,
360 };
361
362 /*******************************************************************************
363 * Export the platform specific power ops.
364 ******************************************************************************/
plat_setup_psci_ops(uintptr_t sec_entrypoint,const struct plat_psci_ops ** psci_ops)365 int32_t plat_setup_psci_ops(uintptr_t sec_entrypoint,
366 const struct plat_psci_ops **psci_ops)
367 {
368 versal_net_sec_entry = sec_entrypoint;
369
370 VERBOSE("Setting up entry point %lx\n", versal_net_sec_entry);
371
372 *psci_ops = &versal_net_nopmc_psci_ops;
373
374 return 0;
375 }
376
sip_svc_setup_init(void)377 int32_t sip_svc_setup_init(void)
378 {
379 return pm_setup();
380 }
381
smc_handler(uint32_t smc_fid,uint64_t x1,uint64_t x2,uint64_t x3,uint64_t x4,void * cookie,void * handle,uint64_t flags)382 uint64_t smc_handler(uint32_t smc_fid, uint64_t x1, uint64_t x2, uint64_t x3, uint64_t x4,
383 void *cookie, void *handle, uint64_t flags)
384 {
385 return pm_smc_handler(smc_fid, x1, x2, x3, x4, cookie, handle, flags);
386 }
387