xref: /rk3399_ARM-atf/plat/xilinx/zynqmp/plat_psci.c (revision b67e984664a8644d6cfd1812cabaa02cf24f09c9)
1 /*
2  * Copyright (c) 2013-2022, 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 #include <errno.h>
10 
11 #include <arch_helpers.h>
12 #include <common/debug.h>
13 #include <drivers/arm/gicv2.h>
14 #include <lib/mmio.h>
15 #include <lib/psci/psci.h>
16 #include <plat/arm/common/plat_arm.h>
17 #include <plat/common/platform.h>
18 
19 #include <plat_private.h>
20 #include "pm_client.h"
21 #include "zynqmp_pm_api_sys.h"
22 
23 static uintptr_t zynqmp_sec_entry;
24 
25 static void zynqmp_cpu_standby(plat_local_state_t cpu_state)
26 {
27 	VERBOSE("%s: cpu_state: 0x%x\n", __func__, cpu_state);
28 
29 	dsb();
30 	wfi();
31 }
32 
33 static int32_t zynqmp_pwr_domain_on(u_register_t mpidr)
34 {
35 	int32_t cpu_id = plat_core_pos_by_mpidr(mpidr);
36 	const struct pm_proc *proc;
37 	uint32_t buff[3];
38 	enum pm_ret_status ret;
39 	int32_t result = PSCI_E_INTERN_FAIL;
40 
41 	VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr);
42 
43 	if (cpu_id == -1) {
44 		goto exit_label;
45 	}
46 
47 	proc = pm_get_proc(cpu_id);
48 	if (proc == NULL) {
49 		goto exit_label;
50 	}
51 
52 	/* Check the APU proc status before wakeup */
53 	ret = pm_get_node_status(proc->node_id, buff, NON_SECURE);
54 	if ((ret != PM_RET_SUCCESS) || (buff[0] == PM_PROC_STATE_SUSPENDING)) {
55 		goto exit_label;
56 	}
57 
58 	/* Clear power down request */
59 	pm_client_wakeup(proc);
60 
61 	/* Send request to PMU to wake up selected APU CPU core */
62 	(void)pm_req_wakeup(proc->node_id, 1, zynqmp_sec_entry, REQ_ACK_BLOCKING,
63 			    NON_SECURE);
64 
65 	result = PSCI_E_SUCCESS;
66 
67 exit_label:
68 	return result;
69 }
70 
71 static void zynqmp_pwr_domain_off(const psci_power_state_t *target_state)
72 {
73 	uint32_t cpu_id = plat_my_core_pos();
74 	const struct pm_proc *proc = pm_get_proc(cpu_id);
75 
76 	if (proc == NULL) {
77 		return;
78 	}
79 
80 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
81 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
82 			__func__, i, target_state->pwr_domain_state[i]);
83 	}
84 
85 	/* Prevent interrupts from spuriously waking up this cpu */
86 	gicv2_cpuif_disable();
87 
88 	/*
89 	 * Send request to PMU to power down the appropriate APU CPU
90 	 * core.
91 	 * According to PSCI specification, CPU_off function does not
92 	 * have resume address and CPU core can only be woken up
93 	 * invoking CPU_on function, during which resume address will
94 	 * be set.
95 	 */
96 	(void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0,
97 			      NON_SECURE);
98 }
99 
100 static void zynqmp_pwr_domain_suspend(const psci_power_state_t *target_state)
101 {
102 	uint32_t state;
103 	uint32_t cpu_id = plat_my_core_pos();
104 	const struct pm_proc *proc = pm_get_proc(cpu_id);
105 
106 	if (proc == NULL) {
107 		return;
108 	}
109 
110 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
111 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
112 			__func__, i, target_state->pwr_domain_state[i]);
113 	}
114 
115 	state = (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) ?
116 		PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE;
117 
118 	/* Send request to PMU to suspend this core */
119 	(void)pm_self_suspend(proc->node_id, MAX_LATENCY, state, zynqmp_sec_entry,
120 			      NON_SECURE);
121 
122 	/* APU is to be turned off */
123 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
124 		/* disable coherency */
125 		plat_arm_interconnect_exit_coherency();
126 	}
127 }
128 
129 static void zynqmp_pwr_domain_on_finish(const psci_power_state_t *target_state)
130 {
131 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
132 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
133 			__func__, i, target_state->pwr_domain_state[i]);
134 	}
135 	plat_arm_gic_pcpu_init();
136 	gicv2_cpuif_enable();
137 }
138 
139 static void zynqmp_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
140 {
141 	uint32_t cpu_id = plat_my_core_pos();
142 	const struct pm_proc *proc = pm_get_proc(cpu_id);
143 
144 	if (proc == NULL) {
145 		return;
146 	}
147 
148 	for (size_t i = 0; i <= PLAT_MAX_PWR_LVL; i++) {
149 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
150 			__func__, i, target_state->pwr_domain_state[i]);
151 	}
152 
153 	/* Clear the APU power control register for this cpu */
154 	pm_client_wakeup(proc);
155 
156 	/* enable coherency */
157 	plat_arm_interconnect_enter_coherency();
158 	/* APU was turned off */
159 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
160 		plat_arm_gic_init();
161 	} else {
162 		gicv2_cpuif_enable();
163 		gicv2_pcpu_distif_init();
164 	}
165 }
166 
167 /*******************************************************************************
168  * ZynqMP handlers to shutdown/reboot the system
169  ******************************************************************************/
170 
171 static void __dead2 zynqmp_system_off(void)
172 {
173 	/* disable coherency */
174 	plat_arm_interconnect_exit_coherency();
175 
176 	/* Send the power down request to the PMU */
177 	(void)pm_system_shutdown((uint32_t)PMF_SHUTDOWN_TYPE_SHUTDOWN,
178 				 pm_get_shutdown_scope(),
179 				 NON_SECURE);
180 
181 	while (true) {
182 		wfi();
183 	}
184 }
185 
186 static void __dead2 zynqmp_system_reset(void)
187 {
188 	/* disable coherency */
189 	plat_arm_interconnect_exit_coherency();
190 
191 	/* Send the system reset request to the PMU */
192 	(void)pm_system_shutdown((uint32_t)PMF_SHUTDOWN_TYPE_RESET,
193 				 pm_get_shutdown_scope(),
194 				 NON_SECURE);
195 
196 	while (true) {
197 		wfi();
198 	}
199 }
200 
201 static int32_t zynqmp_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 static int32_t zynqmp_validate_power_state(uint32_t power_state,
214 				psci_power_state_t *req_state)
215 {
216 	VERBOSE("%s: power_state: 0x%x\n", __func__, power_state);
217 
218 	uint32_t pstate = psci_get_pstate_type(power_state);
219 	int32_t result = PSCI_E_INVALID_PARAMS;
220 
221 	assert(req_state);
222 
223 	/* Sanity check the requested state */
224 	if (pstate == PSTATE_TYPE_STANDBY) {
225 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE;
226 	} else {
227 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE;
228 	}
229 	/* We expect the 'state id' to be zero */
230 	if (psci_get_pstate_id(power_state) == 0U) {
231 		result = PSCI_E_SUCCESS;
232 	}
233 
234 	return result;
235 }
236 
237 static void zynqmp_get_sys_suspend_power_state(psci_power_state_t *req_state)
238 {
239 	req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE;
240 	req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE;
241 }
242 
243 /*******************************************************************************
244  * Export the platform handlers to enable psci to invoke them
245  ******************************************************************************/
246 static const struct plat_psci_ops zynqmp_psci_ops = {
247 	.cpu_standby			= zynqmp_cpu_standby,
248 	.pwr_domain_on			= zynqmp_pwr_domain_on,
249 	.pwr_domain_off			= zynqmp_pwr_domain_off,
250 	.pwr_domain_suspend		= zynqmp_pwr_domain_suspend,
251 	.pwr_domain_on_finish		= zynqmp_pwr_domain_on_finish,
252 	.pwr_domain_suspend_finish	= zynqmp_pwr_domain_suspend_finish,
253 	.system_off			= zynqmp_system_off,
254 	.system_reset			= zynqmp_system_reset,
255 	.validate_ns_entrypoint		= zynqmp_validate_ns_entrypoint,
256 	.validate_power_state		= zynqmp_validate_power_state,
257 	.get_sys_suspend_power_state	= zynqmp_get_sys_suspend_power_state,
258 };
259 
260 /*******************************************************************************
261  * Export the platform specific power ops.
262  ******************************************************************************/
263 int plat_setup_psci_ops(uintptr_t sec_entrypoint,
264 			const struct plat_psci_ops **psci_ops)
265 {
266 	zynqmp_sec_entry = sec_entrypoint;
267 
268 	*psci_ops = &zynqmp_psci_ops;
269 
270 	return 0;
271 }
272