blob: 857b01e5f43317224748f38ffa7db11dafffeb31 [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
84
85u_register_t tftf_psci_stat_residency(u_register_t target_cpu,
86 uint32_t power_state)
87{
88 smc_args args = {
89 SMC_PSCI_STAT_RESIDENCY,
90 target_cpu,
91 power_state,
92 };
93 smc_ret_values ret_vals;
94
95 ret_vals = tftf_smc(&args);
96 return ret_vals.ret0;
97}
98
99u_register_t tftf_psci_stat_count(u_register_t target_cpu,
100 uint32_t power_state)
101{
102 smc_args args = {
103 SMC_PSCI_STAT_COUNT,
104 target_cpu,
105 power_state,
106 };
107 smc_ret_values ret_vals;
108
109 ret_vals = tftf_smc(&args);
110 return ret_vals.ret0;
111}
112
113int32_t tftf_psci_affinity_info(u_register_t target_affinity,
114 uint32_t lowest_affinity_level)
115{
116 smc_ret_values ret_vals;
117
118 smc_args args = {
119 SMC_PSCI_AFFINITY_INFO,
120 target_affinity,
121 lowest_affinity_level
122 };
123
124 ret_vals = tftf_smc(&args);
125 return ret_vals.ret0;
126}
127
128int32_t tftf_psci_node_hw_state(u_register_t target_cpu, uint32_t power_level)
129{
130 smc_args args = {
131 SMC_PSCI_CPU_HW_STATE,
132 target_cpu,
133 power_level
134 };
135 smc_ret_values ret;
136
137 ret = tftf_smc(&args);
138 return ret.ret0;
139}
140
141int32_t tftf_get_psci_feature_info(uint32_t psci_func_id)
142{
143 smc_args args = {
144 SMC_PSCI_FEATURES,
145 psci_func_id
146 };
147 smc_ret_values ret;
148
149 ret = tftf_smc(&args);
150 return ret.ret0;
151}
152
153int tftf_psci_make_composite_state_id(uint32_t affinity_level,
154 uint32_t state_type, uint32_t *state_id)
155{
156 unsigned int found_entry, i;
157 int ret = PSCI_E_SUCCESS;
158 const plat_state_prop_t *state_prop;
159
160 assert(state_id);
161
162 *state_id = 0;
163 for (i = 0; i <= affinity_level; i++) {
164 state_prop = plat_get_state_prop(i);
165 if (!state_prop) {
166 *state_id |= psci_make_local_state_id(i,
167 PLAT_PSCI_DUMMY_STATE_ID);
168 ret = PSCI_E_INVALID_PARAMS;
169 continue;
170 }
171 found_entry = 0;
172
173 while (state_prop->state_ID) {
174 if (state_type == state_prop->is_pwrdown) {
175 *state_id |= psci_make_local_state_id(i,
176 state_prop->state_ID);
177 found_entry = 1;
178 break;
179 }
180 state_prop++;
181 }
182 if (!found_entry) {
183 *state_id |= psci_make_local_state_id(i,
184 PLAT_PSCI_DUMMY_STATE_ID);
185 ret = PSCI_E_INVALID_PARAMS;
186 }
187 }
188
189 return ret;
190}
191
192static unsigned int tftf_psci_get_pstate_format(void)
193{
194 int ret;
195
196 ret = tftf_get_psci_feature_info(SMC_PSCI_CPU_SUSPEND);
197
198 /*
199 * If error is returned, then it probably means that the PSCI version
200 * is less than 1.0 and only the original format is supported. In case
201 * the version is 1.0 or higher, then PSCI FEATURES which is a
202 * mandatory API is not implemented which implies that only the
203 * original format is supported.
204 */
205 if (ret == PSCI_E_NOT_SUPPORTED)
206 return CPU_SUSPEND_FEAT_PSTATE_FORMAT_ORIGINAL;
207
208 /* Treat the invalid return value as PSCI FEATURES not supported */
209 if ((ret & ~CPU_SUSPEND_FEAT_VALID_MASK) != 0)
210 return CPU_SUSPEND_FEAT_PSTATE_FORMAT_ORIGINAL;
211
212 return (ret >> CPU_SUSPEND_FEAT_PSTATE_FORMAT_SHIFT) & 0x1;
213}
214
215/* Make the power state in the original format */
216uint32_t tftf_make_psci_pstate(uint32_t affinity_level,
217 uint32_t state_type,
218 uint32_t state_id)
219{
220 uint32_t power_state;
221
222 assert(psci_state_type_valid(state_type));
223
224 assert(pstate_format_detected);
225
226 if (pstate_format == CPU_SUSPEND_FEAT_PSTATE_FORMAT_EXTENDED) {
227 assert(psci_state_id_ext_valid(state_id));
228 power_state = (state_type << PSTATE_TYPE_SHIFT_EXT)
229 | (state_id << PSTATE_ID_SHIFT_EXT);
230 } else {
231 assert(psci_affinity_level_valid(affinity_level));
232 assert(psci_state_id_valid(state_id));
233 power_state = (affinity_level << PSTATE_AFF_LVL_SHIFT)
234 | (state_type << PSTATE_TYPE_SHIFT);
235 if (!is_state_id_null)
236 power_state |= (state_id << PSTATE_ID_SHIFT);
237 }
238
239 return power_state;
240}
241
242
243void tftf_detect_psci_pstate_format(void)
244{
245 uint32_t power_state;
246 unsigned int ret;
247
248 pstate_format = tftf_psci_get_pstate_format();
249
250 /*
251 * If the power state format is extended format, then the state-ID
252 * must use recommended encoding.
253 */
254 if (pstate_format == CPU_SUSPEND_FEAT_PSTATE_FORMAT_EXTENDED) {
255 pstate_format_detected = 1;
256 INFO("Extended PSCI power state format detected\n");
257 return;
258 }
259
260 tftf_irq_enable(IRQ_NS_SGI_0, GIC_HIGHEST_NS_PRIORITY);
261
262 /*
263 * Mask IRQ to prevent the interrupt handler being invoked
264 * and clearing the interrupt. A pending interrupt will cause this
265 * CPU to wake-up from suspend.
266 */
267 disable_irq();
268
269 /* Configure an SGI to wake-up from suspend */
270 tftf_send_sgi(IRQ_NS_SGI_0,
271 platform_get_core_pos(read_mpidr_el1() & MPID_MASK));
272
273
274 /*
275 * Try to detect if the platform uses NULL State-ID encoding by sending
276 * PSCI_SUSPEND call with the NULL State-ID encoding. If the call
277 * succeeds then the platform uses NULL State-ID encoding. Else it
278 * uses the recommended encoding for State-ID.
279 */
280 power_state = (PSTATE_AFF_LVL_0 << PSTATE_AFF_LVL_SHIFT)
281 | (PSTATE_TYPE_STANDBY << PSTATE_TYPE_SHIFT);
282
283 ret = tftf_cpu_suspend(power_state);
284
285 /* Unmask the IRQ to let the interrupt handler to execute */
286 enable_irq();
287 isb();
288
289 tftf_irq_disable(IRQ_NS_SGI_0);
290
291 /*
292 * The NULL State-ID returned SUCCESS. Hence State-ID is NULL
293 * for the power state format.
294 */
295 if (ret == PSCI_E_SUCCESS) {
296 is_state_id_null = 1;
297 INFO("Original PSCI power state format with NULL State-ID detected\n");
298 } else
299 INFO("Original PSCI power state format detected\n");
300
301
302 pstate_format_detected = 1;
303}
304
305unsigned int tftf_is_psci_state_id_null(void)
306{
307 assert(pstate_format_detected);
308
309 if (pstate_format == CPU_SUSPEND_FEAT_PSTATE_FORMAT_ORIGINAL)
310 return is_state_id_null;
311 else
312 return 0; /* Extended state ID does not support null format */
313}
314
315unsigned int tftf_is_psci_pstate_format_original(void)
316{
317 assert(pstate_format_detected);
318
319 return pstate_format == CPU_SUSPEND_FEAT_PSTATE_FORMAT_ORIGINAL;
320}
321
322unsigned int tftf_get_psci_version(void)
323{
324 smc_args args = { SMC_PSCI_VERSION };
325 smc_ret_values ret;
326
327 ret = tftf_smc(&args);
328
329 return ret.ret0;
330}
331
332int tftf_is_valid_psci_version(unsigned int version)
333{
334 if (version != PSCI_VERSION(1, 1) &&
335 version != PSCI_VERSION(1, 0) &&
336 version != PSCI_VERSION(0, 2) &&
337 version != PSCI_VERSION(0, 1)) {
338 return 0;
339 }
340 return 1;
341}