aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2013-01-23 17:44:13 -0500
committerRussell King <rmk+kernel@arm.linux.org.uk>2013-01-23 17:44:13 -0500
commit6629096a5eb861b6d91fe9c2204414d9b072d161 (patch)
tree2b1259c2a58acd4314fef12ae771b74c4521f448
parentff70ca7330762089402b0e5ab188c5ae1a8d328b (diff)
parent2bdd424f26be1c98b6e3d9acfffb5559c131c888 (diff)
Merge branch 'for-rmk/virt/psci' of git://git.kernel.org/pub/scm/linux/kernel/git/will/linux into devel-stable
-rw-r--r--Documentation/devicetree/bindings/arm/psci.txt55
-rw-r--r--arch/arm/Kconfig10
-rw-r--r--arch/arm/include/asm/opcodes-sec.h24
-rw-r--r--arch/arm/include/asm/opcodes.h1
-rw-r--r--arch/arm/include/asm/psci.h36
-rw-r--r--arch/arm/kernel/Makefile1
-rw-r--r--arch/arm/kernel/psci.c211
7 files changed, 338 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/arm/psci.txt b/Documentation/devicetree/bindings/arm/psci.txt
new file mode 100644
index 000000000000..433afe9cb590
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/psci.txt
@@ -0,0 +1,55 @@
1* Power State Coordination Interface (PSCI)
2
3Firmware implementing the PSCI functions described in ARM document number
4ARM DEN 0022A ("Power State Coordination Interface System Software on ARM
5processors") can be used by Linux to initiate various CPU-centric power
6operations.
7
8Issue A of the specification describes functions for CPU suspend, hotplug
9and migration of secure software.
10
11Functions are invoked by trapping to the privilege level of the PSCI
12firmware (specified as part of the binding below) and passing arguments
13in a manner similar to that specified by AAPCS:
14
15 r0 => 32-bit Function ID / return value
16 {r1 - r3} => Parameters
17
18Note that the immediate field of the trapping instruction must be set
19to #0.
20
21
22Main node required properties:
23
24 - compatible : Must be "arm,psci"
25
26 - method : The method of calling the PSCI firmware. Permitted
27 values are:
28
29 "smc" : SMC #0, with the register assignments specified
30 in this binding.
31
32 "hvc" : HVC #0, with the register assignments specified
33 in this binding.
34
35Main node optional properties:
36
37 - cpu_suspend : Function ID for CPU_SUSPEND operation
38
39 - cpu_off : Function ID for CPU_OFF operation
40
41 - cpu_on : Function ID for CPU_ON operation
42
43 - migrate : Function ID for MIGRATE operation
44
45
46Example:
47
48 psci {
49 compatible = "arm,psci";
50 method = "smc";
51 cpu_suspend = <0x95c10000>;
52 cpu_off = <0x95c10001>;
53 cpu_on = <0x95c10002>;
54 migrate = <0x95c10003>;
55 };
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 67874b82a4ed..96863b860641 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -1620,6 +1620,16 @@ config HOTPLUG_CPU
1620 Say Y here to experiment with turning CPUs off and on. CPUs 1620 Say Y here to experiment with turning CPUs off and on. CPUs
1621 can be controlled through /sys/devices/system/cpu. 1621 can be controlled through /sys/devices/system/cpu.
1622 1622
1623config ARM_PSCI
1624 bool "Support for the ARM Power State Coordination Interface (PSCI)"
1625 depends on CPU_V7
1626 help
1627 Say Y here if you want Linux to communicate with system firmware
1628 implementing the PSCI specification for CPU-centric power
1629 management operations described in ARM document number ARM DEN
1630 0022A ("Power State Coordination Interface System Software on
1631 ARM processors").
1632
1623config LOCAL_TIMERS 1633config LOCAL_TIMERS
1624 bool "Use local timer interrupts" 1634 bool "Use local timer interrupts"
1625 depends on SMP 1635 depends on SMP
diff --git a/arch/arm/include/asm/opcodes-sec.h b/arch/arm/include/asm/opcodes-sec.h
new file mode 100644
index 000000000000..bc3a9174417c
--- /dev/null
+++ b/arch/arm/include/asm/opcodes-sec.h
@@ -0,0 +1,24 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License version 2 as
4 * published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 *
11 * Copyright (C) 2012 ARM Limited
12 */
13
14#ifndef __ASM_ARM_OPCODES_SEC_H
15#define __ASM_ARM_OPCODES_SEC_H
16
17#include <asm/opcodes.h>
18
19#define __SMC(imm4) __inst_arm_thumb32( \
20 0xE1600070 | (((imm4) & 0xF) << 0), \
21 0xF7F08000 | (((imm4) & 0xF) << 16) \
22)
23
24#endif /* __ASM_ARM_OPCODES_SEC_H */
diff --git a/arch/arm/include/asm/opcodes.h b/arch/arm/include/asm/opcodes.h
index 74e211a6fb24..e796c598513b 100644
--- a/arch/arm/include/asm/opcodes.h
+++ b/arch/arm/include/asm/opcodes.h
@@ -10,6 +10,7 @@
10#define __ASM_ARM_OPCODES_H 10#define __ASM_ARM_OPCODES_H
11 11
12#ifndef __ASSEMBLY__ 12#ifndef __ASSEMBLY__
13#include <linux/linkage.h>
13extern asmlinkage unsigned int arm_check_condition(u32 opcode, u32 psr); 14extern asmlinkage unsigned int arm_check_condition(u32 opcode, u32 psr);
14#endif 15#endif
15 16
diff --git a/arch/arm/include/asm/psci.h b/arch/arm/include/asm/psci.h
new file mode 100644
index 000000000000..ce0dbe7c1625
--- /dev/null
+++ b/arch/arm/include/asm/psci.h
@@ -0,0 +1,36 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License version 2 as
4 * published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 *
11 * Copyright (C) 2012 ARM Limited
12 */
13
14#ifndef __ASM_ARM_PSCI_H
15#define __ASM_ARM_PSCI_H
16
17#define PSCI_POWER_STATE_TYPE_STANDBY 0
18#define PSCI_POWER_STATE_TYPE_POWER_DOWN 1
19
20struct psci_power_state {
21 u16 id;
22 u8 type;
23 u8 affinity_level;
24};
25
26struct psci_operations {
27 int (*cpu_suspend)(struct psci_power_state state,
28 unsigned long entry_point);
29 int (*cpu_off)(struct psci_power_state state);
30 int (*cpu_on)(unsigned long cpuid, unsigned long entry_point);
31 int (*migrate)(unsigned long cpuid);
32};
33
34extern struct psci_operations psci_ops;
35
36#endif /* __ASM_ARM_PSCI_H */
diff --git a/arch/arm/kernel/Makefile b/arch/arm/kernel/Makefile
index 5bbec7b8183e..5f3338eacad2 100644
--- a/arch/arm/kernel/Makefile
+++ b/arch/arm/kernel/Makefile
@@ -82,5 +82,6 @@ obj-$(CONFIG_DEBUG_LL) += debug.o
82obj-$(CONFIG_EARLY_PRINTK) += early_printk.o 82obj-$(CONFIG_EARLY_PRINTK) += early_printk.o
83 83
84obj-$(CONFIG_ARM_VIRT_EXT) += hyp-stub.o 84obj-$(CONFIG_ARM_VIRT_EXT) += hyp-stub.o
85obj-$(CONFIG_ARM_PSCI) += psci.o
85 86
86extra-y := $(head-y) vmlinux.lds 87extra-y := $(head-y) vmlinux.lds
diff --git a/arch/arm/kernel/psci.c b/arch/arm/kernel/psci.c
new file mode 100644
index 000000000000..36531643cc2c
--- /dev/null
+++ b/arch/arm/kernel/psci.c
@@ -0,0 +1,211 @@
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License version 2 as
4 * published by the Free Software Foundation.
5 *
6 * This program is distributed in the hope that it will be useful,
7 * but WITHOUT ANY WARRANTY; without even the implied warranty of
8 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 * GNU General Public License for more details.
10 *
11 * Copyright (C) 2012 ARM Limited
12 *
13 * Author: Will Deacon <will.deacon@arm.com>
14 */
15
16#define pr_fmt(fmt) "psci: " fmt
17
18#include <linux/init.h>
19#include <linux/of.h>
20
21#include <asm/compiler.h>
22#include <asm/errno.h>
23#include <asm/opcodes-sec.h>
24#include <asm/opcodes-virt.h>
25#include <asm/psci.h>
26
27struct psci_operations psci_ops;
28
29static int (*invoke_psci_fn)(u32, u32, u32, u32);
30
31enum psci_function {
32 PSCI_FN_CPU_SUSPEND,
33 PSCI_FN_CPU_ON,
34 PSCI_FN_CPU_OFF,
35 PSCI_FN_MIGRATE,
36 PSCI_FN_MAX,
37};
38
39static u32 psci_function_id[PSCI_FN_MAX];
40
41#define PSCI_RET_SUCCESS 0
42#define PSCI_RET_EOPNOTSUPP -1
43#define PSCI_RET_EINVAL -2
44#define PSCI_RET_EPERM -3
45
46static int psci_to_linux_errno(int errno)
47{
48 switch (errno) {
49 case PSCI_RET_SUCCESS:
50 return 0;
51 case PSCI_RET_EOPNOTSUPP:
52 return -EOPNOTSUPP;
53 case PSCI_RET_EINVAL:
54 return -EINVAL;
55 case PSCI_RET_EPERM:
56 return -EPERM;
57 };
58
59 return -EINVAL;
60}
61
62#define PSCI_POWER_STATE_ID_MASK 0xffff
63#define PSCI_POWER_STATE_ID_SHIFT 0
64#define PSCI_POWER_STATE_TYPE_MASK 0x1
65#define PSCI_POWER_STATE_TYPE_SHIFT 16
66#define PSCI_POWER_STATE_AFFL_MASK 0x3
67#define PSCI_POWER_STATE_AFFL_SHIFT 24
68
69static u32 psci_power_state_pack(struct psci_power_state state)
70{
71 return ((state.id & PSCI_POWER_STATE_ID_MASK)
72 << PSCI_POWER_STATE_ID_SHIFT) |
73 ((state.type & PSCI_POWER_STATE_TYPE_MASK)
74 << PSCI_POWER_STATE_TYPE_SHIFT) |
75 ((state.affinity_level & PSCI_POWER_STATE_AFFL_MASK)
76 << PSCI_POWER_STATE_AFFL_SHIFT);
77}
78
79/*
80 * The following two functions are invoked via the invoke_psci_fn pointer
81 * and will not be inlined, allowing us to piggyback on the AAPCS.
82 */
83static noinline int __invoke_psci_fn_hvc(u32 function_id, u32 arg0, u32 arg1,
84 u32 arg2)
85{
86 asm volatile(
87 __asmeq("%0", "r0")
88 __asmeq("%1", "r1")
89 __asmeq("%2", "r2")
90 __asmeq("%3", "r3")
91 __HVC(0)
92 : "+r" (function_id)
93 : "r" (arg0), "r" (arg1), "r" (arg2));
94
95 return function_id;
96}
97
98static noinline int __invoke_psci_fn_smc(u32 function_id, u32 arg0, u32 arg1,
99 u32 arg2)
100{
101 asm volatile(
102 __asmeq("%0", "r0")
103 __asmeq("%1", "r1")
104 __asmeq("%2", "r2")
105 __asmeq("%3", "r3")
106 __SMC(0)
107 : "+r" (function_id)
108 : "r" (arg0), "r" (arg1), "r" (arg2));
109
110 return function_id;
111}
112
113static int psci_cpu_suspend(struct psci_power_state state,
114 unsigned long entry_point)
115{
116 int err;
117 u32 fn, power_state;
118
119 fn = psci_function_id[PSCI_FN_CPU_SUSPEND];
120 power_state = psci_power_state_pack(state);
121 err = invoke_psci_fn(fn, power_state, entry_point, 0);
122 return psci_to_linux_errno(err);
123}
124
125static int psci_cpu_off(struct psci_power_state state)
126{
127 int err;
128 u32 fn, power_state;
129
130 fn = psci_function_id[PSCI_FN_CPU_OFF];
131 power_state = psci_power_state_pack(state);
132 err = invoke_psci_fn(fn, power_state, 0, 0);
133 return psci_to_linux_errno(err);
134}
135
136static int psci_cpu_on(unsigned long cpuid, unsigned long entry_point)
137{
138 int err;
139 u32 fn;
140
141 fn = psci_function_id[PSCI_FN_CPU_ON];
142 err = invoke_psci_fn(fn, cpuid, entry_point, 0);
143 return psci_to_linux_errno(err);
144}
145
146static int psci_migrate(unsigned long cpuid)
147{
148 int err;
149 u32 fn;
150
151 fn = psci_function_id[PSCI_FN_MIGRATE];
152 err = invoke_psci_fn(fn, cpuid, 0, 0);
153 return psci_to_linux_errno(err);
154}
155
156static const struct of_device_id psci_of_match[] __initconst = {
157 { .compatible = "arm,psci", },
158 {},
159};
160
161static int __init psci_init(void)
162{
163 struct device_node *np;
164 const char *method;
165 u32 id;
166
167 np = of_find_matching_node(NULL, psci_of_match);
168 if (!np)
169 return 0;
170
171 pr_info("probing function IDs from device-tree\n");
172
173 if (of_property_read_string(np, "method", &method)) {
174 pr_warning("missing \"method\" property\n");
175 goto out_put_node;
176 }
177
178 if (!strcmp("hvc", method)) {
179 invoke_psci_fn = __invoke_psci_fn_hvc;
180 } else if (!strcmp("smc", method)) {
181 invoke_psci_fn = __invoke_psci_fn_smc;
182 } else {
183 pr_warning("invalid \"method\" property: %s\n", method);
184 goto out_put_node;
185 }
186
187 if (!of_property_read_u32(np, "cpu_suspend", &id)) {
188 psci_function_id[PSCI_FN_CPU_SUSPEND] = id;
189 psci_ops.cpu_suspend = psci_cpu_suspend;
190 }
191
192 if (!of_property_read_u32(np, "cpu_off", &id)) {
193 psci_function_id[PSCI_FN_CPU_OFF] = id;
194 psci_ops.cpu_off = psci_cpu_off;
195 }
196
197 if (!of_property_read_u32(np, "cpu_on", &id)) {
198 psci_function_id[PSCI_FN_CPU_ON] = id;
199 psci_ops.cpu_on = psci_cpu_on;
200 }
201
202 if (!of_property_read_u32(np, "migrate", &id)) {
203 psci_function_id[PSCI_FN_MIGRATE] = id;
204 psci_ops.migrate = psci_migrate;
205 }
206
207out_put_node:
208 of_node_put(np);
209 return 0;
210}
211early_initcall(psci_init);