diff options
Diffstat (limited to 'drivers/firmware/psci.c')
-rw-r--r-- | drivers/firmware/psci.c | 108 |
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 | ||
71 | static u32 psci_function_id[PSCI_FN_MAX]; | 73 | static 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 | |||
84 | static u32 psci_cpu_suspend_feature; | ||
85 | |||
86 | static 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 | |||
92 | bool 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 | |||
101 | bool 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 | |||
73 | static int psci_to_linux_errno(int errno) | 110 | static 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) | |||
134 | static int psci_affinity_info(unsigned long target_affinity, | 172 | static 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 | ||
146 | static unsigned long psci_migrate_info_up_cpu(void) | 184 | static 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 | ||
222 | static 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 | |||
228 | static 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 | |||
234 | static int psci_system_suspend_enter(suspend_state_t state) | ||
235 | { | ||
236 | return cpu_suspend(0, psci_system_suspend); | ||
237 | } | ||
238 | |||
239 | static const struct platform_suspend_ops psci_suspend_ops = { | ||
240 | .valid = suspend_valid_only_mem, | ||
241 | .enter = psci_system_suspend_enter, | ||
242 | }; | ||
243 | |||
244 | static 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 | |||
257 | static 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) | |||
224 | static void __init psci_0_2_set_functions(void) | 305 | static 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: | |||
340 | static const struct of_device_id const psci_of_match[] __initconst = { | 427 | static 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 | ||