aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/firmware/psci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/firmware/psci.c')
-rw-r--r--drivers/firmware/psci.c108
1 files changed, 98 insertions, 10 deletions
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index 42700f09a8c5..d24f35d74b27 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -20,23 +20,25 @@
20#include <linux/printk.h> 20#include <linux/printk.h>
21#include <linux/psci.h> 21#include <linux/psci.h>
22#include <linux/reboot.h> 22#include <linux/reboot.h>
23#include <linux/suspend.h>
23 24
24#include <uapi/linux/psci.h> 25#include <uapi/linux/psci.h>
25 26
26#include <asm/cputype.h> 27#include <asm/cputype.h>
27#include <asm/system_misc.h> 28#include <asm/system_misc.h>
28#include <asm/smp_plat.h> 29#include <asm/smp_plat.h>
30#include <asm/suspend.h>
29 31
30/* 32/*
31 * While a 64-bit OS can make calls with SMC32 calling conventions, for some 33 * 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 34 * calls it is necessary to use SMC64 to pass or return 64-bit values.
33 * calls PSCI_0_2_FN_NATIVE(x) will choose the appropriate (native-width) 35 * For such calls PSCI_FN_NATIVE(version, name) will choose the appropriate
34 * function ID. 36 * (native-width) function ID.
35 */ 37 */
36#ifdef CONFIG_64BIT 38#ifdef CONFIG_64BIT
37#define PSCI_0_2_FN_NATIVE(name) PSCI_0_2_FN64_##name 39#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN64_##name
38#else 40#else
39#define PSCI_0_2_FN_NATIVE(name) PSCI_0_2_FN_##name 41#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN_##name
40#endif 42#endif
41 43
42/* 44/*
@@ -70,6 +72,41 @@ enum psci_function {
70 72
71static u32 psci_function_id[PSCI_FN_MAX]; 73static u32 psci_function_id[PSCI_FN_MAX];
72 74
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)
79
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)
83
84static u32 psci_cpu_suspend_feature;
85
86static inline bool psci_has_ext_power_state(void)
87{
88 return psci_cpu_suspend_feature &
89 PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK;
90}
91
92bool psci_power_state_loses_context(u32 state)
93{
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;
97
98 return state & mask;
99}
100
101bool psci_power_state_is_valid(u32 state)
102{
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;
106
107 return !(state & ~valid_mask);
108}
109
73static int psci_to_linux_errno(int errno) 110static int psci_to_linux_errno(int errno)
74{ 111{
75 switch (errno) { 112 switch (errno) {
@@ -78,6 +115,7 @@ static int psci_to_linux_errno(int errno)
78 case PSCI_RET_NOT_SUPPORTED: 115 case PSCI_RET_NOT_SUPPORTED:
79 return -EOPNOTSUPP; 116 return -EOPNOTSUPP;
80 case PSCI_RET_INVALID_PARAMS: 117 case PSCI_RET_INVALID_PARAMS:
118 case PSCI_RET_INVALID_ADDRESS:
81 return -EINVAL; 119 return -EINVAL;
82 case PSCI_RET_DENIED: 120 case PSCI_RET_DENIED:
83 return -EPERM; 121 return -EPERM;
@@ -134,7 +172,7 @@ static int psci_migrate(unsigned long cpuid)
134static int psci_affinity_info(unsigned long target_affinity, 172static int psci_affinity_info(unsigned long target_affinity,
135 unsigned long lowest_affinity_level) 173 unsigned long lowest_affinity_level)
136{ 174{
137 return invoke_psci_fn(PSCI_0_2_FN_NATIVE(AFFINITY_INFO), 175 return invoke_psci_fn(PSCI_FN_NATIVE(0_2, AFFINITY_INFO),
138 target_affinity, lowest_affinity_level, 0); 176 target_affinity, lowest_affinity_level, 0);
139} 177}
140 178
@@ -145,7 +183,7 @@ static int psci_migrate_info_type(void)
145 183
146static unsigned long psci_migrate_info_up_cpu(void) 184static unsigned long psci_migrate_info_up_cpu(void)
147{ 185{
148 return invoke_psci_fn(PSCI_0_2_FN_NATIVE(MIGRATE_INFO_UP_CPU), 186 return invoke_psci_fn(PSCI_FN_NATIVE(0_2, MIGRATE_INFO_UP_CPU),
149 0, 0, 0); 187 0, 0, 0);
150} 188}
151 189
@@ -181,6 +219,49 @@ static void psci_sys_poweroff(void)
181 invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); 219 invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0);
182} 220}
183 221
222static int __init psci_features(u32 psci_func_id)
223{
224 return invoke_psci_fn(PSCI_1_0_FN_PSCI_FEATURES,
225 psci_func_id, 0, 0);
226}
227
228static int psci_system_suspend(unsigned long unused)
229{
230 return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND),
231 virt_to_phys(cpu_resume), 0, 0);
232}
233
234static int psci_system_suspend_enter(suspend_state_t state)
235{
236 return cpu_suspend(0, psci_system_suspend);
237}
238
239static const struct platform_suspend_ops psci_suspend_ops = {
240 .valid = suspend_valid_only_mem,
241 .enter = psci_system_suspend_enter,
242};
243
244static void __init psci_init_system_suspend(void)
245{
246 int ret;
247
248 if (!IS_ENABLED(CONFIG_SUSPEND))
249 return;
250
251 ret = psci_features(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND));
252
253 if (ret != PSCI_RET_NOT_SUPPORTED)
254 suspend_set_ops(&psci_suspend_ops);
255}
256
257static void __init psci_init_cpu_suspend(void)
258{
259 int feature = psci_features(psci_function_id[PSCI_FN_CPU_SUSPEND]);
260
261 if (feature != PSCI_RET_NOT_SUPPORTED)
262 psci_cpu_suspend_feature = feature;
263}
264
184/* 265/*
185 * Detect the presence of a resident Trusted OS which may cause CPU_OFF to 266 * Detect the presence of a resident Trusted OS which may cause CPU_OFF to
186 * return DENIED (which would be fatal). 267 * return DENIED (which would be fatal).
@@ -224,16 +305,17 @@ static void __init psci_init_migrate(void)
224static void __init psci_0_2_set_functions(void) 305static void __init psci_0_2_set_functions(void)
225{ 306{
226 pr_info("Using standard PSCI v0.2 function IDs\n"); 307 pr_info("Using standard PSCI v0.2 function IDs\n");
227 psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN_NATIVE(CPU_SUSPEND); 308 psci_function_id[PSCI_FN_CPU_SUSPEND] =
309 PSCI_FN_NATIVE(0_2, CPU_SUSPEND);
228 psci_ops.cpu_suspend = psci_cpu_suspend; 310 psci_ops.cpu_suspend = psci_cpu_suspend;
229 311
230 psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF; 312 psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF;
231 psci_ops.cpu_off = psci_cpu_off; 313 psci_ops.cpu_off = psci_cpu_off;
232 314
233 psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN_NATIVE(CPU_ON); 315 psci_function_id[PSCI_FN_CPU_ON] = PSCI_FN_NATIVE(0_2, CPU_ON);
234 psci_ops.cpu_on = psci_cpu_on; 316 psci_ops.cpu_on = psci_cpu_on;
235 317
236 psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN_NATIVE(MIGRATE); 318 psci_function_id[PSCI_FN_MIGRATE] = PSCI_FN_NATIVE(0_2, MIGRATE);
237 psci_ops.migrate = psci_migrate; 319 psci_ops.migrate = psci_migrate;
238 320
239 psci_ops.affinity_info = psci_affinity_info; 321 psci_ops.affinity_info = psci_affinity_info;
@@ -265,6 +347,11 @@ static int __init psci_probe(void)
265 347
266 psci_init_migrate(); 348 psci_init_migrate();
267 349
350 if (PSCI_VERSION_MAJOR(ver) >= 1) {
351 psci_init_cpu_suspend();
352 psci_init_system_suspend();
353 }
354
268 return 0; 355 return 0;
269} 356}
270 357
@@ -340,6 +427,7 @@ out_put_node:
340static const struct of_device_id const psci_of_match[] __initconst = { 427static const struct of_device_id const psci_of_match[] __initconst = {
341 { .compatible = "arm,psci", .data = psci_0_1_init}, 428 { .compatible = "arm,psci", .data = psci_0_1_init},
342 { .compatible = "arm,psci-0.2", .data = psci_0_2_init}, 429 { .compatible = "arm,psci-0.2", .data = psci_0_2_init},
430 { .compatible = "arm,psci-1.0", .data = psci_0_2_init},
343 {}, 431 {},
344}; 432};
345 433