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