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