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.
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.
11 * Copyright (C) 2015 ARM Limited
14 #define pr_fmt(fmt) "psci: " fmt
16 #include <linux/errno.h>
17 #include <linux/linkage.h>
20 #include <linux/printk.h>
21 #include <linux/psci.h>
22 #include <linux/reboot.h>
23 #include <linux/suspend.h>
25 #include <uapi/linux/psci.h>
27 #include <asm/cputype.h>
28 #include <asm/system_misc.h>
29 #include <asm/smp_plat.h>
30 #include <asm/suspend.h>
33 * While a 64-bit OS can make calls with SMC32 calling conventions, for some
34 * calls it is necessary to use SMC64 to pass or return 64-bit values.
35 * For such calls PSCI_FN_NATIVE(version, name) will choose the appropriate
36 * (native-width) function ID.
39 #define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN64_##name
41 #define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN_##name
45 * The CPU any Trusted OS is resident on. The trusted OS may reject CPU_OFF
46 * calls to its resident CPU, so we must avoid issuing those. We never migrate
47 * a Trusted OS even if it claims to be capable of migration -- doing so will
48 * require cooperation with a Trusted OS driver.
50 static int resident_cpu
= -1;
52 bool psci_tos_resident_on(int cpu
)
54 return cpu
== resident_cpu
;
57 struct psci_operations psci_ops
;
59 typedef unsigned long (psci_fn
)(unsigned long, unsigned long,
60 unsigned long, unsigned long);
61 asmlinkage psci_fn __invoke_psci_fn_hvc
;
62 asmlinkage psci_fn __invoke_psci_fn_smc
;
63 static psci_fn
*invoke_psci_fn
;
73 static u32 psci_function_id
[PSCI_FN_MAX
];
75 #define PSCI_0_2_POWER_STATE_MASK \
76 (PSCI_0_2_POWER_STATE_ID_MASK | \
77 PSCI_0_2_POWER_STATE_TYPE_MASK | \
78 PSCI_0_2_POWER_STATE_AFFL_MASK)
80 #define PSCI_1_0_EXT_POWER_STATE_MASK \
81 (PSCI_1_0_EXT_POWER_STATE_ID_MASK | \
82 PSCI_1_0_EXT_POWER_STATE_TYPE_MASK)
84 static u32 psci_cpu_suspend_feature
;
86 static inline bool psci_has_ext_power_state(void)
88 return psci_cpu_suspend_feature
&
89 PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK
;
92 bool psci_power_state_loses_context(u32 state
)
94 const u32 mask
= psci_has_ext_power_state() ?
95 PSCI_1_0_EXT_POWER_STATE_TYPE_MASK
:
96 PSCI_0_2_POWER_STATE_TYPE_MASK
;
101 bool psci_power_state_is_valid(u32 state
)
103 const u32 valid_mask
= psci_has_ext_power_state() ?
104 PSCI_1_0_EXT_POWER_STATE_MASK
:
105 PSCI_0_2_POWER_STATE_MASK
;
107 return !(state
& ~valid_mask
);
110 static int psci_to_linux_errno(int errno
)
113 case PSCI_RET_SUCCESS
:
115 case PSCI_RET_NOT_SUPPORTED
:
117 case PSCI_RET_INVALID_PARAMS
:
118 case PSCI_RET_INVALID_ADDRESS
:
120 case PSCI_RET_DENIED
:
127 static u32
psci_get_version(void)
129 return invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION
, 0, 0, 0);
132 static int psci_cpu_suspend(u32 state
, unsigned long entry_point
)
137 fn
= psci_function_id
[PSCI_FN_CPU_SUSPEND
];
138 err
= invoke_psci_fn(fn
, state
, entry_point
, 0);
139 return psci_to_linux_errno(err
);
142 static int psci_cpu_off(u32 state
)
147 fn
= psci_function_id
[PSCI_FN_CPU_OFF
];
148 err
= invoke_psci_fn(fn
, state
, 0, 0);
149 return psci_to_linux_errno(err
);
152 static int psci_cpu_on(unsigned long cpuid
, unsigned long entry_point
)
157 fn
= psci_function_id
[PSCI_FN_CPU_ON
];
158 err
= invoke_psci_fn(fn
, cpuid
, entry_point
, 0);
159 return psci_to_linux_errno(err
);
162 static int psci_migrate(unsigned long cpuid
)
167 fn
= psci_function_id
[PSCI_FN_MIGRATE
];
168 err
= invoke_psci_fn(fn
, cpuid
, 0, 0);
169 return psci_to_linux_errno(err
);
172 static int psci_affinity_info(unsigned long target_affinity
,
173 unsigned long lowest_affinity_level
)
175 return invoke_psci_fn(PSCI_FN_NATIVE(0_2
, AFFINITY_INFO
),
176 target_affinity
, lowest_affinity_level
, 0);
179 static int psci_migrate_info_type(void)
181 return invoke_psci_fn(PSCI_0_2_FN_MIGRATE_INFO_TYPE
, 0, 0, 0);
184 static unsigned long psci_migrate_info_up_cpu(void)
186 return invoke_psci_fn(PSCI_FN_NATIVE(0_2
, MIGRATE_INFO_UP_CPU
),
190 static int get_set_conduit_method(struct device_node
*np
)
194 pr_info("probing for conduit method from DT.\n");
196 if (of_property_read_string(np
, "method", &method
)) {
197 pr_warn("missing \"method\" property\n");
201 if (!strcmp("hvc", method
)) {
202 invoke_psci_fn
= __invoke_psci_fn_hvc
;
203 } else if (!strcmp("smc", method
)) {
204 invoke_psci_fn
= __invoke_psci_fn_smc
;
206 pr_warn("invalid \"method\" property: %s\n", method
);
212 static void psci_sys_reset(enum reboot_mode reboot_mode
, const char *cmd
)
214 invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET
, 0, 0, 0);
217 static void psci_sys_poweroff(void)
219 invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF
, 0, 0, 0);
222 static int __init
psci_features(u32 psci_func_id
)
224 return invoke_psci_fn(PSCI_1_0_FN_PSCI_FEATURES
,
228 static int psci_system_suspend(unsigned long unused
)
230 return invoke_psci_fn(PSCI_FN_NATIVE(1_0
, SYSTEM_SUSPEND
),
231 virt_to_phys(cpu_resume
), 0, 0);
234 static int psci_system_suspend_enter(suspend_state_t state
)
236 return cpu_suspend(0, psci_system_suspend
);
239 static const struct platform_suspend_ops psci_suspend_ops
= {
240 .valid
= suspend_valid_only_mem
,
241 .enter
= psci_system_suspend_enter
,
244 static void __init
psci_init_system_suspend(void)
248 if (!IS_ENABLED(CONFIG_SUSPEND
))
251 ret
= psci_features(PSCI_FN_NATIVE(1_0
, SYSTEM_SUSPEND
));
253 if (ret
!= PSCI_RET_NOT_SUPPORTED
)
254 suspend_set_ops(&psci_suspend_ops
);
257 static void __init
psci_init_cpu_suspend(void)
259 int feature
= psci_features(psci_function_id
[PSCI_FN_CPU_SUSPEND
]);
261 if (feature
!= PSCI_RET_NOT_SUPPORTED
)
262 psci_cpu_suspend_feature
= feature
;
266 * Detect the presence of a resident Trusted OS which may cause CPU_OFF to
267 * return DENIED (which would be fatal).
269 static void __init
psci_init_migrate(void)
274 type
= psci_ops
.migrate_info_type();
276 if (type
== PSCI_0_2_TOS_MP
) {
277 pr_info("Trusted OS migration not required\n");
281 if (type
== PSCI_RET_NOT_SUPPORTED
) {
282 pr_info("MIGRATE_INFO_TYPE not supported.\n");
286 if (type
!= PSCI_0_2_TOS_UP_MIGRATE
&&
287 type
!= PSCI_0_2_TOS_UP_NO_MIGRATE
) {
288 pr_err("MIGRATE_INFO_TYPE returned unknown type (%d)\n", type
);
292 cpuid
= psci_migrate_info_up_cpu();
293 if (cpuid
& ~MPIDR_HWID_BITMASK
) {
294 pr_warn("MIGRATE_INFO_UP_CPU reported invalid physical ID (0x%lx)\n",
299 cpu
= get_logical_index(cpuid
);
300 resident_cpu
= cpu
>= 0 ? cpu
: -1;
302 pr_info("Trusted OS resident on physical CPU 0x%lx\n", cpuid
);
305 static void __init
psci_0_2_set_functions(void)
307 pr_info("Using standard PSCI v0.2 function IDs\n");
308 psci_function_id
[PSCI_FN_CPU_SUSPEND
] =
309 PSCI_FN_NATIVE(0_2
, CPU_SUSPEND
);
310 psci_ops
.cpu_suspend
= psci_cpu_suspend
;
312 psci_function_id
[PSCI_FN_CPU_OFF
] = PSCI_0_2_FN_CPU_OFF
;
313 psci_ops
.cpu_off
= psci_cpu_off
;
315 psci_function_id
[PSCI_FN_CPU_ON
] = PSCI_FN_NATIVE(0_2
, CPU_ON
);
316 psci_ops
.cpu_on
= psci_cpu_on
;
318 psci_function_id
[PSCI_FN_MIGRATE
] = PSCI_FN_NATIVE(0_2
, MIGRATE
);
319 psci_ops
.migrate
= psci_migrate
;
321 psci_ops
.affinity_info
= psci_affinity_info
;
323 psci_ops
.migrate_info_type
= psci_migrate_info_type
;
325 arm_pm_restart
= psci_sys_reset
;
327 pm_power_off
= psci_sys_poweroff
;
331 * Probe function for PSCI firmware versions >= 0.2
333 static int __init
psci_probe(void)
335 u32 ver
= psci_get_version();
337 pr_info("PSCIv%d.%d detected in firmware.\n",
338 PSCI_VERSION_MAJOR(ver
),
339 PSCI_VERSION_MINOR(ver
));
341 if (PSCI_VERSION_MAJOR(ver
) == 0 && PSCI_VERSION_MINOR(ver
) < 2) {
342 pr_err("Conflicting PSCI version detected.\n");
346 psci_0_2_set_functions();
350 if (PSCI_VERSION_MAJOR(ver
) >= 1) {
351 psci_init_cpu_suspend();
352 psci_init_system_suspend();
358 typedef int (*psci_initcall_t
)(const struct device_node
*);
361 * PSCI init function for PSCI versions >=0.2
363 * Probe based on PSCI PSCI_VERSION function
365 static int __init
psci_0_2_init(struct device_node
*np
)
369 err
= get_set_conduit_method(np
);
374 * Starting with v0.2, the PSCI specification introduced a call
375 * (PSCI_VERSION) that allows probing the firmware version, so
376 * that PSCI function IDs and version specific initialization
377 * can be carried out according to the specific version reported
388 * PSCI < v0.2 get PSCI Function IDs via DT.
390 static int __init
psci_0_1_init(struct device_node
*np
)
395 err
= get_set_conduit_method(np
);
400 pr_info("Using PSCI v0.1 Function IDs from DT\n");
402 if (!of_property_read_u32(np
, "cpu_suspend", &id
)) {
403 psci_function_id
[PSCI_FN_CPU_SUSPEND
] = id
;
404 psci_ops
.cpu_suspend
= psci_cpu_suspend
;
407 if (!of_property_read_u32(np
, "cpu_off", &id
)) {
408 psci_function_id
[PSCI_FN_CPU_OFF
] = id
;
409 psci_ops
.cpu_off
= psci_cpu_off
;
412 if (!of_property_read_u32(np
, "cpu_on", &id
)) {
413 psci_function_id
[PSCI_FN_CPU_ON
] = id
;
414 psci_ops
.cpu_on
= psci_cpu_on
;
417 if (!of_property_read_u32(np
, "migrate", &id
)) {
418 psci_function_id
[PSCI_FN_MIGRATE
] = id
;
419 psci_ops
.migrate
= psci_migrate
;
427 static const struct of_device_id
const psci_of_match
[] __initconst
= {
428 { .compatible
= "arm,psci", .data
= psci_0_1_init
},
429 { .compatible
= "arm,psci-0.2", .data
= psci_0_2_init
},
430 { .compatible
= "arm,psci-1.0", .data
= psci_0_2_init
},
434 int __init
psci_dt_init(void)
436 struct device_node
*np
;
437 const struct of_device_id
*matched_np
;
438 psci_initcall_t init_fn
;
440 np
= of_find_matching_node_and_match(NULL
, psci_of_match
, &matched_np
);
445 init_fn
= (psci_initcall_t
)matched_np
->data
;
451 * We use PSCI 0.2+ when ACPI is deployed on ARM64 and it's
452 * explicitly clarified in SBBR
454 int __init
psci_acpi_init(void)
456 if (!acpi_psci_present()) {
457 pr_info("is not implemented in ACPI.\n");
461 pr_info("probing for conduit method from ACPI.\n");
463 if (acpi_psci_use_hvc())
464 invoke_psci_fn
= __invoke_psci_fn_hvc
;
466 invoke_psci_fn
= __invoke_psci_fn_smc
;