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