blob: 2c86e1c3fb9626658e8a68ff783b7cdf3f562d69 [file] [log] [blame]
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +02001/*
Boyan Karatotev794b0ac2025-06-20 13:13:29 +01002 * Copyright (c) 2018-2025, Arm Limited. All rights reserved.
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +02003 *
4 * SPDX-License-Identifier: BSD-3-Clause
5 */
6
7#include <arch_helpers.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +02008#include <debug.h>
Antonio Nino Diaz09a00ef2019-01-11 13:12:58 +00009#include <drivers/arm/arm_gic.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020010#include <irq.h>
11#include <platform.h>
12#include <power_management.h>
13#include <psci.h>
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020014#include <tftf.h>
15#include <tftf_lib.h>
16
17static unsigned int pstate_format_detected;
18static unsigned int pstate_format;
19static unsigned int is_state_id_null;
20
21const psci_function_t psci_functions[PSCI_NUM_CALLS] = {
22 DEFINE_PSCI_FUNC(PSCI_FEATURES, true),
23 DEFINE_PSCI_FUNC(PSCI_VERSION, true),
24 DEFINE_PSCI_FUNC(PSCI_CPU_SUSPEND_AARCH32, true),
25 DEFINE_PSCI_FUNC(PSCI_CPU_SUSPEND_AARCH64, true),
26 DEFINE_PSCI_FUNC(PSCI_CPU_OFF, true),
27 DEFINE_PSCI_FUNC(PSCI_CPU_ON_AARCH32, true),
28 DEFINE_PSCI_FUNC(PSCI_CPU_ON_AARCH64, true),
29 DEFINE_PSCI_FUNC(PSCI_AFFINITY_INFO_AARCH32, true),
30 DEFINE_PSCI_FUNC(PSCI_AFFINITY_INFO_AARCH64, true),
31 DEFINE_PSCI_FUNC(PSCI_SYSTEM_OFF, true),
32 DEFINE_PSCI_FUNC(PSCI_SYSTEM_RESET, true),
33 DEFINE_PSCI_FUNC(PSCI_MIG_INFO_TYPE, false),
34 DEFINE_PSCI_FUNC(PSCI_MIG_INFO_UP_CPU_AARCH32, false),
35 DEFINE_PSCI_FUNC(PSCI_MIG_INFO_UP_CPU_AARCH64, false),
36 DEFINE_PSCI_FUNC(PSCI_MIG_AARCH32, false),
37 DEFINE_PSCI_FUNC(PSCI_MIG_AARCH64, false),
38 DEFINE_PSCI_FUNC(PSCI_CPU_FREEZE, false),
39 DEFINE_PSCI_FUNC(PSCI_CPU_DEFAULT_SUSPEND32, false),
40 DEFINE_PSCI_FUNC(PSCI_CPU_DEFAULT_SUSPEND64, false),
41 DEFINE_PSCI_FUNC(PSCI_CPU_HW_STATE32, false),
42 DEFINE_PSCI_FUNC(PSCI_CPU_HW_STATE64, false),
43 DEFINE_PSCI_FUNC(PSCI_SYSTEM_SUSPEND32, false),
44 DEFINE_PSCI_FUNC(PSCI_SYSTEM_SUSPEND64, false),
45 DEFINE_PSCI_FUNC(PSCI_SET_SUSPEND_MODE, false),
46 DEFINE_PSCI_FUNC(PSCI_STAT_RESIDENCY32, false),
47 DEFINE_PSCI_FUNC(PSCI_STAT_RESIDENCY64, false),
48 DEFINE_PSCI_FUNC(PSCI_STAT_COUNT32, false),
49 DEFINE_PSCI_FUNC(PSCI_STAT_COUNT64, false),
50 DEFINE_PSCI_FUNC(PSCI_MEM_PROTECT, false),
51 DEFINE_PSCI_FUNC(PSCI_MEM_PROTECT_CHECK_RANGE32, false),
52 DEFINE_PSCI_FUNC(PSCI_MEM_PROTECT_CHECK_RANGE64, false),
53 DEFINE_PSCI_FUNC(PSCI_RESET2_AARCH32, false),
54 DEFINE_PSCI_FUNC(PSCI_RESET2_AARCH64, false),
55};
56
57int32_t tftf_psci_cpu_on(u_register_t target_cpu,
58 uintptr_t entry_point_address,
59 u_register_t context_id)
60{
61 smc_args args = {
62 SMC_PSCI_CPU_ON,
Imre Kisb1467682025-08-28 14:38:36 +020063 psci_target_cpu_from_mpid(target_cpu),
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020064 entry_point_address,
65 context_id
66 };
67 smc_ret_values ret_vals;
68
69 ret_vals = tftf_smc(&args);
70
71 return ret_vals.ret0;
72}
73
74int32_t tftf_psci_cpu_off(void)
75{
76 smc_args args = { SMC_PSCI_CPU_OFF };
77 smc_ret_values ret_vals;
78
79 ret_vals = tftf_smc(&args);
80 return ret_vals.ret0;
81}
82
Wing Licb88add2022-10-29 02:32:06 +010083int32_t tftf_psci_set_suspend_mode(uint32_t mode)
84{
85 smc_args args = {
86 SMC_PSCI_SET_SUSPEND_MODE,
87 mode
88 };
89 smc_ret_values ret_vals;
90
91 ret_vals = tftf_smc(&args);
92 return ret_vals.ret0;
93}
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +020094
95u_register_t tftf_psci_stat_residency(u_register_t target_cpu,
96 uint32_t power_state)
97{
98 smc_args args = {
99 SMC_PSCI_STAT_RESIDENCY,
Imre Kisb1467682025-08-28 14:38:36 +0200100 psci_target_cpu_from_mpid(target_cpu),
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200101 power_state,
102 };
103 smc_ret_values ret_vals;
104
105 ret_vals = tftf_smc(&args);
106 return ret_vals.ret0;
107}
108
109u_register_t tftf_psci_stat_count(u_register_t target_cpu,
110 uint32_t power_state)
111{
112 smc_args args = {
113 SMC_PSCI_STAT_COUNT,
Imre Kisb1467682025-08-28 14:38:36 +0200114 psci_target_cpu_from_mpid(target_cpu),
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200115 power_state,
116 };
117 smc_ret_values ret_vals;
118
119 ret_vals = tftf_smc(&args);
120 return ret_vals.ret0;
121}
122
123int32_t tftf_psci_affinity_info(u_register_t target_affinity,
124 uint32_t lowest_affinity_level)
125{
126 smc_ret_values ret_vals;
127
128 smc_args args = {
129 SMC_PSCI_AFFINITY_INFO,
Imre Kisb1467682025-08-28 14:38:36 +0200130 psci_target_cpu_from_mpid(target_affinity),
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200131 lowest_affinity_level
132 };
133
134 ret_vals = tftf_smc(&args);
135 return ret_vals.ret0;
136}
137
138int32_t tftf_psci_node_hw_state(u_register_t target_cpu, uint32_t power_level)
139{
140 smc_args args = {
141 SMC_PSCI_CPU_HW_STATE,
Imre Kisb1467682025-08-28 14:38:36 +0200142 psci_target_cpu_from_mpid(target_cpu),
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200143 power_level
144 };
145 smc_ret_values ret;
146
147 ret = tftf_smc(&args);
148 return ret.ret0;
149}
150
151int32_t tftf_get_psci_feature_info(uint32_t psci_func_id)
152{
153 smc_args args = {
154 SMC_PSCI_FEATURES,
155 psci_func_id
156 };
157 smc_ret_values ret;
158
159 ret = tftf_smc(&args);
160 return ret.ret0;
161}
162
163int tftf_psci_make_composite_state_id(uint32_t affinity_level,
164 uint32_t state_type, uint32_t *state_id)
165{
166 unsigned int found_entry, i;
167 int ret = PSCI_E_SUCCESS;
168 const plat_state_prop_t *state_prop;
169
170 assert(state_id);
171
172 *state_id = 0;
173 for (i = 0; i <= affinity_level; i++) {
174 state_prop = plat_get_state_prop(i);
175 if (!state_prop) {
176 *state_id |= psci_make_local_state_id(i,
177 PLAT_PSCI_DUMMY_STATE_ID);
178 ret = PSCI_E_INVALID_PARAMS;
179 continue;
180 }
181 found_entry = 0;
182
183 while (state_prop->state_ID) {
184 if (state_type == state_prop->is_pwrdown) {
185 *state_id |= psci_make_local_state_id(i,
186 state_prop->state_ID);
187 found_entry = 1;
188 break;
189 }
190 state_prop++;
191 }
192 if (!found_entry) {
193 *state_id |= psci_make_local_state_id(i,
194 PLAT_PSCI_DUMMY_STATE_ID);
195 ret = PSCI_E_INVALID_PARAMS;
196 }
197 }
Wing Licb88add2022-10-29 02:32:06 +0100198 *state_id |= psci_make_local_state_id(PLAT_MAX_PWR_LEVEL + 1,
199 affinity_level);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200200
201 return ret;
202}
203
204static unsigned int tftf_psci_get_pstate_format(void)
205{
206 int ret;
207
208 ret = tftf_get_psci_feature_info(SMC_PSCI_CPU_SUSPEND);
209
210 /*
211 * If error is returned, then it probably means that the PSCI version
212 * is less than 1.0 and only the original format is supported. In case
213 * the version is 1.0 or higher, then PSCI FEATURES which is a
214 * mandatory API is not implemented which implies that only the
215 * original format is supported.
216 */
217 if (ret == PSCI_E_NOT_SUPPORTED)
218 return CPU_SUSPEND_FEAT_PSTATE_FORMAT_ORIGINAL;
219
220 /* Treat the invalid return value as PSCI FEATURES not supported */
221 if ((ret & ~CPU_SUSPEND_FEAT_VALID_MASK) != 0)
222 return CPU_SUSPEND_FEAT_PSTATE_FORMAT_ORIGINAL;
223
224 return (ret >> CPU_SUSPEND_FEAT_PSTATE_FORMAT_SHIFT) & 0x1;
225}
226
227/* Make the power state in the original format */
228uint32_t tftf_make_psci_pstate(uint32_t affinity_level,
229 uint32_t state_type,
230 uint32_t state_id)
231{
232 uint32_t power_state;
233
234 assert(psci_state_type_valid(state_type));
235
236 assert(pstate_format_detected);
237
238 if (pstate_format == CPU_SUSPEND_FEAT_PSTATE_FORMAT_EXTENDED) {
239 assert(psci_state_id_ext_valid(state_id));
240 power_state = (state_type << PSTATE_TYPE_SHIFT_EXT)
241 | (state_id << PSTATE_ID_SHIFT_EXT);
242 } else {
243 assert(psci_affinity_level_valid(affinity_level));
244 assert(psci_state_id_valid(state_id));
245 power_state = (affinity_level << PSTATE_AFF_LVL_SHIFT)
246 | (state_type << PSTATE_TYPE_SHIFT);
247 if (!is_state_id_null)
248 power_state |= (state_id << PSTATE_ID_SHIFT);
249 }
250
251 return power_state;
252}
253
254
255void tftf_detect_psci_pstate_format(void)
256{
257 uint32_t power_state;
258 unsigned int ret;
259
260 pstate_format = tftf_psci_get_pstate_format();
261
262 /*
263 * If the power state format is extended format, then the state-ID
264 * must use recommended encoding.
265 */
266 if (pstate_format == CPU_SUSPEND_FEAT_PSTATE_FORMAT_EXTENDED) {
267 pstate_format_detected = 1;
268 INFO("Extended PSCI power state format detected\n");
269 return;
270 }
271
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100272 tftf_irq_enable_sgi(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200273
274 /*
275 * Mask IRQ to prevent the interrupt handler being invoked
276 * and clearing the interrupt. A pending interrupt will cause this
277 * CPU to wake-up from suspend.
278 */
279 disable_irq();
280
281 /* Configure an SGI to wake-up from suspend */
282 tftf_send_sgi(IRQ_NS_SGI_0,
283 platform_get_core_pos(read_mpidr_el1() & MPID_MASK));
284
285
286 /*
287 * Try to detect if the platform uses NULL State-ID encoding by sending
288 * PSCI_SUSPEND call with the NULL State-ID encoding. If the call
289 * succeeds then the platform uses NULL State-ID encoding. Else it
290 * uses the recommended encoding for State-ID.
291 */
292 power_state = (PSTATE_AFF_LVL_0 << PSTATE_AFF_LVL_SHIFT)
293 | (PSTATE_TYPE_STANDBY << PSTATE_TYPE_SHIFT);
294
295 ret = tftf_cpu_suspend(power_state);
296
297 /* Unmask the IRQ to let the interrupt handler to execute */
298 enable_irq();
299 isb();
300
Boyan Karatotev6d144db2025-06-23 15:04:53 +0100301 tftf_irq_disable_sgi(IRQ_NS_SGI_0);
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200302
303 /*
304 * The NULL State-ID returned SUCCESS. Hence State-ID is NULL
305 * for the power state format.
306 */
307 if (ret == PSCI_E_SUCCESS) {
308 is_state_id_null = 1;
309 INFO("Original PSCI power state format with NULL State-ID detected\n");
310 } else
311 INFO("Original PSCI power state format detected\n");
312
313
314 pstate_format_detected = 1;
315}
316
317unsigned int tftf_is_psci_state_id_null(void)
318{
319 assert(pstate_format_detected);
320
321 if (pstate_format == CPU_SUSPEND_FEAT_PSTATE_FORMAT_ORIGINAL)
322 return is_state_id_null;
323 else
324 return 0; /* Extended state ID does not support null format */
325}
326
327unsigned int tftf_is_psci_pstate_format_original(void)
328{
329 assert(pstate_format_detected);
330
331 return pstate_format == CPU_SUSPEND_FEAT_PSTATE_FORMAT_ORIGINAL;
332}
333
334unsigned int tftf_get_psci_version(void)
335{
336 smc_args args = { SMC_PSCI_VERSION };
337 smc_ret_values ret;
338
339 ret = tftf_smc(&args);
340
341 return ret.ret0;
342}
343
344int tftf_is_valid_psci_version(unsigned int version)
345{
Imre Kis2fb75222025-08-06 16:26:23 +0200346 if (version != PSCI_VERSION(1, 3) &&
347 version != PSCI_VERSION(1, 2) &&
348 version != PSCI_VERSION(1, 1) &&
Sandrine Bailleux3cd87d72018-10-09 11:12:55 +0200349 version != PSCI_VERSION(1, 0) &&
350 version != PSCI_VERSION(0, 2) &&
351 version != PSCI_VERSION(0, 1)) {
352 return 0;
353 }
354 return 1;
355}