Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PSCI: Add initial support for PSCIv0.2 functions

The PSCIv0.2 spec defines standard values of function IDs
and introduces a few new functions. Detect version of PSCI
and appropriately select the right PSCI functions.

Signed-off-by: Ashwin Chaugule <ashwin.chaugule@linaro.org>
Reviewed-by: Rob Herring <robh@kernel.org>
Acked-by: Catalin Marinas <catalin.marinas@arm.com>

+332 -81
+5 -2
arch/arm/include/asm/psci.h
··· 29 29 int (*cpu_off)(struct psci_power_state state); 30 30 int (*cpu_on)(unsigned long cpuid, unsigned long entry_point); 31 31 int (*migrate)(unsigned long cpuid); 32 + int (*affinity_info)(unsigned long target_affinity, 33 + unsigned long lowest_affinity_level); 34 + int (*migrate_info_type)(void); 32 35 }; 33 36 34 37 extern struct psci_operations psci_ops; 35 38 extern struct smp_operations psci_smp_ops; 36 39 37 40 #ifdef CONFIG_ARM_PSCI 38 - void psci_init(void); 41 + int psci_init(void); 39 42 bool psci_smp_available(void); 40 43 #else 41 - static inline void psci_init(void) { } 44 + static inline int psci_init(void) { } 42 45 static inline bool psci_smp_available(void) { return false; } 43 46 #endif 44 47
+161 -39
arch/arm/kernel/psci.c
··· 17 17 18 18 #include <linux/init.h> 19 19 #include <linux/of.h> 20 + #include <linux/reboot.h> 21 + #include <linux/pm.h> 22 + #include <uapi/linux/psci.h> 20 23 21 24 #include <asm/compiler.h> 22 25 #include <asm/errno.h> 23 26 #include <asm/opcodes-sec.h> 24 27 #include <asm/opcodes-virt.h> 25 28 #include <asm/psci.h> 29 + #include <asm/system_misc.h> 26 30 27 31 struct psci_operations psci_ops; 28 32 29 33 static int (*invoke_psci_fn)(u32, u32, u32, u32); 34 + typedef int (*psci_initcall_t)(const struct device_node *); 30 35 31 36 enum psci_function { 32 37 PSCI_FN_CPU_SUSPEND, 33 38 PSCI_FN_CPU_ON, 34 39 PSCI_FN_CPU_OFF, 35 40 PSCI_FN_MIGRATE, 41 + PSCI_FN_AFFINITY_INFO, 42 + PSCI_FN_MIGRATE_INFO_TYPE, 36 43 PSCI_FN_MAX, 37 44 }; 38 45 39 46 static u32 psci_function_id[PSCI_FN_MAX]; 40 - 41 - #define PSCI_RET_SUCCESS 0 42 - #define PSCI_RET_EOPNOTSUPP -1 43 - #define PSCI_RET_EINVAL -2 44 - #define PSCI_RET_EPERM -3 45 47 46 48 static int psci_to_linux_errno(int errno) 47 49 { 48 50 switch (errno) { 49 51 case PSCI_RET_SUCCESS: 50 52 return 0; 51 - case PSCI_RET_EOPNOTSUPP: 53 + case PSCI_RET_NOT_SUPPORTED: 52 54 return -EOPNOTSUPP; 53 - case PSCI_RET_EINVAL: 55 + case PSCI_RET_INVALID_PARAMS: 54 56 return -EINVAL; 55 - case PSCI_RET_EPERM: 57 + case PSCI_RET_DENIED: 56 58 return -EPERM; 57 59 }; 58 60 59 61 return -EINVAL; 60 62 } 61 63 62 - #define PSCI_POWER_STATE_ID_MASK 0xffff 63 - #define PSCI_POWER_STATE_ID_SHIFT 0 64 - #define PSCI_POWER_STATE_TYPE_MASK 0x1 65 - #define PSCI_POWER_STATE_TYPE_SHIFT 16 66 - #define PSCI_POWER_STATE_AFFL_MASK 0x3 67 - #define PSCI_POWER_STATE_AFFL_SHIFT 24 68 - 69 64 static u32 psci_power_state_pack(struct psci_power_state state) 70 65 { 71 - return ((state.id & PSCI_POWER_STATE_ID_MASK) 72 - << PSCI_POWER_STATE_ID_SHIFT) | 73 - ((state.type & PSCI_POWER_STATE_TYPE_MASK) 74 - << PSCI_POWER_STATE_TYPE_SHIFT) | 75 - ((state.affinity_level & PSCI_POWER_STATE_AFFL_MASK) 76 - << PSCI_POWER_STATE_AFFL_SHIFT); 66 + return ((state.id << PSCI_0_2_POWER_STATE_ID_SHIFT) 67 + & PSCI_0_2_POWER_STATE_ID_MASK) | 68 + ((state.type << PSCI_0_2_POWER_STATE_TYPE_SHIFT) 69 + & PSCI_0_2_POWER_STATE_TYPE_MASK) | 70 + ((state.affinity_level << PSCI_0_2_POWER_STATE_AFFL_SHIFT) 71 + & PSCI_0_2_POWER_STATE_AFFL_MASK); 77 72 } 78 73 79 74 /* ··· 103 108 : "r" (arg0), "r" (arg1), "r" (arg2)); 104 109 105 110 return function_id; 111 + } 112 + 113 + static int psci_get_version(void) 114 + { 115 + int err; 116 + 117 + err = invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0); 118 + return err; 106 119 } 107 120 108 121 static int psci_cpu_suspend(struct psci_power_state state, ··· 156 153 return psci_to_linux_errno(err); 157 154 } 158 155 159 - static const struct of_device_id psci_of_match[] __initconst = { 160 - { .compatible = "arm,psci", }, 161 - {}, 162 - }; 163 - 164 - void __init psci_init(void) 156 + static int psci_affinity_info(unsigned long target_affinity, 157 + unsigned long lowest_affinity_level) 165 158 { 166 - struct device_node *np; 159 + int err; 160 + u32 fn; 161 + 162 + fn = psci_function_id[PSCI_FN_AFFINITY_INFO]; 163 + err = invoke_psci_fn(fn, target_affinity, lowest_affinity_level, 0); 164 + return err; 165 + } 166 + 167 + static int psci_migrate_info_type(void) 168 + { 169 + int err; 170 + u32 fn; 171 + 172 + fn = psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE]; 173 + err = invoke_psci_fn(fn, 0, 0, 0); 174 + return err; 175 + } 176 + 177 + static int get_set_conduit_method(struct device_node *np) 178 + { 167 179 const char *method; 168 - u32 id; 169 180 170 - np = of_find_matching_node(NULL, psci_of_match); 171 - if (!np) 172 - return; 173 - 174 - pr_info("probing function IDs from device-tree\n"); 181 + pr_info("probing for conduit method from DT.\n"); 175 182 176 183 if (of_property_read_string(np, "method", &method)) { 177 - pr_warning("missing \"method\" property\n"); 178 - goto out_put_node; 184 + pr_warn("missing \"method\" property\n"); 185 + return -ENXIO; 179 186 } 180 187 181 188 if (!strcmp("hvc", method)) { ··· 193 180 } else if (!strcmp("smc", method)) { 194 181 invoke_psci_fn = __invoke_psci_fn_smc; 195 182 } else { 196 - pr_warning("invalid \"method\" property: %s\n", method); 197 - goto out_put_node; 183 + pr_warn("invalid \"method\" property: %s\n", method); 184 + return -EINVAL; 198 185 } 186 + return 0; 187 + } 188 + 189 + static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd) 190 + { 191 + invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); 192 + } 193 + 194 + static void psci_sys_poweroff(void) 195 + { 196 + invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); 197 + } 198 + 199 + /* 200 + * PSCI Function IDs for v0.2+ are well defined so use 201 + * standard values. 202 + */ 203 + static int psci_0_2_init(struct device_node *np) 204 + { 205 + int err, ver; 206 + 207 + err = get_set_conduit_method(np); 208 + 209 + if (err) 210 + goto out_put_node; 211 + 212 + ver = psci_get_version(); 213 + 214 + if (ver == PSCI_RET_NOT_SUPPORTED) { 215 + /* PSCI v0.2 mandates implementation of PSCI_ID_VERSION. */ 216 + pr_err("PSCI firmware does not comply with the v0.2 spec.\n"); 217 + err = -EOPNOTSUPP; 218 + goto out_put_node; 219 + } else { 220 + pr_info("PSCIv%d.%d detected in firmware.\n", 221 + PSCI_VERSION_MAJOR(ver), 222 + PSCI_VERSION_MINOR(ver)); 223 + 224 + if (PSCI_VERSION_MAJOR(ver) == 0 && 225 + PSCI_VERSION_MINOR(ver) < 2) { 226 + err = -EINVAL; 227 + pr_err("Conflicting PSCI version detected.\n"); 228 + goto out_put_node; 229 + } 230 + } 231 + 232 + pr_info("Using standard PSCI v0.2 function IDs\n"); 233 + psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN_CPU_SUSPEND; 234 + psci_ops.cpu_suspend = psci_cpu_suspend; 235 + 236 + psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF; 237 + psci_ops.cpu_off = psci_cpu_off; 238 + 239 + psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN_CPU_ON; 240 + psci_ops.cpu_on = psci_cpu_on; 241 + 242 + psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN_MIGRATE; 243 + psci_ops.migrate = psci_migrate; 244 + 245 + psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN_AFFINITY_INFO; 246 + psci_ops.affinity_info = psci_affinity_info; 247 + 248 + psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] = 249 + PSCI_0_2_FN_MIGRATE_INFO_TYPE; 250 + psci_ops.migrate_info_type = psci_migrate_info_type; 251 + 252 + arm_pm_restart = psci_sys_reset; 253 + 254 + pm_power_off = psci_sys_poweroff; 255 + 256 + out_put_node: 257 + of_node_put(np); 258 + return err; 259 + } 260 + 261 + /* 262 + * PSCI < v0.2 get PSCI Function IDs via DT. 263 + */ 264 + static int psci_0_1_init(struct device_node *np) 265 + { 266 + u32 id; 267 + int err; 268 + 269 + err = get_set_conduit_method(np); 270 + 271 + if (err) 272 + goto out_put_node; 273 + 274 + pr_info("Using PSCI v0.1 Function IDs from DT\n"); 199 275 200 276 if (!of_property_read_u32(np, "cpu_suspend", &id)) { 201 277 psci_function_id[PSCI_FN_CPU_SUSPEND] = id; ··· 308 206 309 207 out_put_node: 310 208 of_node_put(np); 311 - return; 209 + return err; 210 + } 211 + 212 + static const struct of_device_id psci_of_match[] __initconst = { 213 + { .compatible = "arm,psci", .data = psci_0_1_init}, 214 + { .compatible = "arm,psci-0.2", .data = psci_0_2_init}, 215 + {}, 216 + }; 217 + 218 + int __init psci_init(void) 219 + { 220 + struct device_node *np; 221 + const struct of_device_id *matched_np; 222 + psci_initcall_t init_fn; 223 + 224 + np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np); 225 + if (!np) 226 + return -ENODEV; 227 + 228 + init_fn = (psci_initcall_t)matched_np->data; 229 + return init_fn(np); 312 230 }
+1 -1
arch/arm64/include/asm/psci.h
··· 14 14 #ifndef __ASM_PSCI_H 15 15 #define __ASM_PSCI_H 16 16 17 - void psci_init(void); 17 + int psci_init(void); 18 18 19 19 #endif /* __ASM_PSCI_H */
+165 -39
arch/arm64/kernel/psci.c
··· 18 18 #include <linux/init.h> 19 19 #include <linux/of.h> 20 20 #include <linux/smp.h> 21 + #include <linux/reboot.h> 22 + #include <linux/pm.h> 23 + #include <uapi/linux/psci.h> 21 24 22 25 #include <asm/compiler.h> 23 26 #include <asm/cpu_ops.h> 24 27 #include <asm/errno.h> 25 28 #include <asm/psci.h> 26 29 #include <asm/smp_plat.h> 30 + #include <asm/system_misc.h> 27 31 28 32 #define PSCI_POWER_STATE_TYPE_STANDBY 0 29 33 #define PSCI_POWER_STATE_TYPE_POWER_DOWN 1 ··· 44 40 int (*cpu_off)(struct psci_power_state state); 45 41 int (*cpu_on)(unsigned long cpuid, unsigned long entry_point); 46 42 int (*migrate)(unsigned long cpuid); 43 + int (*affinity_info)(unsigned long target_affinity, 44 + unsigned long lowest_affinity_level); 45 + int (*migrate_info_type)(void); 47 46 }; 48 47 49 48 static struct psci_operations psci_ops; 50 49 51 50 static int (*invoke_psci_fn)(u64, u64, u64, u64); 51 + typedef int (*psci_initcall_t)(const struct device_node *); 52 52 53 53 enum psci_function { 54 54 PSCI_FN_CPU_SUSPEND, 55 55 PSCI_FN_CPU_ON, 56 56 PSCI_FN_CPU_OFF, 57 57 PSCI_FN_MIGRATE, 58 + PSCI_FN_AFFINITY_INFO, 59 + PSCI_FN_MIGRATE_INFO_TYPE, 58 60 PSCI_FN_MAX, 59 61 }; 60 62 61 63 static u32 psci_function_id[PSCI_FN_MAX]; 62 - 63 - #define PSCI_RET_SUCCESS 0 64 - #define PSCI_RET_EOPNOTSUPP -1 65 - #define PSCI_RET_EINVAL -2 66 - #define PSCI_RET_EPERM -3 67 64 68 65 static int psci_to_linux_errno(int errno) 69 66 { 70 67 switch (errno) { 71 68 case PSCI_RET_SUCCESS: 72 69 return 0; 73 - case PSCI_RET_EOPNOTSUPP: 70 + case PSCI_RET_NOT_SUPPORTED: 74 71 return -EOPNOTSUPP; 75 - case PSCI_RET_EINVAL: 72 + case PSCI_RET_INVALID_PARAMS: 76 73 return -EINVAL; 77 - case PSCI_RET_EPERM: 74 + case PSCI_RET_DENIED: 78 75 return -EPERM; 79 76 }; 80 77 81 78 return -EINVAL; 82 79 } 83 80 84 - #define PSCI_POWER_STATE_ID_MASK 0xffff 85 - #define PSCI_POWER_STATE_ID_SHIFT 0 86 - #define PSCI_POWER_STATE_TYPE_MASK 0x1 87 - #define PSCI_POWER_STATE_TYPE_SHIFT 16 88 - #define PSCI_POWER_STATE_AFFL_MASK 0x3 89 - #define PSCI_POWER_STATE_AFFL_SHIFT 24 90 - 91 81 static u32 psci_power_state_pack(struct psci_power_state state) 92 82 { 93 - return ((state.id & PSCI_POWER_STATE_ID_MASK) 94 - << PSCI_POWER_STATE_ID_SHIFT) | 95 - ((state.type & PSCI_POWER_STATE_TYPE_MASK) 96 - << PSCI_POWER_STATE_TYPE_SHIFT) | 97 - ((state.affinity_level & PSCI_POWER_STATE_AFFL_MASK) 98 - << PSCI_POWER_STATE_AFFL_SHIFT); 83 + return ((state.id << PSCI_0_2_POWER_STATE_ID_SHIFT) 84 + & PSCI_0_2_POWER_STATE_ID_MASK) | 85 + ((state.type << PSCI_0_2_POWER_STATE_TYPE_SHIFT) 86 + & PSCI_0_2_POWER_STATE_TYPE_MASK) | 87 + ((state.affinity_level << PSCI_0_2_POWER_STATE_AFFL_SHIFT) 88 + & PSCI_0_2_POWER_STATE_AFFL_MASK); 99 89 } 100 90 101 91 /* ··· 124 126 : "r" (arg0), "r" (arg1), "r" (arg2)); 125 127 126 128 return function_id; 129 + } 130 + 131 + static int psci_get_version(void) 132 + { 133 + int err; 134 + 135 + err = invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0); 136 + return err; 127 137 } 128 138 129 139 static int psci_cpu_suspend(struct psci_power_state state, ··· 177 171 return psci_to_linux_errno(err); 178 172 } 179 173 180 - static const struct of_device_id psci_of_match[] __initconst = { 181 - { .compatible = "arm,psci", }, 182 - {}, 183 - }; 184 - 185 - void __init psci_init(void) 174 + static int psci_affinity_info(unsigned long target_affinity, 175 + unsigned long lowest_affinity_level) 186 176 { 187 - struct device_node *np; 177 + int err; 178 + u32 fn; 179 + 180 + fn = psci_function_id[PSCI_FN_AFFINITY_INFO]; 181 + err = invoke_psci_fn(fn, target_affinity, lowest_affinity_level, 0); 182 + return err; 183 + } 184 + 185 + static int psci_migrate_info_type(void) 186 + { 187 + int err; 188 + u32 fn; 189 + 190 + fn = psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE]; 191 + err = invoke_psci_fn(fn, 0, 0, 0); 192 + return err; 193 + } 194 + 195 + static int get_set_conduit_method(struct device_node *np) 196 + { 188 197 const char *method; 189 - u32 id; 190 198 191 - np = of_find_matching_node(NULL, psci_of_match); 192 - if (!np) 193 - return; 194 - 195 - pr_info("probing function IDs from device-tree\n"); 199 + pr_info("probing for conduit method from DT.\n"); 196 200 197 201 if (of_property_read_string(np, "method", &method)) { 198 - pr_warning("missing \"method\" property\n"); 199 - goto out_put_node; 202 + pr_warn("missing \"method\" property\n"); 203 + return -ENXIO; 200 204 } 201 205 202 206 if (!strcmp("hvc", method)) { ··· 214 198 } else if (!strcmp("smc", method)) { 215 199 invoke_psci_fn = __invoke_psci_fn_smc; 216 200 } else { 217 - pr_warning("invalid \"method\" property: %s\n", method); 218 - goto out_put_node; 201 + pr_warn("invalid \"method\" property: %s\n", method); 202 + return -EINVAL; 219 203 } 204 + return 0; 205 + } 206 + 207 + static void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd) 208 + { 209 + invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); 210 + } 211 + 212 + static void psci_sys_poweroff(void) 213 + { 214 + invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); 215 + } 216 + 217 + /* 218 + * PSCI Function IDs for v0.2+ are well defined so use 219 + * standard values. 220 + */ 221 + static int psci_0_2_init(struct device_node *np) 222 + { 223 + int err, ver; 224 + 225 + err = get_set_conduit_method(np); 226 + 227 + if (err) 228 + goto out_put_node; 229 + 230 + ver = psci_get_version(); 231 + 232 + if (ver == PSCI_RET_NOT_SUPPORTED) { 233 + /* PSCI v0.2 mandates implementation of PSCI_ID_VERSION. */ 234 + pr_err("PSCI firmware does not comply with the v0.2 spec.\n"); 235 + err = -EOPNOTSUPP; 236 + goto out_put_node; 237 + } else { 238 + pr_info("PSCIv%d.%d detected in firmware.\n", 239 + PSCI_VERSION_MAJOR(ver), 240 + PSCI_VERSION_MINOR(ver)); 241 + 242 + if (PSCI_VERSION_MAJOR(ver) == 0 && 243 + PSCI_VERSION_MINOR(ver) < 2) { 244 + err = -EINVAL; 245 + pr_err("Conflicting PSCI version detected.\n"); 246 + goto out_put_node; 247 + } 248 + } 249 + 250 + pr_info("Using standard PSCI v0.2 function IDs\n"); 251 + psci_function_id[PSCI_FN_CPU_SUSPEND] = PSCI_0_2_FN64_CPU_SUSPEND; 252 + psci_ops.cpu_suspend = psci_cpu_suspend; 253 + 254 + psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF; 255 + psci_ops.cpu_off = psci_cpu_off; 256 + 257 + psci_function_id[PSCI_FN_CPU_ON] = PSCI_0_2_FN64_CPU_ON; 258 + psci_ops.cpu_on = psci_cpu_on; 259 + 260 + psci_function_id[PSCI_FN_MIGRATE] = PSCI_0_2_FN64_MIGRATE; 261 + psci_ops.migrate = psci_migrate; 262 + 263 + psci_function_id[PSCI_FN_AFFINITY_INFO] = PSCI_0_2_FN64_AFFINITY_INFO; 264 + psci_ops.affinity_info = psci_affinity_info; 265 + 266 + psci_function_id[PSCI_FN_MIGRATE_INFO_TYPE] = 267 + PSCI_0_2_FN_MIGRATE_INFO_TYPE; 268 + psci_ops.migrate_info_type = psci_migrate_info_type; 269 + 270 + arm_pm_restart = psci_sys_reset; 271 + 272 + pm_power_off = psci_sys_poweroff; 273 + 274 + out_put_node: 275 + of_node_put(np); 276 + return err; 277 + } 278 + 279 + /* 280 + * PSCI < v0.2 get PSCI Function IDs via DT. 281 + */ 282 + static int psci_0_1_init(struct device_node *np) 283 + { 284 + u32 id; 285 + int err; 286 + 287 + err = get_set_conduit_method(np); 288 + 289 + if (err) 290 + goto out_put_node; 291 + 292 + pr_info("Using PSCI v0.1 Function IDs from DT\n"); 220 293 221 294 if (!of_property_read_u32(np, "cpu_suspend", &id)) { 222 295 psci_function_id[PSCI_FN_CPU_SUSPEND] = id; ··· 329 224 330 225 out_put_node: 331 226 of_node_put(np); 332 - return; 227 + return err; 228 + } 229 + 230 + static const struct of_device_id psci_of_match[] __initconst = { 231 + { .compatible = "arm,psci", .data = psci_0_1_init}, 232 + { .compatible = "arm,psci-0.2", .data = psci_0_2_init}, 233 + {}, 234 + }; 235 + 236 + int __init psci_init(void) 237 + { 238 + struct device_node *np; 239 + const struct of_device_id *matched_np; 240 + psci_initcall_t init_fn; 241 + 242 + np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np); 243 + 244 + if (!np) 245 + return -ENODEV; 246 + 247 + init_fn = (psci_initcall_t)matched_np->data; 248 + return init_fn(np); 333 249 } 334 250 335 251 #ifdef CONFIG_SMP