drivers: firmware: psci: add PSCI_FEATURES call
[deliverable/linux.git] / drivers / firmware / psci.c
CommitLineData
bff60792
MR
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License version 2 as
4 * published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 *
11 * Copyright (C) 2015 ARM Limited
12 */
13
14#define pr_fmt(fmt) "psci: " fmt
15
16#include <linux/errno.h>
17#include <linux/linkage.h>
18#include <linux/of.h>
19#include <linux/pm.h>
20#include <linux/printk.h>
21#include <linux/psci.h>
22#include <linux/reboot.h>
23
24#include <uapi/linux/psci.h>
25
26#include <asm/cputype.h>
27#include <asm/system_misc.h>
28#include <asm/smp_plat.h>
29
5211df00
MR
30/*
31 * While a 64-bit OS can make calls with SMC32 calling conventions, for some
32 * calls it is necessary to use SMC64 to pass or return 64-bit values. For such
33 * calls PSCI_0_2_FN_NATIVE(x) will choose the appropriate (native-width)
34 * function ID.
35 */
36#ifdef CONFIG_64BIT
37#define PSCI_0_2_FN_NATIVE(name) PSCI_0_2_FN64_##name
38#else
39#define PSCI_0_2_FN_NATIVE(name) PSCI_0_2_FN_##name
40#endif
41
bff60792
MR
42/*
43 * The CPU any Trusted OS is resident on. The trusted OS may reject CPU_OFF
44 * calls to its resident CPU, so we must avoid issuing those. We never migrate
45 * a Trusted OS even if it claims to be capable of migration -- doing so will
46 * require cooperation with a Trusted OS driver.
47 */
48static int resident_cpu = -1;
49
50bool psci_tos_resident_on(int cpu)
51{
52 return cpu == resident_cpu;
53}
54
55struct psci_operations psci_ops;
56
57typedef unsigned long (psci_fn)(unsigned long, unsigned long,
58 unsigned long, unsigned long);
59asmlinkage psci_fn __invoke_psci_fn_hvc;
60asmlinkage psci_fn __invoke_psci_fn_smc;
61static psci_fn *invoke_psci_fn;
62
63enum psci_function {
64 PSCI_FN_CPU_SUSPEND,
65 PSCI_FN_CPU_ON,
66 PSCI_FN_CPU_OFF,
67 PSCI_FN_MIGRATE,
68 PSCI_FN_MAX,
69};
70
71static u32 psci_function_id[PSCI_FN_MAX];
72
068654c2
LP
73#define PSCI_0_2_POWER_STATE_MASK \
74 (PSCI_0_2_POWER_STATE_ID_MASK | \
75 PSCI_0_2_POWER_STATE_TYPE_MASK | \
76 PSCI_0_2_POWER_STATE_AFFL_MASK)
77
78bool psci_power_state_loses_context(u32 state)
79{
80 return state & PSCI_0_2_POWER_STATE_TYPE_MASK;
81}
82
83bool psci_power_state_is_valid(u32 state)
84{
85 return !(state & ~PSCI_0_2_POWER_STATE_MASK);
86}
87
bff60792
MR
88static int psci_to_linux_errno(int errno)
89{
90 switch (errno) {
91 case PSCI_RET_SUCCESS:
92 return 0;
93 case PSCI_RET_NOT_SUPPORTED:
94 return -EOPNOTSUPP;
95 case PSCI_RET_INVALID_PARAMS:
2217d7c6 96 case PSCI_RET_INVALID_ADDRESS:
bff60792
MR
97 return -EINVAL;
98 case PSCI_RET_DENIED:
99 return -EPERM;
100 };
101
102 return -EINVAL;
103}
104
105static u32 psci_get_version(void)
106{
107 return invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0);
108}
109
110static int psci_cpu_suspend(u32 state, unsigned long entry_point)
111{
112 int err;
113 u32 fn;
114
115 fn = psci_function_id[PSCI_FN_CPU_SUSPEND];
116 err = invoke_psci_fn(fn, state, entry_point, 0);
117 return psci_to_linux_errno(err);
118}
119
120static int psci_cpu_off(u32 state)
121{
122 int err;
123 u32 fn;
124
125 fn = psci_function_id[PSCI_FN_CPU_OFF];
126 err = invoke_psci_fn(fn, state, 0, 0);
127 return psci_to_linux_errno(err);
128}
129
130static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)
131{
132 int err;
133 u32 fn;
134
135 fn = psci_function_id[PSCI_FN_CPU_ON];
136 err = invoke_psci_fn(fn, cpuid, entry_point, 0);
137 return psci_to_linux_errno(err);
138}
139
140static int psci_migrate(unsigned long cpuid)
141{
142 int err;
143 u32 fn;
144
145 fn = psci_function_id[PSCI_FN_MIGRATE];
146 err = invoke_psci_fn(fn, cpuid, 0, 0);
147 return psci_to_linux_errno(err);
148}
149
150static int psci_affinity_info(unsigned long target_affinity,
151 unsigned long lowest_affinity_level)
152{
5211df00
MR
153 return invoke_psci_fn(PSCI_0_2_FN_NATIVE(AFFINITY_INFO),
154 target_affinity, lowest_affinity_level, 0);
bff60792
MR
155}
156
157static int psci_migrate_info_type(void)
158{
159 return invoke_psci_fn(PSCI_0_2_FN_MIGRATE_INFO_TYPE, 0, 0, 0);
160}
161
162static unsigned long psci_migrate_info_up_cpu(void)
163{
5211df00
MR
164 return invoke_psci_fn(PSCI_0_2_FN_NATIVE(MIGRATE_INFO_UP_CPU),
165 0, 0, 0);
bff60792
MR
166}
167
168static int get_set_conduit_method(struct device_node *np)
169{
170 const char *method;
171
172 pr_info("probing for conduit method from DT.\n");
173
174 if (of_property_read_string(np, "method", &method)) {
175 pr_warn("missing \"method\" property\n");
176 return -ENXIO;
177 }
178
179 if (!strcmp("hvc", method)) {
180 invoke_psci_fn = __invoke_psci_fn_hvc;
181 } else if (!strcmp("smc", method)) {
182 invoke_psci_fn = __invoke_psci_fn_smc;
183 } else {
184 pr_warn("invalid \"method\" property: %s\n", method);
185 return -EINVAL;
186 }
187 return 0;
188}
189
190static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd)
191{
192 invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0);
193}
194
195static void psci_sys_poweroff(void)
196{
197 invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
198}
199
5f004e0c
LP
200static int __init psci_features(u32 psci_func_id)
201{
202 return invoke_psci_fn(PSCI_1_0_FN_PSCI_FEATURES,
203 psci_func_id, 0, 0);
204}
205
bff60792
MR
206/*
207 * Detect the presence of a resident Trusted OS which may cause CPU_OFF to
208 * return DENIED (which would be fatal).
209 */
210static void __init psci_init_migrate(void)
211{
212 unsigned long cpuid;
213 int type, cpu = -1;
214
215 type = psci_ops.migrate_info_type();
216
217 if (type == PSCI_0_2_TOS_MP) {
218 pr_info("Trusted OS migration not required\n");
219 return;
220 }
221
222 if (type == PSCI_RET_NOT_SUPPORTED) {
223 pr_info("MIGRATE_INFO_TYPE not supported.\n");
224 return;
225 }
226
227 if (type != PSCI_0_2_TOS_UP_MIGRATE &&
228 type != PSCI_0_2_TOS_UP_NO_MIGRATE) {
229 pr_err("MIGRATE_INFO_TYPE returned unknown type (%d)\n", type);
230 return;
231 }
232
233 cpuid = psci_migrate_info_up_cpu();
234 if (cpuid & ~MPIDR_HWID_BITMASK) {
235 pr_warn("MIGRATE_INFO_UP_CPU reported invalid physical ID (0x%lx)\n",
236 cpuid);
237 return;
238 }
239
240 cpu = get_logical_index(cpuid);
241 resident_cpu = cpu >= 0 ? cpu : -1;
242
243 pr_info("Trusted OS resident on physical CPU 0x%lx\n", cpuid);
244}
245
246static void __init psci_0_2_set_functions(void)
247{
248 pr_info("Using standard PSCI v0.2 function IDs\n");
5211df00 249 psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN_NATIVE(CPU_SUSPEND);
bff60792
MR
250 psci_ops.cpu_suspend = psci_cpu_suspend;
251
252 psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
253 psci_ops.cpu_off = psci_cpu_off;
254
5211df00 255 psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN_NATIVE(CPU_ON);
bff60792
MR
256 psci_ops.cpu_on = psci_cpu_on;
257
5211df00 258 psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN_NATIVE(MIGRATE);
bff60792
MR
259 psci_ops.migrate = psci_migrate;
260
261 psci_ops.affinity_info = psci_affinity_info;
262
263 psci_ops.migrate_info_type = psci_migrate_info_type;
264
265 arm_pm_restart = psci_sys_reset;
266
267 pm_power_off = psci_sys_poweroff;
268}
269
270/*
271 * Probe function for PSCI firmware versions >= 0.2
272 */
273static int __init psci_probe(void)
274{
275 u32 ver = psci_get_version();
276
277 pr_info("PSCIv%d.%d detected in firmware.\n",
278 PSCI_VERSION_MAJOR(ver),
279 PSCI_VERSION_MINOR(ver));
280
281 if (PSCI_VERSION_MAJOR(ver) == 0 && PSCI_VERSION_MINOR(ver) < 2) {
282 pr_err("Conflicting PSCI version detected.\n");
283 return -EINVAL;
284 }
285
286 psci_0_2_set_functions();
287
288 psci_init_migrate();
289
290 return 0;
291}
292
293typedef int (*psci_initcall_t)(const struct device_node *);
294
295/*
296 * PSCI init function for PSCI versions >=0.2
297 *
298 * Probe based on PSCI PSCI_VERSION function
299 */
300static int __init psci_0_2_init(struct device_node *np)
301{
302 int err;
303
304 err = get_set_conduit_method(np);
305
306 if (err)
307 goto out_put_node;
308 /*
309 * Starting with v0.2, the PSCI specification introduced a call
310 * (PSCI_VERSION) that allows probing the firmware version, so
311 * that PSCI function IDs and version specific initialization
312 * can be carried out according to the specific version reported
313 * by firmware
314 */
315 err = psci_probe();
316
317out_put_node:
318 of_node_put(np);
319 return err;
320}
321
322/*
323 * PSCI < v0.2 get PSCI Function IDs via DT.
324 */
325static int __init psci_0_1_init(struct device_node *np)
326{
327 u32 id;
328 int err;
329
330 err = get_set_conduit_method(np);
331
332 if (err)
333 goto out_put_node;
334
335 pr_info("Using PSCI v0.1 Function IDs from DT\n");
336
337 if (!of_property_read_u32(np, "cpu_suspend", &id)) {
338 psci_function_id[PSCI_FN_CPU_SUSPEND] = id;
339 psci_ops.cpu_suspend = psci_cpu_suspend;
340 }
341
342 if (!of_property_read_u32(np, "cpu_off", &id)) {
343 psci_function_id[PSCI_FN_CPU_OFF] = id;
344 psci_ops.cpu_off = psci_cpu_off;
345 }
346
347 if (!of_property_read_u32(np, "cpu_on", &id)) {
348 psci_function_id[PSCI_FN_CPU_ON] = id;
349 psci_ops.cpu_on = psci_cpu_on;
350 }
351
352 if (!of_property_read_u32(np, "migrate", &id)) {
353 psci_function_id[PSCI_FN_MIGRATE] = id;
354 psci_ops.migrate = psci_migrate;
355 }
356
357out_put_node:
358 of_node_put(np);
359 return err;
360}
361
c706c7eb 362static const struct of_device_id const psci_of_match[] __initconst = {
bff60792
MR
363 { .compatible = "arm,psci", .data = psci_0_1_init},
364 { .compatible = "arm,psci-0.2", .data = psci_0_2_init},
365 {},
366};
367
368int __init psci_dt_init(void)
369{
370 struct device_node *np;
371 const struct of_device_id *matched_np;
372 psci_initcall_t init_fn;
373
374 np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np);
375
376 if (!np)
377 return -ENODEV;
378
379 init_fn = (psci_initcall_t)matched_np->data;
380 return init_fn(np);
381}
382
383#ifdef CONFIG_ACPI
384/*
385 * We use PSCI 0.2+ when ACPI is deployed on ARM64 and it's
386 * explicitly clarified in SBBR
387 */
388int __init psci_acpi_init(void)
389{
390 if (!acpi_psci_present()) {
391 pr_info("is not implemented in ACPI.\n");
392 return -EOPNOTSUPP;
393 }
394
395 pr_info("probing for conduit method from ACPI.\n");
396
397 if (acpi_psci_use_hvc())
398 invoke_psci_fn = __invoke_psci_fn_hvc;
399 else
400 invoke_psci_fn = __invoke_psci_fn_smc;
401
402 return psci_probe();
403}
404#endif
This page took 0.046725 seconds and 5 git commands to generate.