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