History

PSCI: cpuidle: Refactor CPU suspend power_state parameter handling

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.

Lorenzo Pieralisi committed 7 years ago

Commit 9ffeb6d1 parent 78896145 files · +154 −160Browse files

Unified · Split

5 files
  1. arch/arm64/kernel/cpuidle.c +45 −4
  2. drivers/cpuidle/cpuidle-psci.c +86 −1
  3. drivers/firmware/psci/psci.c +8 −149
  4. include/linux/cpuidle.h +13 −4
  5. include/linux/psci.h +2 −2
arch/arm64/kernel/cpuidle.c+45−4
@@ -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
50static 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
50int acpi_processor_ffh_lpi_probe(unsigned int cpu)88int 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
55int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi)93int 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 else100 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#endif104#endif
drivers/cpuidle/cpuidle-psci.c+86−1
@@ -22,10 +22,15 @@ · 21 unmodified lines
22 22
23#include "dt_idle_states.h"23#include "dt_idle_states.h"
24 24
25static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
26
25static int psci_enter_idle_state(struct cpuidle_device *dev,27static 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
31static struct cpuidle_driver psci_idle_driver __initdata = {36static 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
59static 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
76static 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
111free_mem:
112 kfree(psci_states);
113 return ret;
114}
115
116static __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
54static int __init psci_idle_init_cpu(int cpu)139static int __init psci_idle_init_cpu(int cpu)
55{140{
56 struct cpuidle_driver *drv;141 struct cpuidle_driver *drv;
drivers/firmware/psci/psci.c+8−149
@@ -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
106static inline bool psci_power_state_is_valid(u32 state)106bool 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_IDLE279#ifdef CONFIG_CPU_IDLE
280static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);280static int psci_suspend_finisher(unsigned long state)
281
282static 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
299static 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
334free_mem:
335 kfree(psci_states);
336 return ret;
337}
338
339#ifdef CONFIG_ACPI
340#include <acpi/processor.h>
341
342static 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
381static int __maybe_unused psci_acpi_cpu_init_idle(unsigned int cpu)
382{
383 return -EINVAL;
384}
385#endif
386
387int 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
413static 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
421int psci_cpu_suspend_enter(unsigned long index)287int 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 else293 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}
include/linux/cpuidle.h+13−4
@@ -256,7 +256,10 @@ static inline int cpuidle_register_governor(struct cpuidle_governor *gov) · 255 unmodified lines
256{return 0;}256{return 0;}
257#endif257#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 */
include/linux/psci.h+2−2
@@ -15,8 +15,8 @@ · 14 unmodified lines
15 15
16bool psci_tos_resident_on(int cpu);16bool psci_tos_resident_on(int cpu);
17 17
18int psci_cpu_init_idle(unsigned int cpu);18int psci_cpu_suspend_enter(u32 state);
19int psci_cpu_suspend_enter(unsigned long index);19bool psci_power_state_is_valid(u32 state);
20 20
21enum psci_conduit {21enum psci_conduit {
22 PSCI_CONDUIT_NONE,22 PSCI_CONDUIT_NONE,

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t