ARM: EXYNOS: Map SYSRAM through generic DT bindings
[linux-2.6-block.git] / arch / arm / mach-exynos / platsmp.c
1 /* linux/arch/arm/mach-exynos4/platsmp.c
2  *
3  * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
4  *              http://www.samsung.com
5  *
6  * Cloned from linux/arch/arm/mach-vexpress/platsmp.c
7  *
8  *  Copyright (C) 2002 ARM Ltd.
9  *  All Rights Reserved
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License version 2 as
13  * published by the Free Software Foundation.
14 */
15
16 #include <linux/init.h>
17 #include <linux/errno.h>
18 #include <linux/delay.h>
19 #include <linux/device.h>
20 #include <linux/jiffies.h>
21 #include <linux/smp.h>
22 #include <linux/io.h>
23 #include <linux/of_address.h>
24
25 #include <asm/cacheflush.h>
26 #include <asm/smp_plat.h>
27 #include <asm/smp_scu.h>
28 #include <asm/firmware.h>
29
30 #include <plat/cpu.h>
31
32 #include "common.h"
33 #include "regs-pmu.h"
34
35 extern void exynos4_secondary_startup(void);
36
37 static void __iomem *sysram_base_addr;
38 void __iomem *sysram_ns_base_addr;
39
40 static void __init exynos_smp_prepare_sysram(void)
41 {
42         struct device_node *node;
43
44         for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram") {
45                 if (!of_device_is_available(node))
46                         continue;
47                 sysram_base_addr = of_iomap(node, 0);
48                 break;
49         }
50
51         for_each_compatible_node(node, NULL, "samsung,exynos4210-sysram-ns") {
52                 if (!of_device_is_available(node))
53                         continue;
54                 sysram_ns_base_addr = of_iomap(node, 0);
55                 break;
56         }
57 }
58
59 static inline void __iomem *cpu_boot_reg_base(void)
60 {
61         if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
62                 return S5P_INFORM5;
63         return sysram_base_addr;
64 }
65
66 static inline void __iomem *cpu_boot_reg(int cpu)
67 {
68         void __iomem *boot_reg;
69
70         boot_reg = cpu_boot_reg_base();
71         if (!boot_reg)
72                 return ERR_PTR(-ENODEV);
73         if (soc_is_exynos4412())
74                 boot_reg += 4*cpu;
75         else if (soc_is_exynos5420())
76                 boot_reg += 4;
77         return boot_reg;
78 }
79
80 /*
81  * Write pen_release in a way that is guaranteed to be visible to all
82  * observers, irrespective of whether they're taking part in coherency
83  * or not.  This is necessary for the hotplug code to work reliably.
84  */
85 static void write_pen_release(int val)
86 {
87         pen_release = val;
88         smp_wmb();
89         sync_cache_w(&pen_release);
90 }
91
92 static void __iomem *scu_base_addr(void)
93 {
94         return (void __iomem *)(S5P_VA_SCU);
95 }
96
97 static DEFINE_SPINLOCK(boot_lock);
98
99 static void exynos_secondary_init(unsigned int cpu)
100 {
101         /*
102          * let the primary processor know we're out of the
103          * pen, then head off into the C entry point
104          */
105         write_pen_release(-1);
106
107         /*
108          * Synchronise with the boot thread.
109          */
110         spin_lock(&boot_lock);
111         spin_unlock(&boot_lock);
112 }
113
114 static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
115 {
116         unsigned long timeout;
117         unsigned long phys_cpu = cpu_logical_map(cpu);
118         int ret = -ENOSYS;
119
120         /*
121          * Set synchronisation state between this boot processor
122          * and the secondary one
123          */
124         spin_lock(&boot_lock);
125
126         /*
127          * The secondary processor is waiting to be released from
128          * the holding pen - release it, then wait for it to flag
129          * that it has been released by resetting pen_release.
130          *
131          * Note that "pen_release" is the hardware CPU ID, whereas
132          * "cpu" is Linux's internal ID.
133          */
134         write_pen_release(phys_cpu);
135
136         if (!(__raw_readl(S5P_ARM_CORE1_STATUS) & S5P_CORE_LOCAL_PWR_EN)) {
137                 __raw_writel(S5P_CORE_LOCAL_PWR_EN,
138                              S5P_ARM_CORE1_CONFIGURATION);
139
140                 timeout = 10;
141
142                 /* wait max 10 ms until cpu1 is on */
143                 while ((__raw_readl(S5P_ARM_CORE1_STATUS)
144                         & S5P_CORE_LOCAL_PWR_EN) != S5P_CORE_LOCAL_PWR_EN) {
145                         if (timeout-- == 0)
146                                 break;
147
148                         mdelay(1);
149                 }
150
151                 if (timeout == 0) {
152                         printk(KERN_ERR "cpu1 power enable failed");
153                         spin_unlock(&boot_lock);
154                         return -ETIMEDOUT;
155                 }
156         }
157         /*
158          * Send the secondary CPU a soft interrupt, thereby causing
159          * the boot monitor to read the system wide flags register,
160          * and branch to the address found there.
161          */
162
163         timeout = jiffies + (1 * HZ);
164         while (time_before(jiffies, timeout)) {
165                 unsigned long boot_addr;
166
167                 smp_rmb();
168
169                 boot_addr = virt_to_phys(exynos4_secondary_startup);
170
171                 /*
172                  * Try to set boot address using firmware first
173                  * and fall back to boot register if it fails.
174                  */
175                 ret = call_firmware_op(set_cpu_boot_addr, phys_cpu, boot_addr);
176                 if (ret && ret != -ENOSYS)
177                         goto fail;
178                 if (ret == -ENOSYS) {
179                         void __iomem *boot_reg = cpu_boot_reg(phys_cpu);
180
181                         if (IS_ERR(boot_reg)) {
182                                 ret = PTR_ERR(boot_reg);
183                                 goto fail;
184                         }
185                         __raw_writel(boot_addr, cpu_boot_reg(phys_cpu));
186                 }
187
188                 call_firmware_op(cpu_boot, phys_cpu);
189
190                 arch_send_wakeup_ipi_mask(cpumask_of(cpu));
191
192                 if (pen_release == -1)
193                         break;
194
195                 udelay(10);
196         }
197
198         /*
199          * now the secondary core is starting up let it run its
200          * calibrations, then wait for it to finish
201          */
202 fail:
203         spin_unlock(&boot_lock);
204
205         return pen_release != -1 ? ret : 0;
206 }
207
208 /*
209  * Initialise the CPU possible map early - this describes the CPUs
210  * which may be present or become present in the system.
211  */
212
213 static void __init exynos_smp_init_cpus(void)
214 {
215         void __iomem *scu_base = scu_base_addr();
216         unsigned int i, ncores;
217
218         if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9)
219                 ncores = scu_base ? scu_get_core_count(scu_base) : 1;
220         else
221                 /*
222                  * CPU Nodes are passed thru DT and set_cpu_possible
223                  * is set by "arm_dt_init_cpu_maps".
224                  */
225                 return;
226
227         /* sanity check */
228         if (ncores > nr_cpu_ids) {
229                 pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
230                         ncores, nr_cpu_ids);
231                 ncores = nr_cpu_ids;
232         }
233
234         for (i = 0; i < ncores; i++)
235                 set_cpu_possible(i, true);
236 }
237
238 static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
239 {
240         int i;
241
242         if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9)
243                 scu_enable(scu_base_addr());
244
245         exynos_smp_prepare_sysram();
246
247         /*
248          * Write the address of secondary startup into the
249          * system-wide flags register. The boot monitor waits
250          * until it receives a soft interrupt, and then the
251          * secondary CPU branches to this address.
252          *
253          * Try using firmware operation first and fall back to
254          * boot register if it fails.
255          */
256         for (i = 1; i < max_cpus; ++i) {
257                 unsigned long phys_cpu;
258                 unsigned long boot_addr;
259                 int ret;
260
261                 phys_cpu = cpu_logical_map(i);
262                 boot_addr = virt_to_phys(exynos4_secondary_startup);
263
264                 ret = call_firmware_op(set_cpu_boot_addr, phys_cpu, boot_addr);
265                 if (ret && ret != -ENOSYS)
266                         break;
267                 if (ret == -ENOSYS) {
268                         void __iomem *boot_reg = cpu_boot_reg(phys_cpu);
269
270                         if (IS_ERR(boot_reg))
271                                 break;
272                         __raw_writel(boot_addr, cpu_boot_reg(phys_cpu));
273                 }
274         }
275 }
276
277 struct smp_operations exynos_smp_ops __initdata = {
278         .smp_init_cpus          = exynos_smp_init_cpus,
279         .smp_prepare_cpus       = exynos_smp_prepare_cpus,
280         .smp_secondary_init     = exynos_secondary_init,
281         .smp_boot_secondary     = exynos_boot_secondary,
282 #ifdef CONFIG_HOTPLUG_CPU
283         .cpu_die                = exynos_cpu_die,
284 #endif
285 };