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/mm.h>
23 #include <xen/smp.h>
24 #include <asm/psci.h>
25 #include <asm/acpi.h>
26 
27 /*
28  * While a 64-bit OS can make calls with SMC32 calling conventions, for
29  * some calls it is necessary to use SMC64 to pass or return 64-bit values.
30  * For such calls PSCI_0_2_FN_NATIVE(x) will choose the appropriate
31  * (native-width) function ID.
32  */
33 #ifdef CONFIG_ARM_64
34 #define PSCI_0_2_FN_NATIVE(name)    PSCI_0_2_FN64(name)
35 #else
36 #define PSCI_0_2_FN_NATIVE(name)    PSCI_0_2_FN32(name)
37 #endif
38 
39 uint32_t psci_ver;
40 
41 static uint32_t psci_cpu_on_nr;
42 
call_psci_cpu_on(int cpu)43 int call_psci_cpu_on(int cpu)
44 {
45     return call_smc(psci_cpu_on_nr, cpu_logical_map(cpu), __pa(init_secondary), 0);
46 }
47 
call_psci_system_off(void)48 void call_psci_system_off(void)
49 {
50     if ( psci_ver > PSCI_VERSION(0, 1) )
51         call_smc(PSCI_0_2_FN32(SYSTEM_OFF), 0, 0, 0);
52 }
53 
call_psci_system_reset(void)54 void call_psci_system_reset(void)
55 {
56     if ( psci_ver > PSCI_VERSION(0, 1) )
57         call_smc(PSCI_0_2_FN32(SYSTEM_RESET), 0, 0, 0);
58 }
59 
psci_is_smc_method(const struct dt_device_node * psci)60 int __init psci_is_smc_method(const struct dt_device_node *psci)
61 {
62     int ret;
63     const char *prop_str;
64 
65     ret = dt_property_read_string(psci, "method", &prop_str);
66     if ( ret )
67     {
68         printk("/psci node does not provide a method (%d)\n", ret);
69         return -EINVAL;
70     }
71 
72     /* Since Xen runs in HYP all of the time, it does not make sense to
73      * let it call into HYP for PSCI handling, since the handler just
74      * won't be there. So bail out with an error if "smc" is not used.
75      */
76     if ( strcmp(prop_str, "smc") )
77     {
78         printk("/psci method must be smc, but is: \"%s\"\n", prop_str);
79         return -EINVAL;
80     }
81 
82     return 0;
83 }
84 
psci_init_0_1(void)85 int __init psci_init_0_1(void)
86 {
87     int ret;
88     const struct dt_device_node *psci;
89 
90     if ( !acpi_disabled )
91     {
92         printk("PSCI 0.1 is not supported when using ACPI\n");
93         return -EINVAL;
94     }
95 
96     psci = dt_find_compatible_node(NULL, NULL, "arm,psci");
97     if ( !psci )
98         return -EOPNOTSUPP;
99 
100     ret = psci_is_smc_method(psci);
101     if ( ret )
102         return -EINVAL;
103 
104     if ( !dt_property_read_u32(psci, "cpu_on", &psci_cpu_on_nr) )
105     {
106         printk("/psci node is missing the \"cpu_on\" property\n");
107         return -ENOENT;
108     }
109 
110     psci_ver = PSCI_VERSION(0, 1);
111 
112     printk(XENLOG_INFO "Using PSCI-0.1 for SMP bringup\n");
113 
114     return 0;
115 }
116 
psci_init_0_2(void)117 int __init psci_init_0_2(void)
118 {
119     static const struct dt_device_match psci_ids[] __initconst =
120     {
121         DT_MATCH_COMPATIBLE("arm,psci-0.2"),
122         DT_MATCH_COMPATIBLE("arm,psci-1.0"),
123         { /* sentinel */ },
124     };
125     int ret;
126 
127     if ( acpi_disabled )
128     {
129         const struct dt_device_node *psci;
130 
131         psci = dt_find_matching_node(NULL, psci_ids);
132         if ( !psci )
133             return -EOPNOTSUPP;
134 
135         ret = psci_is_smc_method(psci);
136         if ( ret )
137             return -EINVAL;
138     }
139     else
140     {
141         if ( acpi_psci_hvc_present() ) {
142             printk("PSCI conduit must be SMC, but is HVC\n");
143             return -EINVAL;
144         }
145     }
146 
147     psci_ver = call_smc(PSCI_0_2_FN32(PSCI_VERSION), 0, 0, 0);
148 
149     /* For the moment, we only support PSCI 0.2 and PSCI 1.x */
150     if ( psci_ver != PSCI_VERSION(0, 2) && PSCI_VERSION_MAJOR(psci_ver) != 1 )
151     {
152         printk("Error: Unrecognized PSCI version %u.%u\n",
153                PSCI_VERSION_MAJOR(psci_ver), PSCI_VERSION_MINOR(psci_ver));
154         return -EOPNOTSUPP;
155     }
156 
157     psci_cpu_on_nr = PSCI_0_2_FN_NATIVE(CPU_ON);
158 
159     printk(XENLOG_INFO "Using PSCI-%u.%u for SMP bringup\n",
160            PSCI_VERSION_MAJOR(psci_ver), PSCI_VERSION_MINOR(psci_ver));
161 
162     return 0;
163 }
164 
psci_init(void)165 int __init psci_init(void)
166 {
167     int ret;
168 
169     if ( !acpi_disabled && !acpi_psci_present() )
170         return -EOPNOTSUPP;
171 
172     ret = psci_init_0_2();
173     if ( ret )
174         ret = psci_init_0_1();
175 
176     return ret;
177 }
178 
179 /*
180  * Local variables:
181  * mode: C
182  * c-file-style: "BSD"
183  * c-basic-offset: 4
184  * indent-tabs-mode: nil
185  * End:
186  */
187