xref: /rk3399_ARM-atf/plat/xilinx/versal/plat_psci.c (revision 83a4dae1af916b938659b39b7d0884359c638185)
1 /*
2  * Copyright (c) 2018-2021, Arm Limited and Contributors. All rights reserved.
3  * Copyright (c) 2022-2023, Advanced Micro Devices, Inc. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #include <assert.h>
9 #include <plat_arm.h>
10 #include <plat_private.h>
11 #include <pm_common.h>
12 #include <common/debug.h>
13 #include <lib/mmio.h>
14 #include <lib/psci/psci.h>
15 #include <plat/common/platform.h>
16 #include <plat/arm/common/plat_arm.h>
17 
18 #include "pm_api_sys.h"
19 #include "pm_client.h"
20 
21 static uintptr_t versal_sec_entry;
22 
23 static int32_t versal_pwr_domain_on(u_register_t mpidr)
24 {
25 	int32_t cpu_id = plat_core_pos_by_mpidr(mpidr);
26 	const struct pm_proc *proc;
27 
28 	VERBOSE("%s: mpidr: 0x%lx\n", __func__, mpidr);
29 
30 	if (cpu_id == -1) {
31 		return PSCI_E_INTERN_FAIL;
32 	}
33 
34 	proc = pm_get_proc((uint32_t)cpu_id);
35 
36 	/* Send request to PMC to wake up selected ACPU core */
37 	(void)pm_req_wakeup(proc->node_id, (versal_sec_entry & 0xFFFFFFFFU) | 0x1U,
38 			    versal_sec_entry >> 32, 0, SECURE_FLAG);
39 
40 	/* Clear power down request */
41 	pm_client_wakeup(proc);
42 
43 	return PSCI_E_SUCCESS;
44 }
45 
46 /**
47  * versal_pwr_domain_suspend() - This function sends request to PMC to suspend
48  *                               core.
49  * @target_state: Targated state.
50  *
51  */
52 static void versal_pwr_domain_suspend(const psci_power_state_t *target_state)
53 {
54 	uint32_t state;
55 	uint32_t cpu_id = plat_my_core_pos();
56 	const struct pm_proc *proc = pm_get_proc(cpu_id);
57 
58 	for (size_t i = 0U; i <= PLAT_MAX_PWR_LVL; i++) {
59 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
60 			__func__, i, target_state->pwr_domain_state[i]);
61 	}
62 
63 	plat_versal_gic_cpuif_disable();
64 
65 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
66 		plat_versal_gic_save();
67 	}
68 
69 	state = target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE ?
70 		PM_STATE_SUSPEND_TO_RAM : PM_STATE_CPU_IDLE;
71 
72 	/* Send request to PMC to suspend this core */
73 	(void)pm_self_suspend(proc->node_id, MAX_LATENCY, state, versal_sec_entry,
74 			      SECURE_FLAG);
75 
76 	/* APU is to be turned off */
77 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
78 		/* disable coherency */
79 		plat_arm_interconnect_exit_coherency();
80 	}
81 }
82 
83 /**
84  * versal_pwr_domain_suspend_finish() - This function performs actions to finish
85  *                                      suspend procedure.
86  * @target_state: Targated state.
87  *
88  */
89 static void versal_pwr_domain_suspend_finish(
90 					const psci_power_state_t *target_state)
91 {
92 	uint32_t cpu_id = plat_my_core_pos();
93 	const struct pm_proc *proc = pm_get_proc(cpu_id);
94 
95 	for (size_t i = 0U; i <= PLAT_MAX_PWR_LVL; i++) {
96 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
97 			__func__, i, target_state->pwr_domain_state[i]);
98 	}
99 
100 	/* Clear the APU power control register for this cpu */
101 	pm_client_wakeup(proc);
102 
103 	/* enable coherency */
104 	plat_arm_interconnect_enter_coherency();
105 
106 	/* APU was turned off, so restore GIC context */
107 	if (target_state->pwr_domain_state[1] > PLAT_MAX_RET_STATE) {
108 		plat_versal_gic_resume();
109 	}
110 
111 	plat_versal_gic_cpuif_enable();
112 }
113 
114 void versal_pwr_domain_on_finish(const psci_power_state_t *target_state)
115 {
116 	/* Enable the gic cpu interface */
117 	plat_versal_gic_pcpu_init();
118 
119 	/* Program the gic per-cpu distributor or re-distributor interface */
120 	plat_versal_gic_cpuif_enable();
121 }
122 
123 /**
124  * versal_system_off() - This function sends the system off request to firmware.
125  *                       This function does not return.
126  *
127  */
128 static void __dead2 versal_system_off(void)
129 {
130 	/* Send the power down request to the PMC */
131 	(void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_SHUTDOWN,
132 				 pm_get_shutdown_scope(), SECURE_FLAG);
133 
134 	while (1) {
135 		wfi();
136 	}
137 }
138 
139 /**
140  * versal_system_reset() - This function sends the reset request to firmware
141  *                         for the system to reset.  This function does not
142  *			   return.
143  *
144  */
145 static void __dead2 versal_system_reset(void)
146 {
147 	/* Send the system reset request to the PMC */
148 	(void)pm_system_shutdown(XPM_SHUTDOWN_TYPE_RESET,
149 				 pm_get_shutdown_scope(), SECURE_FLAG);
150 
151 	while (1) {
152 		wfi();
153 	}
154 }
155 
156 /**
157  * versal_pwr_domain_off() - This function performs actions to turn off core.
158  * @target_state: Targated state.
159  *
160  */
161 static void versal_pwr_domain_off(const psci_power_state_t *target_state)
162 {
163 	uint32_t cpu_id = plat_my_core_pos();
164 	const struct pm_proc *proc = pm_get_proc(cpu_id);
165 
166 	for (size_t i = 0U; i <= PLAT_MAX_PWR_LVL; i++) {
167 		VERBOSE("%s: target_state->pwr_domain_state[%lu]=%x\n",
168 			__func__, i, target_state->pwr_domain_state[i]);
169 	}
170 
171 	/* Prevent interrupts from spuriously waking up this cpu */
172 	plat_versal_gic_cpuif_disable();
173 
174 	/*
175 	 * Send request to PMC to power down the appropriate APU CPU
176 	 * core.
177 	 * According to PSCI specification, CPU_off function does not
178 	 * have resume address and CPU core can only be woken up
179 	 * invoking CPU_on function, during which resume address will
180 	 * be set.
181 	 */
182 	(void)pm_self_suspend(proc->node_id, MAX_LATENCY, PM_STATE_CPU_IDLE, 0,
183 			      SECURE_FLAG);
184 }
185 
186 /**
187  * versal_validate_power_state() - This function ensures that the power state
188  *                                 parameter in request is valid.
189  * @power_state: Power state of core.
190  * @req_state: Requested state.
191  *
192  * Return: Returns status, either success or reason.
193  *
194  */
195 static int32_t versal_validate_power_state(uint32_t power_state,
196 				       psci_power_state_t *req_state)
197 {
198 	VERBOSE("%s: power_state: 0x%x\n", __func__, power_state);
199 
200 	uint32_t pstate = psci_get_pstate_type(power_state);
201 
202 	assert(req_state);
203 
204 	/* Sanity check the requested state */
205 	if (pstate == PSTATE_TYPE_STANDBY) {
206 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_RET_STATE;
207 	} else {
208 		req_state->pwr_domain_state[MPIDR_AFFLVL0] = PLAT_MAX_OFF_STATE;
209 	}
210 
211 	/* We expect the 'state id' to be zero */
212 	if (psci_get_pstate_id(power_state) != 0U) {
213 		return PSCI_E_INVALID_PARAMS;
214 	}
215 
216 	return PSCI_E_SUCCESS;
217 }
218 
219 /**
220  * versal_get_sys_suspend_power_state() - Get power state for system suspend.
221  * @req_state: Requested state.
222  *
223  */
224 static void versal_get_sys_suspend_power_state(psci_power_state_t *req_state)
225 {
226 	req_state->pwr_domain_state[PSCI_CPU_PWR_LVL] = PLAT_MAX_OFF_STATE;
227 	req_state->pwr_domain_state[1] = PLAT_MAX_OFF_STATE;
228 }
229 
230 static const struct plat_psci_ops versal_nopmc_psci_ops = {
231 	.pwr_domain_on			= versal_pwr_domain_on,
232 	.pwr_domain_off			= versal_pwr_domain_off,
233 	.pwr_domain_on_finish		= versal_pwr_domain_on_finish,
234 	.pwr_domain_suspend		= versal_pwr_domain_suspend,
235 	.pwr_domain_suspend_finish	= versal_pwr_domain_suspend_finish,
236 	.system_off			= versal_system_off,
237 	.system_reset			= versal_system_reset,
238 	.validate_power_state		= versal_validate_power_state,
239 	.get_sys_suspend_power_state	= versal_get_sys_suspend_power_state,
240 };
241 
242 /*******************************************************************************
243  * Export the platform specific power ops.
244  ******************************************************************************/
245 int32_t plat_setup_psci_ops(uintptr_t sec_entrypoint,
246 			const struct plat_psci_ops **psci_ops)
247 {
248 	versal_sec_entry = sec_entrypoint;
249 
250 	*psci_ops = &versal_nopmc_psci_ops;
251 
252 	return 0;
253 }
254