1 /*
2 * xen/arch/arm/psci.c
3 *
4 * PSCI host support
5 *
6 * Andre Przywara <andre.przywara@linaro.org>
7 * Copyright (c) 2013 Linaro Limited.
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
19
20
21 #include <xen/types.h>
22 #include <xen/init.h>
23 #include <xen/mm.h>
24 #include <xen/smp.h>
25 #include <asm/cpufeature.h>
26 #include <asm/psci.h>
27 #include <asm/acpi.h>
28
29 /*
30 * While a 64-bit OS can make calls with SMC32 calling conventions, for
31 * some calls it is necessary to use SMC64 to pass or return 64-bit values.
32 * For such calls PSCI_0_2_FN_NATIVE(x) will choose the appropriate
33 * (native-width) function ID.
34 */
35 #ifdef CONFIG_ARM_64
36 #define PSCI_0_2_FN_NATIVE(name) PSCI_0_2_FN64_##name
37 #else
38 #define PSCI_0_2_FN_NATIVE(name) PSCI_0_2_FN32_##name
39 #endif
40
41 uint32_t psci_ver;
42 uint32_t smccc_ver;
43
44 static uint32_t psci_cpu_on_nr;
45
46 #define PSCI_RET(res) ((int32_t)(res).a0)
47
call_psci_cpu_on(int cpu)48 int call_psci_cpu_on(int cpu)
49 {
50 struct arm_smccc_res res;
51
52 arm_smccc_smc(psci_cpu_on_nr, cpu_logical_map(cpu), __pa(init_secondary),
53 &res);
54
55 return PSCI_RET(res);
56 }
57
call_psci_cpu_off(void)58 void call_psci_cpu_off(void)
59 {
60 if ( psci_ver > PSCI_VERSION(0, 1) )
61 {
62 struct arm_smccc_res res;
63
64 /* If successfull the PSCI cpu_off call doesn't return */
65 arm_smccc_smc(PSCI_0_2_FN32_CPU_OFF, &res);
66 panic("PSCI cpu off failed for CPU%d err=%d\n", smp_processor_id(),
67 PSCI_RET(res));
68 }
69 }
70
call_psci_system_off(void)71 void call_psci_system_off(void)
72 {
73 if ( psci_ver > PSCI_VERSION(0, 1) )
74 arm_smccc_smc(PSCI_0_2_FN32_SYSTEM_OFF, NULL);
75 }
76
call_psci_system_reset(void)77 void call_psci_system_reset(void)
78 {
79 if ( psci_ver > PSCI_VERSION(0, 1) )
80 arm_smccc_smc(PSCI_0_2_FN32_SYSTEM_RESET, NULL);
81 }
82
psci_features(uint32_t psci_func_id)83 static int __init psci_features(uint32_t psci_func_id)
84 {
85 struct arm_smccc_res res;
86
87 if ( psci_ver < PSCI_VERSION(1, 0) )
88 return PSCI_NOT_SUPPORTED;
89
90 arm_smccc_smc(PSCI_1_0_FN32_PSCI_FEATURES, psci_func_id, &res);
91
92 return PSCI_RET(res);
93 }
94
psci_is_smc_method(const struct dt_device_node * psci)95 static int __init psci_is_smc_method(const struct dt_device_node *psci)
96 {
97 int ret;
98 const char *prop_str;
99
100 ret = dt_property_read_string(psci, "method", &prop_str);
101 if ( ret )
102 {
103 printk("/psci node does not provide a method (%d)\n", ret);
104 return -EINVAL;
105 }
106
107 /* Since Xen runs in HYP all of the time, it does not make sense to
108 * let it call into HYP for PSCI handling, since the handler just
109 * won't be there. So bail out with an error if "smc" is not used.
110 */
111 if ( strcmp(prop_str, "smc") )
112 {
113 printk("/psci method must be smc, but is: \"%s\"\n", prop_str);
114 return -EINVAL;
115 }
116
117 return 0;
118 }
119
psci_init_smccc(void)120 static void __init psci_init_smccc(void)
121 {
122 /* PSCI is using at least SMCCC 1.0 calling convention. */
123 smccc_ver = ARM_SMCCC_VERSION_1_0;
124
125 if ( psci_features(ARM_SMCCC_VERSION_FID) != PSCI_NOT_SUPPORTED )
126 {
127 struct arm_smccc_res res;
128
129 arm_smccc_smc(ARM_SMCCC_VERSION_FID, &res);
130 if ( PSCI_RET(res) != ARM_SMCCC_NOT_SUPPORTED )
131 smccc_ver = PSCI_RET(res);
132 }
133
134 if ( smccc_ver >= SMCCC_VERSION(1, 1) )
135 cpus_set_cap(ARM_SMCCC_1_1);
136
137 printk(XENLOG_INFO "Using SMC Calling Convention v%u.%u\n",
138 SMCCC_VERSION_MAJOR(smccc_ver), SMCCC_VERSION_MINOR(smccc_ver));
139 }
140
psci_init_0_1(void)141 static int __init psci_init_0_1(void)
142 {
143 int ret;
144 const struct dt_device_node *psci;
145
146 if ( !acpi_disabled )
147 {
148 printk("PSCI 0.1 is not supported when using ACPI\n");
149 return -EINVAL;
150 }
151
152 psci = dt_find_compatible_node(NULL, NULL, "arm,psci");
153 if ( !psci )
154 return -EOPNOTSUPP;
155
156 ret = psci_is_smc_method(psci);
157 if ( ret )
158 return -EINVAL;
159
160 if ( !dt_property_read_u32(psci, "cpu_on", &psci_cpu_on_nr) )
161 {
162 printk("/psci node is missing the \"cpu_on\" property\n");
163 return -ENOENT;
164 }
165
166 psci_ver = PSCI_VERSION(0, 1);
167
168 return 0;
169 }
170
psci_init_0_2(void)171 static int __init psci_init_0_2(void)
172 {
173 static const struct dt_device_match psci_ids[] __initconst =
174 {
175 DT_MATCH_COMPATIBLE("arm,psci-0.2"),
176 DT_MATCH_COMPATIBLE("arm,psci-1.0"),
177 { /* sentinel */ },
178 };
179 int ret;
180 struct arm_smccc_res res;
181
182 if ( acpi_disabled )
183 {
184 const struct dt_device_node *psci;
185
186 psci = dt_find_matching_node(NULL, psci_ids);
187 if ( !psci )
188 return -EOPNOTSUPP;
189
190 ret = psci_is_smc_method(psci);
191 if ( ret )
192 return -EINVAL;
193 }
194 else
195 {
196 if ( acpi_psci_hvc_present() ) {
197 printk("PSCI conduit must be SMC, but is HVC\n");
198 return -EINVAL;
199 }
200 }
201
202 arm_smccc_smc(PSCI_0_2_FN32_PSCI_VERSION, &res);
203 psci_ver = PSCI_RET(res);
204
205 /* For the moment, we only support PSCI 0.2 and PSCI 1.x */
206 if ( psci_ver != PSCI_VERSION(0, 2) && PSCI_VERSION_MAJOR(psci_ver) != 1 )
207 {
208 printk("Error: Unrecognized PSCI version %u.%u\n",
209 PSCI_VERSION_MAJOR(psci_ver), PSCI_VERSION_MINOR(psci_ver));
210 return -EOPNOTSUPP;
211 }
212
213 psci_cpu_on_nr = PSCI_0_2_FN_NATIVE(CPU_ON);
214
215 return 0;
216 }
217
psci_init(void)218 int __init psci_init(void)
219 {
220 int ret;
221
222 if ( !acpi_disabled && !acpi_psci_present() )
223 return -EOPNOTSUPP;
224
225 ret = psci_init_0_2();
226 if ( ret )
227 ret = psci_init_0_1();
228
229 if ( ret )
230 return ret;
231
232 psci_init_smccc();
233
234 printk(XENLOG_INFO "Using PSCI v%u.%u\n",
235 PSCI_VERSION_MAJOR(psci_ver), PSCI_VERSION_MINOR(psci_ver));
236
237 return 0;
238 }
239
240 /*
241 * Local variables:
242 * mode: C
243 * c-file-style: "BSD"
244 * c-basic-offset: 4
245 * indent-tabs-mode: nil
246 * End:
247 */
248