aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLorenzo Pieralisi <lorenzo.pieralisi@arm.com>2016-02-01 12:01:30 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2016-02-11 10:33:38 -0500
commit8b6f2499ac45d5a0ab2e4b6f9613ab3f60416be1 (patch)
treeaf0819f38904564e0c800e20674008b6e4ea6f84
parent1b9bdf5c1661873a10e193b8cbb803a87fe5c4a1 (diff)
ARM: 8511/1: ARM64: kernel: PSCI: move PSCI idle management code to drivers/firmware
ARM64 PSCI kernel interfaces that initialize idle states and implement the suspend API to enter them are generic and can be shared with the ARM architecture. To achieve that goal, this patch moves ARM64 PSCI idle management code to drivers/firmware, so that the interface to initialize and enter idle states can actually be shared by ARM and ARM64 arches back-ends. The ARM generic CPUidle implementation also requires the definition of a cpuidle_ops section entry for the kernel to initialize the CPUidle operations at boot based on the enable-method (ie ARM64 has the statically initialized cpu_ops counterparts for that purpose); therefore this patch also adds the required section entry on CONFIG_ARM for PSCI so that the kernel can initialize the PSCI CPUidle back-end when PSCI is the probed enable-method. On ARM64 this patch provides no functional change. Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org> Acked-by: Catalin Marinas <catalin.marinas@arm.com> [arch/arm64] Acked-by: Mark Rutland <mark.rutland@arm.com> Tested-by: Jisheng Zhang <jszhang@marvell.com> Cc: Will Deacon <will.deacon@arm.com> Cc: Sudeep Holla <sudeep.holla@arm.com> Cc: Daniel Lezcano <daniel.lezcano@linaro.org> Cc: Catalin Marinas <catalin.marinas@arm.com> Cc: Mark Rutland <mark.rutland@arm.com> Cc: Jisheng Zhang <jszhang@marvell.com> Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
-rw-r--r--arch/arm/Kconfig2
-rw-r--r--arch/arm64/kernel/psci.c99
-rw-r--r--drivers/firmware/psci.c120
-rw-r--r--include/linux/psci.h3
4 files changed, 126 insertions, 98 deletions
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 8456f69bc77d..cc95ff8f07cb 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -2110,7 +2110,7 @@ config ARCH_SUSPEND_POSSIBLE
2110 def_bool y 2110 def_bool y
2111 2111
2112config ARM_CPU_SUSPEND 2112config ARM_CPU_SUSPEND
2113 def_bool PM_SLEEP || BL_SWITCHER 2113 def_bool PM_SLEEP || BL_SWITCHER || ARM_PSCI_FW
2114 depends on ARCH_SUSPEND_POSSIBLE 2114 depends on ARCH_SUSPEND_POSSIBLE
2115 2115
2116config ARCH_HIBERNATION_POSSIBLE 2116config ARCH_HIBERNATION_POSSIBLE
diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c
index f67f35b6edb1..42816bebb1e0 100644
--- a/arch/arm64/kernel/psci.c
+++ b/arch/arm64/kernel/psci.c
@@ -20,7 +20,6 @@
20#include <linux/smp.h> 20#include <linux/smp.h>
21#include <linux/delay.h> 21#include <linux/delay.h>
22#include <linux/psci.h> 22#include <linux/psci.h>
23#include <linux/slab.h>
24 23
25#include <uapi/linux/psci.h> 24#include <uapi/linux/psci.h>
26 25
@@ -28,73 +27,6 @@
28#include <asm/cpu_ops.h> 27#include <asm/cpu_ops.h>
29#include <asm/errno.h> 28#include <asm/errno.h>
30#include <asm/smp_plat.h> 29#include <asm/smp_plat.h>
31#include <asm/suspend.h>
32
33static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
34
35static int __maybe_unused cpu_psci_cpu_init_idle(unsigned int cpu)
36{
37 int i, ret, count = 0;
38 u32 *psci_states;
39 struct device_node *state_node, *cpu_node;
40
41 cpu_node = of_get_cpu_node(cpu, NULL);
42 if (!cpu_node)
43 return -ENODEV;
44
45 /*
46 * If the PSCI cpu_suspend function hook has not been initialized
47 * idle states must not be enabled, so bail out
48 */
49 if (!psci_ops.cpu_suspend)
50 return -EOPNOTSUPP;
51
52 /* Count idle states */
53 while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
54 count))) {
55 count++;
56 of_node_put(state_node);
57 }
58
59 if (!count)
60 return -ENODEV;
61
62 psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL);
63 if (!psci_states)
64 return -ENOMEM;
65
66 for (i = 0; i < count; i++) {
67 u32 state;
68
69 state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
70
71 ret = of_property_read_u32(state_node,
72 "arm,psci-suspend-param",
73 &state);
74 if (ret) {
75 pr_warn(" * %s missing arm,psci-suspend-param property\n",
76 state_node->full_name);
77 of_node_put(state_node);
78 goto free_mem;
79 }
80
81 of_node_put(state_node);
82 pr_debug("psci-power-state %#x index %d\n", state, i);
83 if (!psci_power_state_is_valid(state)) {
84 pr_warn("Invalid PSCI power state %#x\n", state);
85 ret = -EINVAL;
86 goto free_mem;
87 }
88 psci_states[i] = state;
89 }
90 /* Idle states parsed correctly, initialize per-cpu pointer */
91 per_cpu(psci_power_state, cpu) = psci_states;
92 return 0;
93
94free_mem:
95 kfree(psci_states);
96 return ret;
97}
98 30
99static int __init cpu_psci_cpu_init(unsigned int cpu) 31static int __init cpu_psci_cpu_init(unsigned int cpu)
100{ 32{
@@ -178,38 +110,11 @@ static int cpu_psci_cpu_kill(unsigned int cpu)
178} 110}
179#endif 111#endif
180 112
181static int psci_suspend_finisher(unsigned long index)
182{
183 u32 *state = __this_cpu_read(psci_power_state);
184
185 return psci_ops.cpu_suspend(state[index - 1],
186 virt_to_phys(cpu_resume));
187}
188
189static int __maybe_unused cpu_psci_cpu_suspend(unsigned long index)
190{
191 int ret;
192 u32 *state = __this_cpu_read(psci_power_state);
193 /*
194 * idle state index 0 corresponds to wfi, should never be called
195 * from the cpu_suspend operations
196 */
197 if (WARN_ON_ONCE(!index))
198 return -EINVAL;
199
200 if (!psci_power_state_loses_context(state[index - 1]))
201 ret = psci_ops.cpu_suspend(state[index - 1], 0);
202 else
203 ret = cpu_suspend(index, psci_suspend_finisher);
204
205 return ret;
206}
207
208const struct cpu_operations cpu_psci_ops = { 113const struct cpu_operations cpu_psci_ops = {
209 .name = "psci", 114 .name = "psci",
210#ifdef CONFIG_CPU_IDLE 115#ifdef CONFIG_CPU_IDLE
211 .cpu_init_idle = cpu_psci_cpu_init_idle, 116 .cpu_init_idle = psci_cpu_init_idle,
212 .cpu_suspend = cpu_psci_cpu_suspend, 117 .cpu_suspend = psci_cpu_suspend_enter,
213#endif 118#endif
214 .cpu_init = cpu_psci_cpu_init, 119 .cpu_init = cpu_psci_cpu_init,
215 .cpu_prepare = cpu_psci_cpu_prepare, 120 .cpu_prepare = cpu_psci_cpu_prepare,
diff --git a/drivers/firmware/psci.c b/drivers/firmware/psci.c
index f25cd79c8a79..11bfee8b79a9 100644
--- a/drivers/firmware/psci.c
+++ b/drivers/firmware/psci.c
@@ -14,6 +14,7 @@
14#define pr_fmt(fmt) "psci: " fmt 14#define pr_fmt(fmt) "psci: " fmt
15 15
16#include <linux/arm-smccc.h> 16#include <linux/arm-smccc.h>
17#include <linux/cpuidle.h>
17#include <linux/errno.h> 18#include <linux/errno.h>
18#include <linux/linkage.h> 19#include <linux/linkage.h>
19#include <linux/of.h> 20#include <linux/of.h>
@@ -21,10 +22,12 @@
21#include <linux/printk.h> 22#include <linux/printk.h>
22#include <linux/psci.h> 23#include <linux/psci.h>
23#include <linux/reboot.h> 24#include <linux/reboot.h>
25#include <linux/slab.h>
24#include <linux/suspend.h> 26#include <linux/suspend.h>
25 27
26#include <uapi/linux/psci.h> 28#include <uapi/linux/psci.h>
27 29
30#include <asm/cpuidle.h>
28#include <asm/cputype.h> 31#include <asm/cputype.h>
29#include <asm/system_misc.h> 32#include <asm/system_misc.h>
30#include <asm/smp_plat.h> 33#include <asm/smp_plat.h>
@@ -244,6 +247,123 @@ static int __init psci_features(u32 psci_func_id)
244 psci_func_id, 0, 0); 247 psci_func_id, 0, 0);
245} 248}
246 249
250#ifdef CONFIG_CPU_IDLE
251static DEFINE_PER_CPU_READ_MOSTLY(u32 *, psci_power_state);
252
253static int psci_dt_cpu_init_idle(struct device_node *cpu_node, int cpu)
254{
255 int i, ret, count = 0;
256 u32 *psci_states;
257 struct device_node *state_node;
258
259 /*
260 * If the PSCI cpu_suspend function hook has not been initialized
261 * idle states must not be enabled, so bail out
262 */
263 if (!psci_ops.cpu_suspend)
264 return -EOPNOTSUPP;
265
266 /* Count idle states */
267 while ((state_node = of_parse_phandle(cpu_node, "cpu-idle-states",
268 count))) {
269 count++;
270 of_node_put(state_node);
271 }
272
273 if (!count)
274 return -ENODEV;
275
276 psci_states = kcalloc(count, sizeof(*psci_states), GFP_KERNEL);
277 if (!psci_states)
278 return -ENOMEM;
279
280 for (i = 0; i < count; i++) {
281 u32 state;
282
283 state_node = of_parse_phandle(cpu_node, "cpu-idle-states", i);
284
285 ret = of_property_read_u32(state_node,
286 "arm,psci-suspend-param",
287 &state);
288 if (ret) {
289 pr_warn(" * %s missing arm,psci-suspend-param property\n",
290 state_node->full_name);
291 of_node_put(state_node);
292 goto free_mem;
293 }
294
295 of_node_put(state_node);
296 pr_debug("psci-power-state %#x index %d\n", state, i);
297 if (!psci_power_state_is_valid(state)) {
298 pr_warn("Invalid PSCI power state %#x\n", state);
299 ret = -EINVAL;
300 goto free_mem;
301 }
302 psci_states[i] = state;
303 }
304 /* Idle states parsed correctly, initialize per-cpu pointer */
305 per_cpu(psci_power_state, cpu) = psci_states;
306 return 0;
307
308free_mem:
309 kfree(psci_states);
310 return ret;
311}
312
313int psci_cpu_init_idle(unsigned int cpu)
314{
315 struct device_node *cpu_node;
316 int ret;
317
318 cpu_node = of_get_cpu_node(cpu, NULL);
319 if (!cpu_node)
320 return -ENODEV;
321
322 ret = psci_dt_cpu_init_idle(cpu_node, cpu);
323
324 of_node_put(cpu_node);
325
326 return ret;
327}
328
329static int psci_suspend_finisher(unsigned long index)
330{
331 u32 *state = __this_cpu_read(psci_power_state);
332
333 return psci_ops.cpu_suspend(state[index - 1],
334 virt_to_phys(cpu_resume));
335}
336
337int psci_cpu_suspend_enter(unsigned long index)
338{
339 int ret;
340 u32 *state = __this_cpu_read(psci_power_state);
341 /*
342 * idle state index 0 corresponds to wfi, should never be called
343 * from the cpu_suspend operations
344 */
345 if (WARN_ON_ONCE(!index))
346 return -EINVAL;
347
348 if (!psci_power_state_loses_context(state[index - 1]))
349 ret = psci_ops.cpu_suspend(state[index - 1], 0);
350 else
351 ret = cpu_suspend(index, psci_suspend_finisher);
352
353 return ret;
354}
355
356/* ARM specific CPU idle operations */
357#ifdef CONFIG_ARM
358static struct cpuidle_ops psci_cpuidle_ops __initdata = {
359 .suspend = psci_cpu_suspend_enter,
360 .init = psci_dt_cpu_init_idle,
361};
362
363CPUIDLE_METHOD_OF_DECLARE(psci, "arm,psci", &psci_cpuidle_ops);
364#endif
365#endif
366
247static int psci_system_suspend(unsigned long unused) 367static int psci_system_suspend(unsigned long unused)
248{ 368{
249 return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND), 369 return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND),
diff --git a/include/linux/psci.h b/include/linux/psci.h
index 12c4865457ad..393efe2edf9a 100644
--- a/include/linux/psci.h
+++ b/include/linux/psci.h
@@ -24,6 +24,9 @@ bool psci_tos_resident_on(int cpu);
24bool psci_power_state_loses_context(u32 state); 24bool psci_power_state_loses_context(u32 state);
25bool psci_power_state_is_valid(u32 state); 25bool psci_power_state_is_valid(u32 state);
26 26
27int psci_cpu_init_idle(unsigned int cpu);
28int psci_cpu_suspend_enter(unsigned long index);
29
27struct psci_operations { 30struct psci_operations {
28 int (*cpu_suspend)(u32 state, unsigned long entry_point); 31 int (*cpu_suspend)(u32 state, unsigned long entry_point);
29 int (*cpu_off)(u32 state); 32 int (*cpu_off)(u32 state);