1 /*
2 * xen/arch/arm/platforms/exynos5.c
3 *
4 * Exynos5 specific settings
5 *
6 * Julien Grall <julien.grall@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 #include <asm/p2m.h>
21 #include <xen/device_tree.h>
22 #include <xen/domain_page.h>
23 #include <xen/mm.h>
24 #include <xen/vmap.h>
25 #include <xen/delay.h>
26 #include <asm/platforms/exynos5.h>
27 #include <asm/platform.h>
28 #include <asm/io.h>
29
30 static bool secure_firmware;
31
32 #define EXYNOS_ARM_CORE0_CONFIG 0x2000
33 #define EXYNOS_ARM_CORE_CONFIG(_nr) (EXYNOS_ARM_CORE0_CONFIG + (0x80 * (_nr)))
34 #define EXYNOS_ARM_CORE_STATUS(_nr) (EXYNOS_ARM_CORE_CONFIG(_nr) + 0x4)
35 #define S5P_CORE_LOCAL_PWR_EN 0x3
36
37 #define SMC_CMD_CPU1BOOT (-4)
38
exynos5_init_time(void)39 static int exynos5_init_time(void)
40 {
41 uint32_t reg;
42 void __iomem *mct;
43 int rc;
44 struct dt_device_node *node;
45 u64 mct_base_addr;
46 u64 size;
47
48 node = dt_find_compatible_node(NULL, NULL, "samsung,exynos4210-mct");
49 if ( !node )
50 {
51 dprintk(XENLOG_ERR, "samsung,exynos4210-mct missing in DT\n");
52 return -ENXIO;
53 }
54
55 rc = dt_device_get_address(node, 0, &mct_base_addr, &size);
56 if ( rc )
57 {
58 dprintk(XENLOG_ERR, "Error in \"samsung,exynos4210-mct\"\n");
59 return -ENXIO;
60 }
61
62 dprintk(XENLOG_INFO, "mct_base_addr: %016llx size: %016llx\n",
63 mct_base_addr, size);
64
65 mct = ioremap_nocache(mct_base_addr, size);
66 if ( !mct )
67 {
68 dprintk(XENLOG_ERR, "Unable to map MCT\n");
69 return -ENOMEM;
70 }
71
72 /* Enable timer on Exynos 5250 should probably be done by u-boot */
73 reg = readl(mct + EXYNOS5_MCT_G_TCON);
74 writel(reg | EXYNOS5_MCT_G_TCON_START, mct + EXYNOS5_MCT_G_TCON);
75
76 iounmap(mct);
77
78 return 0;
79 }
80
81 /* Additional mappings for dom0 (Not in the DTS) */
exynos5250_specific_mapping(struct domain * d)82 static int exynos5250_specific_mapping(struct domain *d)
83 {
84 /* Map the chip ID */
85 map_mmio_regions(d, gaddr_to_gfn(EXYNOS5_PA_CHIPID), 1,
86 maddr_to_mfn(EXYNOS5_PA_CHIPID));
87
88 /* Map the PWM region */
89 map_mmio_regions(d, gaddr_to_gfn(EXYNOS5_PA_TIMER), 2,
90 maddr_to_mfn(EXYNOS5_PA_TIMER));
91
92 return 0;
93 }
94
exynos5_smp_init(void)95 static int __init exynos5_smp_init(void)
96 {
97 struct dt_device_node *node;
98 void __iomem *sysram;
99 char *compatible;
100 u64 sysram_addr;
101 u64 size;
102 u64 sysram_offset;
103 int rc;
104
105 node = dt_find_compatible_node(NULL, NULL, "samsung,secure-firmware");
106 if ( node )
107 {
108 /* Have to use sysram_ns_base_addr + 0x1c for boot address */
109 compatible = "samsung,exynos4210-sysram-ns";
110 sysram_offset = 0x1c;
111 secure_firmware = true;
112 printk("Running under secure firmware.\n");
113 }
114 else
115 {
116 /* Have to use sysram_base_addr + offset 0 for boot address */
117 compatible = "samsung,exynos4210-sysram";
118 sysram_offset = 0;
119 }
120
121 node = dt_find_compatible_node(NULL, NULL, compatible);
122 if ( !node )
123 {
124 dprintk(XENLOG_ERR, "%s missing in DT\n", compatible);
125 return -ENXIO;
126 }
127
128 rc = dt_device_get_address(node, 0, &sysram_addr, &size);
129 if ( rc )
130 {
131 dprintk(XENLOG_ERR, "Error in %s\n", compatible);
132 return -ENXIO;
133 }
134 dprintk(XENLOG_INFO, "sysram_addr: %016llx size: %016llx offset: %016llx\n",
135 sysram_addr, size, sysram_offset);
136
137 sysram = ioremap_nocache(sysram_addr, size);
138 if ( !sysram )
139 {
140 dprintk(XENLOG_ERR, "Unable to map exynos5 MMIO\n");
141 return -EFAULT;
142 }
143
144 printk("Set SYSRAM to %"PRIpaddr" (%p)\n",
145 __pa(init_secondary), init_secondary);
146 writel(__pa(init_secondary), sysram + sysram_offset);
147
148 iounmap(sysram);
149
150 return 0;
151 }
152
exynos_cpu_power_state(void __iomem * power,int cpu)153 static int exynos_cpu_power_state(void __iomem *power, int cpu)
154 {
155 return __raw_readl(power + EXYNOS_ARM_CORE_STATUS(cpu)) &
156 S5P_CORE_LOCAL_PWR_EN;
157 }
158
exynos_cpu_power_up(void __iomem * power,int cpu)159 static void exynos_cpu_power_up(void __iomem *power, int cpu)
160 {
161 __raw_writel(S5P_CORE_LOCAL_PWR_EN,
162 power + EXYNOS_ARM_CORE_CONFIG(cpu));
163 }
164
exynos5_cpu_power_up(void __iomem * power,int cpu)165 static int exynos5_cpu_power_up(void __iomem *power, int cpu)
166 {
167 unsigned int timeout;
168
169 if ( !exynos_cpu_power_state(power, cpu) )
170 {
171 exynos_cpu_power_up(power, cpu);
172 timeout = 10;
173
174 /* wait max 10 ms until cpu is on */
175 while ( exynos_cpu_power_state(power, cpu) != S5P_CORE_LOCAL_PWR_EN )
176 {
177 mdelay(1);
178
179 if ( --timeout == 0 )
180 break;
181 }
182
183 if ( timeout == 0 )
184 {
185 dprintk(XENLOG_ERR, "CPU%d power enable failed\n", cpu);
186 return -ETIMEDOUT;
187 }
188 }
189 return 0;
190 }
191
exynos5_get_pmu_baseandsize(u64 * power_base_addr,u64 * size)192 static int exynos5_get_pmu_baseandsize(u64 *power_base_addr, u64 *size)
193 {
194 struct dt_device_node *node;
195 int rc;
196 static const struct dt_device_match exynos_dt_pmu_matches[] =
197 {
198 DT_MATCH_COMPATIBLE("samsung,exynos5250-pmu"),
199 DT_MATCH_COMPATIBLE("samsung,exynos5410-pmu"),
200 DT_MATCH_COMPATIBLE("samsung,exynos5420-pmu"),
201 { /*sentinel*/ },
202 };
203
204 node = dt_find_matching_node(NULL, exynos_dt_pmu_matches);
205 if ( !node )
206 {
207 dprintk(XENLOG_ERR, "samsung,exynos5XXX-pmu missing in DT\n");
208 return -ENXIO;
209 }
210
211 rc = dt_device_get_address(node, 0, power_base_addr, size);
212 if ( rc )
213 {
214 dprintk(XENLOG_ERR, "Error in \"samsung,exynos5XXX-pmu\"\n");
215 return -ENXIO;
216 }
217
218 dprintk(XENLOG_DEBUG, "power_base_addr: %016llx size: %016llx\n",
219 *power_base_addr, *size);
220
221 return 0;
222 }
223
exynos5_cpu_up(int cpu)224 static int exynos5_cpu_up(int cpu)
225 {
226 u64 power_base_addr;
227 u64 size;
228 void __iomem *power;
229 int rc;
230
231 rc = exynos5_get_pmu_baseandsize(&power_base_addr, &size);
232 if ( rc )
233 return rc;
234
235 power = ioremap_nocache(power_base_addr, size);
236 if ( !power )
237 {
238 dprintk(XENLOG_ERR, "Unable to map power MMIO\n");
239 return -EFAULT;
240 }
241
242 rc = exynos5_cpu_power_up(power, cpu);
243 if ( rc )
244 {
245 iounmap(power);
246 return -ETIMEDOUT;
247 }
248
249 iounmap(power);
250
251 if ( secure_firmware )
252 call_smc(SMC_CMD_CPU1BOOT, cpu, 0, 0);
253
254 return cpu_up_send_sgi(cpu);
255 }
256
exynos5_reset(void)257 static void exynos5_reset(void)
258 {
259 u64 power_base_addr;
260 u64 size;
261 void __iomem *pmu;
262 int rc;
263
264 rc = exynos5_get_pmu_baseandsize(&power_base_addr, &size);
265 if ( rc )
266 return;
267
268 pmu = ioremap_nocache(power_base_addr, size);
269 if ( !pmu )
270 {
271 dprintk(XENLOG_ERR, "Unable to map PMU\n");
272 return;
273 }
274
275 writel(1, pmu + EXYNOS5_SWRESET);
276
277 iounmap(pmu);
278 }
279
280 static const struct dt_device_match exynos5_blacklist_dev[] __initconst =
281 {
282 /* Multi core Timer
283 * TODO: this device set up IRQ to CPU 1 which is not yet handled by Xen.
284 * This is result to random freeze.
285 */
286 DT_MATCH_COMPATIBLE("samsung,exynos4210-mct"),
287 DT_MATCH_COMPATIBLE("samsung,secure-firmware"),
288 { /* sentinel */ },
289 };
290
291 static const char * const exynos5250_dt_compat[] __initconst =
292 {
293 "samsung,exynos5250",
294 NULL
295 };
296
297 static const char * const exynos5_dt_compat[] __initconst =
298 {
299 "samsung,exynos5410",
300 NULL
301 };
302
303 PLATFORM_START(exynos5250, "SAMSUNG EXYNOS5250")
304 .compatible = exynos5250_dt_compat,
305 .init_time = exynos5_init_time,
306 .specific_mapping = exynos5250_specific_mapping,
307 .smp_init = exynos5_smp_init,
308 .cpu_up = cpu_up_send_sgi,
309 .reset = exynos5_reset,
310 .blacklist_dev = exynos5_blacklist_dev,
311 PLATFORM_END
312
313 PLATFORM_START(exynos5, "SAMSUNG EXYNOS5")
314 .compatible = exynos5_dt_compat,
315 .init_time = exynos5_init_time,
316 .smp_init = exynos5_smp_init,
317 .cpu_up = exynos5_cpu_up,
318 .reset = exynos5_reset,
319 .blacklist_dev = exynos5_blacklist_dev,
320 PLATFORM_END
321
322 /*
323 * Local variables:
324 * mode: C
325 * c-file-style: "BSD"
326 * c-basic-offset: 4
327 * indent-tabs-mode: nil
328 * End:
329 */
330