PSCI v1.0 augmented the power_state parameter format specification (extended stateid) and introduced a way to probe it through the PSCI_FEATURES interface. This patch implements code that detects the power_state format at run-time through the PSCI_FEATURES interface, so that the power_state argument can be properly detected and validated in the kernel according to the information provided through firmware.
Unified · Split
| @@ -75,14 +75,34 @@ static u32 psci_function_id[PSCI_FN_MAX]; · 74 unmodified lines | ||
| 75 | 75 | PSCI_0_2_POWER_STATE_TYPE_MASK | \ |
| 76 | 76 | PSCI_0_2_POWER_STATE_AFFL_MASK) |
| 77 | 77 | |
| 78 | +#define PSCI_1_0_EXT_POWER_STATE_MASK \ | |
| 79 | + (PSCI_1_0_EXT_POWER_STATE_ID_MASK | \ | |
| 80 | + PSCI_1_0_EXT_POWER_STATE_TYPE_MASK) | |
| 81 | + | |
| 82 | +static u32 psci_cpu_suspend_feature; | |
| 83 | + | |
| 84 | +static inline bool psci_has_ext_power_state(void) | |
| 85 | +{ | |
| 86 | + return psci_cpu_suspend_feature & | |
| 87 | + PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK; | |
| 88 | +} | |
| 89 | + | |
| 78 | 90 | bool psci_power_state_loses_context(u32 state) |
| 79 | 91 | { |
| 80 | − return state & PSCI_0_2_POWER_STATE_TYPE_MASK; | |
| 92 | + const u32 mask = psci_has_ext_power_state() ? | |
| 93 | + PSCI_1_0_EXT_POWER_STATE_TYPE_MASK : | |
| 94 | + PSCI_0_2_POWER_STATE_TYPE_MASK; | |
| 95 | + | |
| 96 | + return state & mask; | |
| 81 | 97 | } |
| 82 | 98 | |
| 83 | 99 | bool psci_power_state_is_valid(u32 state) |
| 84 | 100 | { |
| 85 | − return !(state & ~PSCI_0_2_POWER_STATE_MASK); | |
| 101 | + const u32 valid_mask = psci_has_ext_power_state() ? | |
| 102 | + PSCI_1_0_EXT_POWER_STATE_MASK : | |
| 103 | + PSCI_0_2_POWER_STATE_MASK; | |
| 104 | + | |
| 105 | + return !(state & ~valid_mask); | |
| 86 | 106 | } |
| 87 | 107 | |
| 88 | 108 | static int psci_to_linux_errno(int errno) |
| @@ -203,6 +223,14 @@ static int __init psci_features(u32 psci_func_id) · 114 unmodified lines | ||
| 203 | 223 | psci_func_id, 0, 0); |
| 204 | 224 | } |
| 205 | 225 | |
| 226 | +static void __init psci_init_cpu_suspend(void) | |
| 227 | +{ | |
| 228 | + int feature = psci_features(psci_function_id[PSCI_FN_CPU_SUSPEND]); | |
| 229 | + | |
| 230 | + if (feature != PSCI_RET_NOT_SUPPORTED) | |
| 231 | + psci_cpu_suspend_feature = feature; | |
| 232 | +} | |
| 233 | + | |
| 206 | 234 | /* |
| 207 | 235 | * Detect the presence of a resident Trusted OS which may cause CPU_OFF to |
| 208 | 236 | * return DENIED (which would be fatal). |
| @@ -287,6 +315,8 @@ static int __init psci_probe(void) · 78 unmodified lines | ||
| 287 | 315 | |
| 288 | 316 | psci_init_migrate(); |
| 289 | 317 | |
| 318 | + psci_init_cpu_suspend(); | |
| 319 | + | |
| 290 | 320 | return 0; |
| 291 | 321 | } |
| 292 | 322 | |
| @@ -58,6 +58,13 @@ · 57 unmodified lines | ||
| 58 | 58 | #define PSCI_0_2_POWER_STATE_AFFL_MASK \ |
| 59 | 59 | (0x3 << PSCI_0_2_POWER_STATE_AFFL_SHIFT) |
| 60 | 60 | |
| 61 | +/* PSCI extended power state encoding for CPU_SUSPEND function */ | |
| 62 | +#define PSCI_1_0_EXT_POWER_STATE_ID_MASK 0xfffffff | |
| 63 | +#define PSCI_1_0_EXT_POWER_STATE_ID_SHIFT 0 | |
| 64 | +#define PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT 30 | |
| 65 | +#define PSCI_1_0_EXT_POWER_STATE_TYPE_MASK \ | |
| 66 | + (0x1 << PSCI_1_0_EXT_POWER_STATE_TYPE_SHIFT) | |
| 67 | + | |
| 61 | 68 | /* PSCI v0.2 affinity level state returned by AFFINITY_INFO */ |
| 62 | 69 | #define PSCI_0_2_AFFINITY_LEVEL_ON 0 |
| 63 | 70 | #define PSCI_0_2_AFFINITY_LEVEL_OFF 1 |
| @@ -78,6 +85,11 @@ · 14 unmodified lines | ||
| 78 | 85 | #define PSCI_VERSION_MINOR(ver) \ |
| 79 | 86 | ((ver) & PSCI_VERSION_MINOR_MASK) |
| 80 | 87 | |
| 88 | +/* PSCI features decoding (>=1.0) */ | |
| 89 | +#define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT 1 | |
| 90 | +#define PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK \ | |
| 91 | + (0x1 << PSCI_1_0_FEATURES_CPU_SUSPEND_PF_SHIFT) | |
| 92 | + | |
| 81 | 93 | /* PSCI return values (inclusive of all PSCI versions) */ |
| 82 | 94 | #define PSCI_RET_SUCCESS 0 |
| 83 | 95 | #define PSCI_RET_NOT_SUPPORTED -1 |