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
4747
4848 #define ARM64_LPI_IS_RETENTION_STATE(arch_flags) (!(arch_flags))
4949
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+
5088 int acpi_processor_ffh_lpi_probe(unsigned int cpu)
5189 {
52− return psci_cpu_init_idle(cpu);
90+ return psci_acpi_cpu_init_idle(cpu);
5391 }
5492
5593 int acpi_processor_ffh_lpi_enter(struct acpi_lpi_state *lpi)
5694 {
95+ u32 state = lpi->address;
96+
5797 if (ARM64_LPI_IS_RETENTION_STATE(lpi->arch_flags))
58− return CPU_PM_CPU_IDLE_ENTER_RETENTION(psci_cpu_suspend_enter,
59− lpi->index);
98+ return CPU_PM_CPU_IDLE_ENTER_RETENTION_PARAM(psci_cpu_suspend_enter,
99+ lpi->index, state);
60100 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);
62103 }
63104 #endif
drivers/cpuidle/cpuidle-psci.c+86−1
@@ -22,10 +22,15 @@ · 21 unmodified lines
2222
2323 #include "dt_idle_states.h"
2424
25+static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
26+
2527 static int psci_enter_idle_state(struct cpuidle_device *dev,
2628 struct cpuidle_driver *drv, int idx)
2729 {
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]);
2934 }
3035
3136 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
5156 { },
5257 };
5358
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+
54139 static int __init psci_idle_init_cpu(int cpu)
55140 {
56141 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
103103 return state & mask;
104104 }
105105
106−static inline bool psci_power_state_is_valid(u32 state)
106+bool psci_power_state_is_valid(u32 state)
107107 {
108108 const u32 valid_mask = psci_has_ext_power_state() ?
109109 PSCI_1_0_EXT_POWER_STATE_MASK :
@@ -277,162 +277,21 @@ static int __init psci_features(u32 psci_func_id) · 167 unmodified lines
277277 }
278278
279279 #ifdef CONFIG_CPU_IDLE
280−static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_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)
280+static int psci_suspend_finisher(unsigned long state)
414281 {
415− u32 *state = __this_cpu_read(psci_power_state);
282+ u32 power_state = state;
416283
417− return psci_ops.cpu_suspend(state[index - 1],
418− __pa_symbol(cpu_resume));
284+ return psci_ops.cpu_suspend(power_state, __pa_symbol(cpu_resume));
419285 }
420286
421−int psci_cpu_suspend_enter(unsigned long index)
287+int psci_cpu_suspend_enter(u32 state)
422288 {
423289 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;
431290
432− if (!psci_power_state_loses_context(state[index - 1]))
433− ret = psci_ops.cpu_suspend(state[index - 1], 0);
291+ if (!psci_power_state_loses_context(state))
292+ ret = psci_ops.cpu_suspend(state, 0);
434293 else
435− ret = cpu_suspend(index, psci_suspend_finisher);
294+ ret = cpu_suspend(state, psci_suspend_finisher);
436295
437296 return ret;
438297 }
include/linux/cpuidle.h+13−4
@@ -256,7 +256,10 @@ static inline int cpuidle_register_governor(struct cpuidle_governor *gov) · 255 unmodified lines
256256 {return 0;}
257257 #endif
258258
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) \
260263 ({ \
261264 int __ret = 0; \
262265 \
@@ -268,7 +271,7 @@ static inline int cpuidle_register_governor(struct cpuidle_governor *gov) · 5 unmodified lines
268271 if (!is_retention) \
269272 __ret = cpu_pm_enter(); \
270273 if (!__ret) { \
271− __ret = low_level_idle_enter(idx); \
274+ __ret = low_level_idle_enter(state); \
272275 if (!is_retention) \
273276 cpu_pm_exit(); \
274277 } \
@@ -277,9 +280,15 @@ static inline int cpuidle_register_governor(struct cpuidle_governor *gov) · 2 unmodified lines
277280 })
278281
279282 #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)
281284
282285 #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)
284293
285294 #endif /* _LINUX_CPUIDLE_H */
include/linux/psci.h+2−2
@@ -15,8 +15,8 @@ · 14 unmodified lines
1515
1616 bool psci_tos_resident_on(int cpu);
1717
18−int psci_cpu_init_idle(unsigned int cpu);
19−int psci_cpu_suspend_enter(unsigned long index);
18+int psci_cpu_suspend_enter(u32 state);
19+bool psci_power_state_is_valid(u32 state);
2020
2121 enum psci_conduit {
2222 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