blob: a083e0591a56abba5e20653c5323ec74effb74c9 [file] [log] [blame]
Kukjin Kim7d30e8b2011-02-14 16:33:10 +09001/* linux/arch/arm/mach-exynos4/platsmp.c
Changhwan Youn2b12b5c2010-07-26 21:08:52 +09002 *
Kukjin Kim7d30e8b2011-02-14 16:33:10 +09003 * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com
Changhwan Youn2b12b5c2010-07-26 21:08:52 +09005 *
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>
Rob Herring520f7bd2012-12-27 13:10:24 -060023#include <linux/irqchip/arm-gic.h>
Changhwan Youn2b12b5c2010-07-26 21:08:52 +090024
25#include <asm/cacheflush.h>
Will Deaconeb504392012-01-20 12:01:12 +010026#include <asm/smp_plat.h>
Changhwan Youn2b12b5c2010-07-26 21:08:52 +090027#include <asm/smp_scu.h>
Tomasz Figabeddf632012-12-11 13:58:43 +090028#include <asm/firmware.h>
Changhwan Youn2b12b5c2010-07-26 21:08:52 +090029
30#include <mach/hardware.h>
31#include <mach/regs-clock.h>
JungHi Min911c29b2011-07-16 13:39:09 +090032#include <mach/regs-pmu.h>
Changhwan Youn2b12b5c2010-07-26 21:08:52 +090033
Kukjin Kim56b20922011-08-20 13:41:21 +090034#include <plat/cpu.h>
35
Marc Zyngier06853ae2011-09-08 13:15:22 +010036#include "common.h"
37
Kukjin Kim7d30e8b2011-02-14 16:33:10 +090038extern void exynos4_secondary_startup(void);
Changhwan Youn2b12b5c2010-07-26 21:08:52 +090039
Tomasz Figa1f054f52012-11-24 11:13:48 +090040static inline void __iomem *cpu_boot_reg_base(void)
41{
42 if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
43 return S5P_INFORM5;
44 return S5P_VA_SYSRAM;
45}
46
47static inline void __iomem *cpu_boot_reg(int cpu)
48{
49 void __iomem *boot_reg;
50
51 boot_reg = cpu_boot_reg_base();
52 if (soc_is_exynos4412())
53 boot_reg += 4*cpu;
54 return boot_reg;
55}
JungHi Min911c29b2011-07-16 13:39:09 +090056
Changhwan Youn2b12b5c2010-07-26 21:08:52 +090057/*
Russell King3705ff62010-12-18 10:53:12 +000058 * Write pen_release in a way that is guaranteed to be visible to all
59 * observers, irrespective of whether they're taking part in coherency
60 * or not. This is necessary for the hotplug code to work reliably.
61 */
62static void write_pen_release(int val)
63{
64 pen_release = val;
65 smp_wmb();
66 __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
67 outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
68}
69
Changhwan Youn2b12b5c2010-07-26 21:08:52 +090070static void __iomem *scu_base_addr(void)
71{
72 return (void __iomem *)(S5P_VA_SCU);
73}
74
75static DEFINE_SPINLOCK(boot_lock);
76
Marc Zyngier06853ae2011-09-08 13:15:22 +010077static void __cpuinit exynos_secondary_init(unsigned int cpu)
Changhwan Youn2b12b5c2010-07-26 21:08:52 +090078{
Changhwan Youn2b12b5c2010-07-26 21:08:52 +090079 /*
80 * if any interrupts are already enabled for the primary
81 * core (e.g. timer irq), then they will not have been enabled
82 * for us: do so
83 */
Marc Zyngierdb0d4db2011-11-12 16:09:49 +000084 gic_secondary_init(0);
Changhwan Youn2b12b5c2010-07-26 21:08:52 +090085
86 /*
87 * let the primary processor know we're out of the
88 * pen, then head off into the C entry point
89 */
Russell King3705ff62010-12-18 10:53:12 +000090 write_pen_release(-1);
Changhwan Youn2b12b5c2010-07-26 21:08:52 +090091
92 /*
93 * Synchronise with the boot thread.
94 */
95 spin_lock(&boot_lock);
96 spin_unlock(&boot_lock);
97}
98
Marc Zyngier06853ae2011-09-08 13:15:22 +010099static int __cpuinit exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
Changhwan Youn2b12b5c2010-07-26 21:08:52 +0900100{
101 unsigned long timeout;
Tomasz Figa1f054f52012-11-24 11:13:48 +0900102 unsigned long phys_cpu = cpu_logical_map(cpu);
Changhwan Youn2b12b5c2010-07-26 21:08:52 +0900103
104 /*
105 * Set synchronisation state between this boot processor
106 * and the secondary one
107 */
108 spin_lock(&boot_lock);
109
110 /*
111 * The secondary processor is waiting to be released from
112 * the holding pen - release it, then wait for it to flag
113 * that it has been released by resetting pen_release.
114 *
115 * Note that "pen_release" is the hardware CPU ID, whereas
116 * "cpu" is Linux's internal ID.
117 */
Tomasz Figa1f054f52012-11-24 11:13:48 +0900118 write_pen_release(phys_cpu);
Changhwan Youn2b12b5c2010-07-26 21:08:52 +0900119
JungHi Min911c29b2011-07-16 13:39:09 +0900120 if (!(__raw_readl(S5P_ARM_CORE1_STATUS) & S5P_CORE_LOCAL_PWR_EN)) {
121 __raw_writel(S5P_CORE_LOCAL_PWR_EN,
122 S5P_ARM_CORE1_CONFIGURATION);
123
124 timeout = 10;
125
126 /* wait max 10 ms until cpu1 is on */
127 while ((__raw_readl(S5P_ARM_CORE1_STATUS)
128 & S5P_CORE_LOCAL_PWR_EN) != S5P_CORE_LOCAL_PWR_EN) {
129 if (timeout-- == 0)
130 break;
131
132 mdelay(1);
133 }
134
135 if (timeout == 0) {
136 printk(KERN_ERR "cpu1 power enable failed");
137 spin_unlock(&boot_lock);
138 return -ETIMEDOUT;
139 }
140 }
Changhwan Youn2b12b5c2010-07-26 21:08:52 +0900141 /*
142 * Send the secondary CPU a soft interrupt, thereby causing
143 * the boot monitor to read the system wide flags register,
144 * and branch to the address found there.
145 */
Changhwan Youn2b12b5c2010-07-26 21:08:52 +0900146
147 timeout = jiffies + (1 * HZ);
148 while (time_before(jiffies, timeout)) {
Tomasz Figabeddf632012-12-11 13:58:43 +0900149 unsigned long boot_addr;
150
Changhwan Youn2b12b5c2010-07-26 21:08:52 +0900151 smp_rmb();
JungHi Min911c29b2011-07-16 13:39:09 +0900152
Tomasz Figabeddf632012-12-11 13:58:43 +0900153 boot_addr = virt_to_phys(exynos4_secondary_startup);
154
155 /*
156 * Try to set boot address using firmware first
157 * and fall back to boot register if it fails.
158 */
159 if (call_firmware_op(set_cpu_boot_addr, phys_cpu, boot_addr))
160 __raw_writel(boot_addr, cpu_boot_reg(phys_cpu));
161
162 call_firmware_op(cpu_boot, phys_cpu);
163
Rob Herringb1cffeb2012-11-26 15:05:48 -0600164 arch_send_wakeup_ipi_mask(cpumask_of(cpu));
JungHi Min911c29b2011-07-16 13:39:09 +0900165
Changhwan Youn2b12b5c2010-07-26 21:08:52 +0900166 if (pen_release == -1)
167 break;
168
169 udelay(10);
170 }
171
172 /*
173 * now the secondary core is starting up let it run its
174 * calibrations, then wait for it to finish
175 */
176 spin_unlock(&boot_lock);
177
178 return pen_release != -1 ? -ENOSYS : 0;
179}
180
181/*
182 * Initialise the CPU possible map early - this describes the CPUs
183 * which may be present or become present in the system.
184 */
185
Marc Zyngier06853ae2011-09-08 13:15:22 +0100186static void __init exynos_smp_init_cpus(void)
Changhwan Youn2b12b5c2010-07-26 21:08:52 +0900187{
188 void __iomem *scu_base = scu_base_addr();
189 unsigned int i, ncores;
190
Kukjin Kime9bba612012-01-25 15:35:57 +0900191 if (soc_is_exynos5250())
192 ncores = 2;
193 else
194 ncores = scu_base ? scu_get_core_count(scu_base) : 1;
Changhwan Youn2b12b5c2010-07-26 21:08:52 +0900195
196 /* sanity check */
Russell Kinga06f9162011-10-20 22:04:18 +0100197 if (ncores > nr_cpu_ids) {
198 pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
199 ncores, nr_cpu_ids);
200 ncores = nr_cpu_ids;
Changhwan Youn2b12b5c2010-07-26 21:08:52 +0900201 }
202
203 for (i = 0; i < ncores; i++)
204 set_cpu_possible(i, true);
205}
206
Marc Zyngier06853ae2011-09-08 13:15:22 +0100207static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
Changhwan Youn2b12b5c2010-07-26 21:08:52 +0900208{
Tomasz Figa1f054f52012-11-24 11:13:48 +0900209 int i;
210
Kukjin Kim83e877a2012-12-06 15:32:14 +0900211 if (!(soc_is_exynos5250() || soc_is_exynos5440()))
Kukjin Kime9bba612012-01-25 15:35:57 +0900212 scu_enable(scu_base_addr());
Russell King05c74a62010-12-03 11:09:48 +0000213
Changhwan Youn2b12b5c2010-07-26 21:08:52 +0900214 /*
Russell King05c74a62010-12-03 11:09:48 +0000215 * Write the address of secondary startup into the
216 * system-wide flags register. The boot monitor waits
217 * until it receives a soft interrupt, and then the
218 * secondary CPU branches to this address.
Tomasz Figabeddf632012-12-11 13:58:43 +0900219 *
220 * Try using firmware operation first and fall back to
221 * boot register if it fails.
Changhwan Youn2b12b5c2010-07-26 21:08:52 +0900222 */
Tomasz Figabeddf632012-12-11 13:58:43 +0900223 for (i = 1; i < max_cpus; ++i) {
224 unsigned long phys_cpu;
225 unsigned long boot_addr;
226
227 phys_cpu = cpu_logical_map(i);
228 boot_addr = virt_to_phys(exynos4_secondary_startup);
229
230 if (call_firmware_op(set_cpu_boot_addr, phys_cpu, boot_addr))
231 __raw_writel(boot_addr, cpu_boot_reg(phys_cpu));
232 }
Changhwan Youn2b12b5c2010-07-26 21:08:52 +0900233}
Marc Zyngier06853ae2011-09-08 13:15:22 +0100234
235struct smp_operations exynos_smp_ops __initdata = {
236 .smp_init_cpus = exynos_smp_init_cpus,
237 .smp_prepare_cpus = exynos_smp_prepare_cpus,
238 .smp_secondary_init = exynos_secondary_init,
239 .smp_boot_secondary = exynos_boot_secondary,
240#ifdef CONFIG_HOTPLUG_CPU
241 .cpu_die = exynos_cpu_die,
242#endif
243};