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