]> git.kernelconcepts.de Git - karo-tx-linux.git/blob - arch/arm/mach-exynos/platsmp.c
Merge branch 'for-linus' of git://git.armlinux.org.uk/~rmk/linux-arm
[karo-tx-linux.git] / arch / arm / mach-exynos / platsmp.c
1  /*
2  * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
3  *              http://www.samsung.com
4  *
5  * Cloned from linux/arch/arm/mach-vexpress/platsmp.c
6  *
7  *  Copyright (C) 2002 ARM Ltd.
8  *  All Rights Reserved
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License version 2 as
12  * published by the Free Software Foundation.
13 */
14
15 #include <linux/init.h>
16 #include <linux/errno.h>
17 #include <linux/delay.h>
18 #include <linux/jiffies.h>
19 #include <linux/smp.h>
20 #include <linux/io.h>
21 #include <linux/of_address.h>
22 #include <linux/soc/samsung/exynos-regs-pmu.h>
23
24 #include <asm/cacheflush.h>
25 #include <asm/cp15.h>
26 #include <asm/smp_plat.h>
27 #include <asm/smp_scu.h>
28 #include <asm/firmware.h>
29
30 #include <mach/map.h>
31
32 #include "common.h"
33
34 extern void exynos4_secondary_startup(void);
35
36 #ifdef CONFIG_HOTPLUG_CPU
37 static inline void cpu_leave_lowpower(u32 core_id)
38 {
39         unsigned int v;
40
41         asm volatile(
42         "mrc    p15, 0, %0, c1, c0, 0\n"
43         "       orr     %0, %0, %1\n"
44         "       mcr     p15, 0, %0, c1, c0, 0\n"
45         "       mrc     p15, 0, %0, c1, c0, 1\n"
46         "       orr     %0, %0, %2\n"
47         "       mcr     p15, 0, %0, c1, c0, 1\n"
48           : "=&r" (v)
49           : "Ir" (CR_C), "Ir" (0x40)
50           : "cc");
51 }
52
53 static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
54 {
55         u32 mpidr = cpu_logical_map(cpu);
56         u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
57
58         for (;;) {
59
60                 /* Turn the CPU off on next WFI instruction. */
61                 exynos_cpu_power_down(core_id);
62
63                 wfi();
64
65                 if (pen_release == core_id) {
66                         /*
67                          * OK, proper wakeup, we're done
68                          */
69                         break;
70                 }
71
72                 /*
73                  * Getting here, means that we have come out of WFI without
74                  * having been woken up - this shouldn't happen
75                  *
76                  * Just note it happening - when we're woken, we can report
77                  * its occurrence.
78                  */
79                 (*spurious)++;
80         }
81 }
82 #endif /* CONFIG_HOTPLUG_CPU */
83
84 /**
85  * exynos_core_power_down : power down the specified cpu
86  * @cpu : the cpu to power down
87  *
88  * Power down the specified cpu. The sequence must be finished by a
89  * call to cpu_do_idle()
90  *
91  */
92 void exynos_cpu_power_down(int cpu)
93 {
94         u32 core_conf;
95
96         if (cpu == 0 && (soc_is_exynos5420() || soc_is_exynos5800())) {
97                 /*
98                  * Bypass power down for CPU0 during suspend. Check for
99                  * the SYS_PWR_REG value to decide if we are suspending
100                  * the system.
101                  */
102                 int val = pmu_raw_readl(EXYNOS5_ARM_CORE0_SYS_PWR_REG);
103
104                 if (!(val & S5P_CORE_LOCAL_PWR_EN))
105                         return;
106         }
107
108         core_conf = pmu_raw_readl(EXYNOS_ARM_CORE_CONFIGURATION(cpu));
109         core_conf &= ~S5P_CORE_LOCAL_PWR_EN;
110         pmu_raw_writel(core_conf, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
111 }
112
113 /**
114  * exynos_cpu_power_up : power up the specified cpu
115  * @cpu : the cpu to power up
116  *
117  * Power up the specified cpu
118  */
119 void exynos_cpu_power_up(int cpu)
120 {
121         u32 core_conf = S5P_CORE_LOCAL_PWR_EN;
122
123         if (soc_is_exynos3250())
124                 core_conf |= S5P_CORE_AUTOWAKEUP_EN;
125
126         pmu_raw_writel(core_conf,
127                         EXYNOS_ARM_CORE_CONFIGURATION(cpu));
128 }
129
130 /**
131  * exynos_cpu_power_state : returns the power state of the cpu
132  * @cpu : the cpu to retrieve the power state from
133  *
134  */
135 int exynos_cpu_power_state(int cpu)
136 {
137         return (pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(cpu)) &
138                         S5P_CORE_LOCAL_PWR_EN);
139 }
140
141 /**
142  * exynos_cluster_power_down : power down the specified cluster
143  * @cluster : the cluster to power down
144  */
145 void exynos_cluster_power_down(int cluster)
146 {
147         pmu_raw_writel(0, EXYNOS_COMMON_CONFIGURATION(cluster));
148 }
149
150 /**
151  * exynos_cluster_power_up : power up the specified cluster
152  * @cluster : the cluster to power up
153  */
154 void exynos_cluster_power_up(int cluster)
155 {
156         pmu_raw_writel(S5P_CORE_LOCAL_PWR_EN,
157                         EXYNOS_COMMON_CONFIGURATION(cluster));
158 }
159
160 /**
161  * exynos_cluster_power_state : returns the power state of the cluster
162  * @cluster : the cluster to retrieve the power state from
163  *
164  */
165 int exynos_cluster_power_state(int cluster)
166 {
167         return (pmu_raw_readl(EXYNOS_COMMON_STATUS(cluster)) &
168                 S5P_CORE_LOCAL_PWR_EN);
169 }
170
171 static void __iomem *cpu_boot_reg_base(void)
172 {
173         if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
174                 return pmu_base_addr + S5P_INFORM5;
175         return sysram_base_addr;
176 }
177
178 static inline void __iomem *cpu_boot_reg(int cpu)
179 {
180         void __iomem *boot_reg;
181
182         boot_reg = cpu_boot_reg_base();
183         if (!boot_reg)
184                 return IOMEM_ERR_PTR(-ENODEV);
185         if (soc_is_exynos4412())
186                 boot_reg += 4*cpu;
187         else if (soc_is_exynos5420() || soc_is_exynos5800())
188                 boot_reg += 4;
189         return boot_reg;
190 }
191
192 /*
193  * Set wake up by local power mode and execute software reset for given core.
194  *
195  * Currently this is needed only when booting secondary CPU on Exynos3250.
196  */
197 void exynos_core_restart(u32 core_id)
198 {
199         u32 val;
200
201         if (!of_machine_is_compatible("samsung,exynos3250"))
202                 return;
203
204         while (!pmu_raw_readl(S5P_PMU_SPARE2))
205                 udelay(10);
206         udelay(10);
207
208         val = pmu_raw_readl(EXYNOS_ARM_CORE_STATUS(core_id));
209         val |= S5P_CORE_WAKEUP_FROM_LOCAL_CFG;
210         pmu_raw_writel(val, EXYNOS_ARM_CORE_STATUS(core_id));
211
212         pmu_raw_writel(EXYNOS_CORE_PO_RESET(core_id), EXYNOS_SWRESET);
213 }
214
215 /*
216  * Write pen_release in a way that is guaranteed to be visible to all
217  * observers, irrespective of whether they're taking part in coherency
218  * or not.  This is necessary for the hotplug code to work reliably.
219  */
220 static void write_pen_release(int val)
221 {
222         pen_release = val;
223         smp_wmb();
224         sync_cache_w(&pen_release);
225 }
226
227 static void __iomem *scu_base_addr(void)
228 {
229         return (void __iomem *)(S5P_VA_SCU);
230 }
231
232 static DEFINE_SPINLOCK(boot_lock);
233
234 static void exynos_secondary_init(unsigned int cpu)
235 {
236         /*
237          * let the primary processor know we're out of the
238          * pen, then head off into the C entry point
239          */
240         write_pen_release(-1);
241
242         /*
243          * Synchronise with the boot thread.
244          */
245         spin_lock(&boot_lock);
246         spin_unlock(&boot_lock);
247 }
248
249 int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr)
250 {
251         int ret;
252
253         /*
254          * Try to set boot address using firmware first
255          * and fall back to boot register if it fails.
256          */
257         ret = call_firmware_op(set_cpu_boot_addr, core_id, boot_addr);
258         if (ret && ret != -ENOSYS)
259                 goto fail;
260         if (ret == -ENOSYS) {
261                 void __iomem *boot_reg = cpu_boot_reg(core_id);
262
263                 if (IS_ERR(boot_reg)) {
264                         ret = PTR_ERR(boot_reg);
265                         goto fail;
266                 }
267                 writel_relaxed(boot_addr, boot_reg);
268                 ret = 0;
269         }
270 fail:
271         return ret;
272 }
273
274 int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr)
275 {
276         int ret;
277
278         /*
279          * Try to get boot address using firmware first
280          * and fall back to boot register if it fails.
281          */
282         ret = call_firmware_op(get_cpu_boot_addr, core_id, boot_addr);
283         if (ret && ret != -ENOSYS)
284                 goto fail;
285         if (ret == -ENOSYS) {
286                 void __iomem *boot_reg = cpu_boot_reg(core_id);
287
288                 if (IS_ERR(boot_reg)) {
289                         ret = PTR_ERR(boot_reg);
290                         goto fail;
291                 }
292                 *boot_addr = readl_relaxed(boot_reg);
293                 ret = 0;
294         }
295 fail:
296         return ret;
297 }
298
299 static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
300 {
301         unsigned long timeout;
302         u32 mpidr = cpu_logical_map(cpu);
303         u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
304         int ret = -ENOSYS;
305
306         /*
307          * Set synchronisation state between this boot processor
308          * and the secondary one
309          */
310         spin_lock(&boot_lock);
311
312         /*
313          * The secondary processor is waiting to be released from
314          * the holding pen - release it, then wait for it to flag
315          * that it has been released by resetting pen_release.
316          *
317          * Note that "pen_release" is the hardware CPU core ID, whereas
318          * "cpu" is Linux's internal ID.
319          */
320         write_pen_release(core_id);
321
322         if (!exynos_cpu_power_state(core_id)) {
323                 exynos_cpu_power_up(core_id);
324                 timeout = 10;
325
326                 /* wait max 10 ms until cpu1 is on */
327                 while (exynos_cpu_power_state(core_id)
328                        != S5P_CORE_LOCAL_PWR_EN) {
329                         if (timeout-- == 0)
330                                 break;
331
332                         mdelay(1);
333                 }
334
335                 if (timeout == 0) {
336                         printk(KERN_ERR "cpu1 power enable failed");
337                         spin_unlock(&boot_lock);
338                         return -ETIMEDOUT;
339                 }
340         }
341
342         exynos_core_restart(core_id);
343
344         /*
345          * Send the secondary CPU a soft interrupt, thereby causing
346          * the boot monitor to read the system wide flags register,
347          * and branch to the address found there.
348          */
349
350         timeout = jiffies + (1 * HZ);
351         while (time_before(jiffies, timeout)) {
352                 unsigned long boot_addr;
353
354                 smp_rmb();
355
356                 boot_addr = __pa_symbol(exynos4_secondary_startup);
357
358                 ret = exynos_set_boot_addr(core_id, boot_addr);
359                 if (ret)
360                         goto fail;
361
362                 call_firmware_op(cpu_boot, core_id);
363
364                 if (soc_is_exynos3250())
365                         dsb_sev();
366                 else
367                         arch_send_wakeup_ipi_mask(cpumask_of(cpu));
368
369                 if (pen_release == -1)
370                         break;
371
372                 udelay(10);
373         }
374
375         if (pen_release != -1)
376                 ret = -ETIMEDOUT;
377
378         /*
379          * now the secondary core is starting up let it run its
380          * calibrations, then wait for it to finish
381          */
382 fail:
383         spin_unlock(&boot_lock);
384
385         return pen_release != -1 ? ret : 0;
386 }
387
388 static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
389 {
390         int i;
391
392         exynos_sysram_init();
393
394         exynos_set_delayed_reset_assertion(true);
395
396         if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9)
397                 scu_enable(scu_base_addr());
398
399         /*
400          * Write the address of secondary startup into the
401          * system-wide flags register. The boot monitor waits
402          * until it receives a soft interrupt, and then the
403          * secondary CPU branches to this address.
404          *
405          * Try using firmware operation first and fall back to
406          * boot register if it fails.
407          */
408         for (i = 1; i < max_cpus; ++i) {
409                 unsigned long boot_addr;
410                 u32 mpidr;
411                 u32 core_id;
412                 int ret;
413
414                 mpidr = cpu_logical_map(i);
415                 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
416                 boot_addr = __pa_symbol(exynos4_secondary_startup);
417
418                 ret = exynos_set_boot_addr(core_id, boot_addr);
419                 if (ret)
420                         break;
421         }
422 }
423
424 #ifdef CONFIG_HOTPLUG_CPU
425 /*
426  * platform-specific code to shutdown a CPU
427  *
428  * Called with IRQs disabled
429  */
430 static void exynos_cpu_die(unsigned int cpu)
431 {
432         int spurious = 0;
433         u32 mpidr = cpu_logical_map(cpu);
434         u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
435
436         v7_exit_coherency_flush(louis);
437
438         platform_do_lowpower(cpu, &spurious);
439
440         /*
441          * bring this CPU back into the world of cache
442          * coherency, and then restore interrupts
443          */
444         cpu_leave_lowpower(core_id);
445
446         if (spurious)
447                 pr_warn("CPU%u: %u spurious wakeup calls\n", cpu, spurious);
448 }
449 #endif /* CONFIG_HOTPLUG_CPU */
450
451 const struct smp_operations exynos_smp_ops __initconst = {
452         .smp_prepare_cpus       = exynos_smp_prepare_cpus,
453         .smp_secondary_init     = exynos_secondary_init,
454         .smp_boot_secondary     = exynos_boot_secondary,
455 #ifdef CONFIG_HOTPLUG_CPU
456         .cpu_die                = exynos_cpu_die,
457 #endif
458 };