Current PSCI code handles idle state entry through the psci_cpu_suspend_enter() API, that takes an idle state index as a parameter and convert the index into a previously initialized power_state parameter before calling the PSCI.CPU_SUSPEND() with it. This is unwieldly, since it forces the PSCI firmware layer to keep track of power_state parameter for every idle state so that the index->power_state conversion can be made in the PSCI firmware layer instead of the CPUidle driver implementations. Move the power_state handling out of drivers/firmware/psci into the respective ACPI/DT PSCI CPUidle backends and convert the psci_cpu_suspend_enter() API to get the power_state parameter as input, which makes it closer to its firmware interface PSCI.CPU_SUSPEND() API. A notable side effect is that the PSCI ACPI/DT CPUidle backends now can directly handle (and if needed update) power_state parameters before handing them over to the PSCI firmware interface to trigger PSCI.CPU_SUSPEND() calls.
Unified · Split
| @@ -47,17 +47,58 @@ int arm_cpuidle_suspend(int index) · 46 unmodified lines | |||
| 47 | 47 | ||
| 48 | #define ARM64_LPI_IS_RETENTION_STATE(arch_flags) (!(arch_flags)) | 48 | #define ARM64_LPI_IS_RETENTION_STATE(arch_flags) (!(arch_flags)) |
| 49 | 49 | ||
| 50 | static int psci_acpi_cpu_init_idle(unsigned int cpu) | ||
| 51 | { | ||
| 52 | int i, count; | ||
| 53 | struct acpi_lpi_state *lpi; | ||
| 54 | struct acpi_processor *pr = per_cpu(processors, cpu); | ||
| 55 | |||
| 56 | /* | ||
| 57 | * If the PSCI cpu_suspend function hook has not been initialized | ||
| 58 | * idle states must not be enabled, so bail out | ||
| 59 | */ | ||
| 60 | if (!psci_ops.cpu_suspend) | ||
| 61 | return -EOPNOTSUPP; | ||
| 62 | |||
| 63 | if (unlikely(!pr || !pr->flags.has_lpi)) | ||
| 64 | return -EINVAL; | ||
| 65 | |||
| 66 | count = pr->power.count - 1; | ||
| 67 | if (count <= 0) | ||
| 68 | return -ENODEV; | ||
| 69 | |||
| 70 | for (i = 0; i < count; i++) { | ||
| 71 | u32 state; | ||
| 72 | |||
| 73 | lpi = &pr->power.lpi_states[i + 1]; | ||
| 74 | /* | ||
| 75 | * Only bits[31:0] represent a PSCI power_state while | ||
| 76 | * bits[63:32] must be 0x0 as per ARM ACPI FFH Specification | ||
| 77 | */ | ||
| 78 | state = lpi->address; | ||
| 79 | if (!psci_power_state_is_valid(state)) { | ||
| 80 | pr_warn("Invalid PSCI power state %#x\n", state); | ||
| 81 | return -EINVAL; | ||
| 82 | } | ||
| 83 | } | ||
| 84 | |||
| 85 | return 0; | ||
| 86 | } | ||
| 87 | |||
| 50 | int acpi_processor_ffh_lpi_probe(unsigned int cpu) | 88 | int acpi_processor_ffh_lpi_probe(unsigned int cpu) |
| 51 | { | 89 | { |
| 52 | return psci_cpu_init_idle(cpu); | 90 | return psci_acpi_cpu_init_idle(cpu); |
| 53 | } | 91 | } |
| 54 | 92 | ||
| 55 | int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) | 93 | int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi) |
| 56 | { | 94 | { |
| 95 | u32 state = lpi->address; | ||
| 96 | |||
| 57 | if (ARM64_LPI_IS_RETENTION_STATE(lpi->arch_flags)) | 97 | if (ARM64_LPI_IS_RETENTION_STATE(lpi->arch_flags)) |
| 58 | return CPU_PM_CPU_IDLE_ENTER_RETENTION(psci_cpu_suspend_enter, | 98 | return CPU_PM_CPU_IDLE_ENTER_RETENTION_PARAM(psci_cpu_suspend_enter, |
| 59 | lpi->index); | 99 | lpi->index, state); |
| 60 | else | 100 | else |
| 61 | return CPU_PM_CPU_IDLE_ENTER(psci_cpu_suspend_enter, lpi->index); | 101 | return CPU_PM_CPU_IDLE_ENTER_PARAM(psci_cpu_suspend_enter, |
| 102 | lpi->index, state); | ||
| 62 | } | 103 | } |
| 63 | #endif | 104 | #endif |
| @@ -22,10 +22,15 @@ · 21 unmodified lines | |||
| 22 | 22 | ||
| 23 | #include "dt_idle_states.h" | 23 | #include "dt_idle_states.h" |
| 24 | 24 | ||
| 25 | static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state); | ||
| 26 | |||
| 25 | static int psci_enter_idle_state(struct cpuidle_device *dev, | 27 | static int psci_enter_idle_state(struct cpuidle_device *dev, |
| 26 | struct cpuidle_driver *drv, int idx) | 28 | struct cpuidle_driver *drv, int idx) |
| 27 | { | 29 | { |
| 28 | return CPU_PM_CPU_IDLE_ENTER(psci_cpu_suspend_enter, idx); | 30 | u32 *state = __this_cpu_read(psci_power_state); |
| 31 | |||
| 32 | return CPU_PM_CPU_IDLE_ENTER_PARAM(psci_cpu_suspend_enter, | ||
| 33 | idx, state[idx - 1]); | ||
| 29 | } | 34 | } |
| 30 | 35 | ||
| 31 | static struct cpuidle_driver psci_idle_driver __initdata = { | 36 | static struct cpuidle_driver psci_idle_driver __initdata = { |
| @@ -51,6 +56,86 @@ static const struct of_device_id psci_idle_state_match[] __initconst = { · 19 unmodified lines | |||
| 51 | { }, | 56 | { }, |
| 52 | }; | 57 | }; |
| 53 | 58 | ||
| 59 | static int __init psci_dt_parse_state_node(struct device_node *np, u32 *state) | ||
| 60 | { | ||
| 61 | int err = of_property_read_u32(np, "arm,psci-suspend-param", state); | ||
| 62 | |||
| 63 | if (err) { | ||
| 64 | pr_warn("%pOF missing arm,psci-suspend-param property\n", np); | ||
| 65 | return err; | ||
| 66 | } | ||
| 67 | |||
| 68 | if (!psci_power_state_is_valid(*state)) { | ||
| 69 | pr_warn("Invalid PSCI power state %#x\n", *state); | ||
| 70 | return -EINVAL; | ||
| 71 | } | ||
| 72 | |||
| 73 | return 0; | ||
| 74 | } | ||
| 75 | |||
| 76 | static int __init psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu) | ||
| 77 | { | ||
| 78 | int i, ret = 0, count = 0; | ||
| 79 | u32 *psci_states; | ||
| 80 | struct device_node *state_node; | ||
| 81 | |||
| 82 | /* Count idle states */ | ||
| 83 | while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states", | ||
| 84 | count))) { | ||
| 85 | count++; | ||
| 86 | of_node_put(state_node); | ||
| 87 | } | ||
| 88 | |||
| 89 | if (!count) | ||
| 90 | return -ENODEV; | ||
| 91 | |||
| 92 | psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL); | ||
| 93 | if (!psci_states) | ||
| 94 | return -ENOMEM; | ||
| 95 | |||
| 96 | for (i = 0; i < count; i++) { | ||
| 97 | state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); | ||
| 98 | ret = psci_dt_parse_state_node(state_node, &psci_states[i]); | ||
| 99 | of_node_put(state_node); | ||
| 100 | |||
| 101 | if (ret) | ||
| 102 | goto free_mem; | ||
| 103 | |||
| 104 | pr_debug("psci-power-state %#x index %d\n", psci_states[i], i); | ||
| 105 | } | ||
| 106 | |||
| 107 | /* Idle states parsed correctly, initialize per-cpu pointer */ | ||
| 108 | per_cpu(psci_power_state, cpu) = psci_states; | ||
| 109 | return 0; | ||
| 110 | |||
| 111 | free_mem: | ||
| 112 | kfree(psci_states); | ||
| 113 | return ret; | ||
| 114 | } | ||
| 115 | |||
| 116 | static __init int psci_cpu_init_idle(unsigned int cpu) | ||
| 117 | { | ||
| 118 | struct device_node *cpu_node; | ||
| 119 | int ret; | ||
| 120 | |||
| 121 | /* | ||
| 122 | * If the PSCI cpu_suspend function hook has not been initialized | ||
| 123 | * idle states must not be enabled, so bail out | ||
| 124 | */ | ||
| 125 | if (!psci_ops.cpu_suspend) | ||
| 126 | return -EOPNOTSUPP; | ||
| 127 | |||
| 128 | cpu_node = of_cpu_device_node_get(cpu); | ||
| 129 | if (!cpu_node) | ||
| 130 | return -ENODEV; | ||
| 131 | |||
| 132 | ret = psci_dt_cpu_init_idle(cpu_node, cpu); | ||
| 133 | |||
| 134 | of_node_put(cpu_node); | ||
| 135 | |||
| 136 | return ret; | ||
| 137 | } | ||
| 138 | |||
| 54 | static int __init psci_idle_init_cpu(int cpu) | 139 | static int __init psci_idle_init_cpu(int cpu) |
| 55 | { | 140 | { |
| 56 | struct cpuidle_driver *drv; | 141 | struct cpuidle_driver *drv; |
| @@ -103,7 +103,7 @@ static inline bool psci_power_state_loses_context(u32 state) · 102 unmodified lines | |||
| 103 | return state & mask; | 103 | return state & mask; |
| 104 | } | 104 | } |
| 105 | 105 | ||
| 106 | static inline bool psci_power_state_is_valid(u32 state) | 106 | bool psci_power_state_is_valid(u32 state) |
| 107 | { | 107 | { |
| 108 | const u32 valid_mask = psci_has_ext_power_state() ? | 108 | const u32 valid_mask = psci_has_ext_power_state() ? |
| 109 | PSCI_1_0_EXT_POWER_STATE_MASK : | 109 | PSCI_1_0_EXT_POWER_STATE_MASK : |
| @@ -277,162 +277,21 @@ static int __init psci_features(u32 psci_func_id) · 167 unmodified lines | |||
| 277 | } | 277 | } |
| 278 | 278 | ||
| 279 | #ifdef CONFIG_CPU_IDLE | 279 | #ifdef CONFIG_CPU_IDLE |
| 280 | static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state); | 280 | static int psci_suspend_finisher(unsigned long state) |
| 281 | |||
| 282 | static int psci_dt_parse_state_node(struct device_node *np, u32 *state) | ||
| 283 | { | ||
| 284 | int err = of_property_read_u32(np, "arm,psci-suspend-param", state); | ||
| 285 | |||
| 286 | if (err) { | ||
| 287 | pr_warn("%pOF missing arm,psci-suspend-param property\n", np); | ||
| 288 | return err; | ||
| 289 | } | ||
| 290 | |||
| 291 | if (!psci_power_state_is_valid(*state)) { | ||
| 292 | pr_warn("Invalid PSCI power state %#x\n", *state); | ||
| 293 | return -EINVAL; | ||
| 294 | } | ||
| 295 | |||
| 296 | return 0; | ||
| 297 | } | ||
| 298 | |||
| 299 | static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu) | ||
| 300 | { | ||
| 301 | int i, ret = 0, count = 0; | ||
| 302 | u32 *psci_states; | ||
| 303 | struct device_node *state_node; | ||
| 304 | |||
| 305 | /* Count idle states */ | ||
| 306 | while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states", | ||
| 307 | count))) { | ||
| 308 | count++; | ||
| 309 | of_node_put(state_node); | ||
| 310 | } | ||
| 311 | |||
| 312 | if (!count) | ||
| 313 | return -ENODEV; | ||
| 314 | |||
| 315 | psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL); | ||
| 316 | if (!psci_states) | ||
| 317 | return -ENOMEM; | ||
| 318 | |||
| 319 | for (i = 0; i < count; i++) { | ||
| 320 | state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i); | ||
| 321 | ret = psci_dt_parse_state_node(state_node, &psci_states[i]); | ||
| 322 | of_node_put(state_node); | ||
| 323 | |||
| 324 | if (ret) | ||
| 325 | goto free_mem; | ||
| 326 | |||
| 327 | pr_debug("psci-power-state %#x index %d\n", psci_states[i], i); | ||
| 328 | } | ||
| 329 | |||
| 330 | /* Idle states parsed correctly, initialize per-cpu pointer */ | ||
| 331 | per_cpu(psci_power_state, cpu) = psci_states; | ||
| 332 | return 0; | ||
| 333 | |||
| 334 | free_mem: | ||
| 335 | kfree(psci_states); | ||
| 336 | return ret; | ||
| 337 | } | ||
| 338 | |||
| 339 | #ifdef CONFIG_ACPI | ||
| 340 | #include <acpi/processor.h> | ||
| 341 | |||
| 342 | static int __maybe_unused psci_acpi_cpu_init_idle(unsigned int cpu) | ||
| 343 | { | ||
| 344 | int i, count; | ||
| 345 | u32 *psci_states; | ||
| 346 | struct acpi_lpi_state *lpi; | ||
| 347 | struct acpi_processor *pr = per_cpu(processors, cpu); | ||
| 348 | |||
| 349 | if (unlikely(!pr || !pr->flags.has_lpi)) | ||
| 350 | return -EINVAL; | ||
| 351 | |||
| 352 | count = pr->power.count - 1; | ||
| 353 | if (count <= 0) | ||
| 354 | return -ENODEV; | ||
| 355 | |||
| 356 | psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL); | ||
| 357 | if (!psci_states) | ||
| 358 | return -ENOMEM; | ||
| 359 | |||
| 360 | for (i = 0; i < count; i++) { | ||
| 361 | u32 state; | ||
| 362 | |||
| 363 | lpi = &pr->power.lpi_states[i + 1]; | ||
| 364 | /* | ||
| 365 | * Only bits[31:0] represent a PSCI power_state while | ||
| 366 | * bits[63:32] must be 0x0 as per ARM ACPI FFH Specification | ||
| 367 | */ | ||
| 368 | state = lpi->address; | ||
| 369 | if (!psci_power_state_is_valid(state)) { | ||
| 370 | pr_warn("Invalid PSCI power state %#x\n", state); | ||
| 371 | kfree(psci_states); | ||
| 372 | return -EINVAL; | ||
| 373 | } | ||
| 374 | psci_states[i] = state; | ||
| 375 | } | ||
| 376 | /* Idle states parsed correctly, initialize per-cpu pointer */ | ||
| 377 | per_cpu(psci_power_state, cpu) = psci_states; | ||
| 378 | return 0; | ||
| 379 | } | ||
| 380 | #else | ||
| 381 | static int __maybe_unused psci_acpi_cpu_init_idle(unsigned int cpu) | ||
| 382 | { | ||
| 383 | return -EINVAL; | ||
| 384 | } | ||
| 385 | #endif | ||
| 386 | |||
| 387 | int psci_cpu_init_idle(unsigned int cpu) | ||
| 388 | { | ||
| 389 | struct device_node *cpu_node; | ||
| 390 | int ret; | ||
| 391 | |||
| 392 | /* | ||
| 393 | * If the PSCI cpu_suspend function hook has not been initialized | ||
| 394 | * idle states must not be enabled, so bail out | ||
| 395 | */ | ||
| 396 | if (!psci_ops.cpu_suspend) | ||
| 397 | return -EOPNOTSUPP; | ||
| 398 | |||
| 399 | if (!acpi_disabled) | ||
| 400 | return psci_acpi_cpu_init_idle(cpu); | ||
| 401 | |||
| 402 | cpu_node = of_get_cpu_node(cpu, NULL); | ||
| 403 | if (!cpu_node) | ||
| 404 | return -ENODEV; | ||
| 405 | |||
| 406 | ret = psci_dt_cpu_init_idle(cpu_node, cpu); | ||
| 407 | |||
| 408 | of_node_put(cpu_node); | ||
| 409 | |||
| 410 | return ret; | ||
| 411 | } | ||
| 412 | |||
| 413 | static int psci_suspend_finisher(unsigned long index) | ||
| 414 | { | 281 | { |
| 415 | u32 *state = __this_cpu_read(psci_power_state); | 282 | u32 power_state = state; |
| 416 | 283 | ||
| 417 | return psci_ops.cpu_suspend(state[index - 1], | 284 | return psci_ops.cpu_suspend(power_state, __pa_symbol(cpu_resume)); |
| 418 | __pa_symbol(cpu_resume)); | ||
| 419 | } | 285 | } |
| 420 | 286 | ||
| 421 | int psci_cpu_suspend_enter(unsigned long index) | 287 | int psci_cpu_suspend_enter(u32 state) |
| 422 | { | 288 | { |
| 423 | int ret; | 289 | int ret; |
| 424 | u32 *state = __this_cpu_read(psci_power_state); | ||
| 425 | /* | ||
| 426 | * idle state index 0 corresponds to wfi, should never be called | ||
| 427 | * from the cpu_suspend operations | ||
| 428 | */ | ||
| 429 | if (WARN_ON_ONCE(!index)) | ||
| 430 | return -EINVAL; | ||
| 431 | 290 | ||
| 432 | if (!psci_power_state_loses_context(state[index - 1])) | 291 | if (!psci_power_state_loses_context(state)) |
| 433 | ret = psci_ops.cpu_suspend(state[index - 1], 0); | 292 | ret = psci_ops.cpu_suspend(state, 0); |
| 434 | else | 293 | else |
| 435 | ret = cpu_suspend(index, psci_suspend_finisher); | 294 | ret = cpu_suspend(state, psci_suspend_finisher); |
| 436 | 295 | ||
| 437 | return ret; | 296 | return ret; |
| 438 | } | 297 | } |
| @@ -256,7 +256,10 @@ static inline int cpuidle_register_governor(struct cpuidle_governor *gov) · 255 unmodified lines | |||
| 256 | {return 0;} | 256 | {return 0;} |
| 257 | #endif | 257 | #endif |
| 258 | 258 | ||
| 259 | #define __CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx, is_retention) \ | 259 | #define __CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, \ |
| 260 | idx, \ | ||
| 261 | state, \ | ||
| 262 | is_retention) \ | ||
| 260 | ({ \ | 263 | ({ \ |
| 261 | int __ret = 0; \ | 264 | int __ret = 0; \ |
| 262 | \ | 265 | \ |
| @@ -268,7 +271,7 @@ static inline int cpuidle_register_governor(struct cpuidle_governor *gov) · 5 unmodified lines | |||
| 268 | if (!is_retention) \ | 271 | if (!is_retention) \ |
| 269 | __ret = cpu_pm_enter(); \ | 272 | __ret = cpu_pm_enter(); \ |
| 270 | if (!__ret) { \ | 273 | if (!__ret) { \ |
| 271 | __ret = low_level_idle_enter(idx); \ | 274 | __ret = low_level_idle_enter(state); \ |
| 272 | if (!is_retention) \ | 275 | if (!is_retention) \ |
| 273 | cpu_pm_exit(); \ | 276 | cpu_pm_exit(); \ |
| 274 | } \ | 277 | } \ |
| @@ -277,9 +280,15 @@ static inline int cpuidle_register_governor(struct cpuidle_governor *gov) · 2 unmodified lines | |||
| 277 | }) | 280 | }) |
| 278 | 281 | ||
| 279 | #define CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx) \ | 282 | #define CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx) \ |
| 280 | __CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx, 0) | 283 | __CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx, idx, 0) |
| 281 | 284 | ||
| 282 | #define CPU_PM_CPU_IDLE_ENTER_RETENTION(low_level_idle_enter, idx) \ | 285 | #define CPU_PM_CPU_IDLE_ENTER_RETENTION(low_level_idle_enter, idx) \ |
| 283 | __CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx, 1) | 286 | __CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx, idx, 1) |
| 287 | |||
| 288 | #define CPU_PM_CPU_IDLE_ENTER_PARAM(low_level_idle_enter, idx, state) \ | ||
| 289 | __CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx, state, 0) | ||
| 290 | |||
| 291 | #define CPU_PM_CPU_IDLE_ENTER_RETENTION_PARAM(low_level_idle_enter, idx, state) \ | ||
| 292 | __CPU_PM_CPU_IDLE_ENTER(low_level_idle_enter, idx, state, 1) | ||
| 284 | 293 | ||
| 285 | #endif /* _LINUX_CPUIDLE_H */ | 294 | #endif /* _LINUX_CPUIDLE_H */ |
| @@ -15,8 +15,8 @@ · 14 unmodified lines | |||
| 15 | 15 | ||
| 16 | bool psci_tos_resident_on(int cpu); | 16 | bool psci_tos_resident_on(int cpu); |
| 17 | 17 | ||
| 18 | int psci_cpu_init_idle(unsigned int cpu); | 18 | int psci_cpu_suspend_enter(u32 state); |
| 19 | int psci_cpu_suspend_enter(unsigned long index); | 19 | bool psci_power_state_is_valid(u32 state); |
| 20 | 20 | ||
| 21 | enum psci_conduit { | 21 | enum psci_conduit { |
| 22 | PSCI_CONDUIT_NONE, | 22 | PSCI_CONDUIT_NONE, |