xref: /openbmc/linux/arch/arm/mach-exynos/platsmp.c (revision 830145796a5c8f1ca3f87ea619063c1d99a57df5)
1*83014579SKukjin Kim /* linux/arch/arm/mach-exynos4/platsmp.c
2*83014579SKukjin Kim  *
3*83014579SKukjin Kim  * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
4*83014579SKukjin Kim  *		http://www.samsung.com
5*83014579SKukjin Kim  *
6*83014579SKukjin Kim  * Cloned from linux/arch/arm/mach-vexpress/platsmp.c
7*83014579SKukjin Kim  *
8*83014579SKukjin Kim  *  Copyright (C) 2002 ARM Ltd.
9*83014579SKukjin Kim  *  All Rights Reserved
10*83014579SKukjin Kim  *
11*83014579SKukjin Kim  * This program is free software; you can redistribute it and/or modify
12*83014579SKukjin Kim  * it under the terms of the GNU General Public License version 2 as
13*83014579SKukjin Kim  * published by the Free Software Foundation.
14*83014579SKukjin Kim */
15*83014579SKukjin Kim 
16*83014579SKukjin Kim #include <linux/init.h>
17*83014579SKukjin Kim #include <linux/errno.h>
18*83014579SKukjin Kim #include <linux/delay.h>
19*83014579SKukjin Kim #include <linux/device.h>
20*83014579SKukjin Kim #include <linux/jiffies.h>
21*83014579SKukjin Kim #include <linux/smp.h>
22*83014579SKukjin Kim #include <linux/io.h>
23*83014579SKukjin Kim 
24*83014579SKukjin Kim #include <asm/cacheflush.h>
25*83014579SKukjin Kim #include <asm/hardware/gic.h>
26*83014579SKukjin Kim #include <asm/smp_scu.h>
27*83014579SKukjin Kim #include <asm/unified.h>
28*83014579SKukjin Kim 
29*83014579SKukjin Kim #include <mach/hardware.h>
30*83014579SKukjin Kim #include <mach/regs-clock.h>
31*83014579SKukjin Kim #include <mach/regs-pmu.h>
32*83014579SKukjin Kim 
33*83014579SKukjin Kim #include <plat/cpu.h>
34*83014579SKukjin Kim 
35*83014579SKukjin Kim extern unsigned int gic_bank_offset;
36*83014579SKukjin Kim extern void exynos4_secondary_startup(void);
37*83014579SKukjin Kim 
38*83014579SKukjin Kim #define CPU1_BOOT_REG		(samsung_rev() == EXYNOS4210_REV_1_1 ? \
39*83014579SKukjin Kim 				S5P_INFORM5 : S5P_VA_SYSRAM)
40*83014579SKukjin Kim 
41*83014579SKukjin Kim /*
42*83014579SKukjin Kim  * control for which core is the next to come out of the secondary
43*83014579SKukjin Kim  * boot "holding pen"
44*83014579SKukjin Kim  */
45*83014579SKukjin Kim 
46*83014579SKukjin Kim volatile int __cpuinitdata pen_release = -1;
47*83014579SKukjin Kim 
48*83014579SKukjin Kim /*
49*83014579SKukjin Kim  * Write pen_release in a way that is guaranteed to be visible to all
50*83014579SKukjin Kim  * observers, irrespective of whether they're taking part in coherency
51*83014579SKukjin Kim  * or not.  This is necessary for the hotplug code to work reliably.
52*83014579SKukjin Kim  */
53*83014579SKukjin Kim static void write_pen_release(int val)
54*83014579SKukjin Kim {
55*83014579SKukjin Kim 	pen_release = val;
56*83014579SKukjin Kim 	smp_wmb();
57*83014579SKukjin Kim 	__cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
58*83014579SKukjin Kim 	outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
59*83014579SKukjin Kim }
60*83014579SKukjin Kim 
61*83014579SKukjin Kim static void __iomem *scu_base_addr(void)
62*83014579SKukjin Kim {
63*83014579SKukjin Kim 	return (void __iomem *)(S5P_VA_SCU);
64*83014579SKukjin Kim }
65*83014579SKukjin Kim 
66*83014579SKukjin Kim static DEFINE_SPINLOCK(boot_lock);
67*83014579SKukjin Kim 
68*83014579SKukjin Kim static void __cpuinit exynos4_gic_secondary_init(void)
69*83014579SKukjin Kim {
70*83014579SKukjin Kim 	void __iomem *dist_base = S5P_VA_GIC_DIST +
71*83014579SKukjin Kim 				(gic_bank_offset * smp_processor_id());
72*83014579SKukjin Kim 	void __iomem *cpu_base = S5P_VA_GIC_CPU +
73*83014579SKukjin Kim 				(gic_bank_offset * smp_processor_id());
74*83014579SKukjin Kim 	int i;
75*83014579SKukjin Kim 
76*83014579SKukjin Kim 	/*
77*83014579SKukjin Kim 	 * Deal with the banked PPI and SGI interrupts - disable all
78*83014579SKukjin Kim 	 * PPI interrupts, ensure all SGI interrupts are enabled.
79*83014579SKukjin Kim 	 */
80*83014579SKukjin Kim 	__raw_writel(0xffff0000, dist_base + GIC_DIST_ENABLE_CLEAR);
81*83014579SKukjin Kim 	__raw_writel(0x0000ffff, dist_base + GIC_DIST_ENABLE_SET);
82*83014579SKukjin Kim 
83*83014579SKukjin Kim 	/*
84*83014579SKukjin Kim 	 * Set priority on PPI and SGI interrupts
85*83014579SKukjin Kim 	 */
86*83014579SKukjin Kim 	for (i = 0; i < 32; i += 4)
87*83014579SKukjin Kim 		__raw_writel(0xa0a0a0a0, dist_base + GIC_DIST_PRI + i * 4 / 4);
88*83014579SKukjin Kim 
89*83014579SKukjin Kim 	__raw_writel(0xf0, cpu_base + GIC_CPU_PRIMASK);
90*83014579SKukjin Kim 	__raw_writel(1, cpu_base + GIC_CPU_CTRL);
91*83014579SKukjin Kim }
92*83014579SKukjin Kim 
93*83014579SKukjin Kim void __cpuinit platform_secondary_init(unsigned int cpu)
94*83014579SKukjin Kim {
95*83014579SKukjin Kim 	/*
96*83014579SKukjin Kim 	 * if any interrupts are already enabled for the primary
97*83014579SKukjin Kim 	 * core (e.g. timer irq), then they will not have been enabled
98*83014579SKukjin Kim 	 * for us: do so
99*83014579SKukjin Kim 	 */
100*83014579SKukjin Kim 	exynos4_gic_secondary_init();
101*83014579SKukjin Kim 
102*83014579SKukjin Kim 	/*
103*83014579SKukjin Kim 	 * let the primary processor know we're out of the
104*83014579SKukjin Kim 	 * pen, then head off into the C entry point
105*83014579SKukjin Kim 	 */
106*83014579SKukjin Kim 	write_pen_release(-1);
107*83014579SKukjin Kim 
108*83014579SKukjin Kim 	/*
109*83014579SKukjin Kim 	 * Synchronise with the boot thread.
110*83014579SKukjin Kim 	 */
111*83014579SKukjin Kim 	spin_lock(&boot_lock);
112*83014579SKukjin Kim 	spin_unlock(&boot_lock);
113*83014579SKukjin Kim 
114*83014579SKukjin Kim 	set_cpu_online(cpu, true);
115*83014579SKukjin Kim }
116*83014579SKukjin Kim 
117*83014579SKukjin Kim int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
118*83014579SKukjin Kim {
119*83014579SKukjin Kim 	unsigned long timeout;
120*83014579SKukjin Kim 
121*83014579SKukjin Kim 	/*
122*83014579SKukjin Kim 	 * Set synchronisation state between this boot processor
123*83014579SKukjin Kim 	 * and the secondary one
124*83014579SKukjin Kim 	 */
125*83014579SKukjin Kim 	spin_lock(&boot_lock);
126*83014579SKukjin Kim 
127*83014579SKukjin Kim 	/*
128*83014579SKukjin Kim 	 * The secondary processor is waiting to be released from
129*83014579SKukjin Kim 	 * the holding pen - release it, then wait for it to flag
130*83014579SKukjin Kim 	 * that it has been released by resetting pen_release.
131*83014579SKukjin Kim 	 *
132*83014579SKukjin Kim 	 * Note that "pen_release" is the hardware CPU ID, whereas
133*83014579SKukjin Kim 	 * "cpu" is Linux's internal ID.
134*83014579SKukjin Kim 	 */
135*83014579SKukjin Kim 	write_pen_release(cpu_logical_map(cpu));
136*83014579SKukjin Kim 
137*83014579SKukjin Kim 	if (!(__raw_readl(S5P_ARM_CORE1_STATUS) & S5P_CORE_LOCAL_PWR_EN)) {
138*83014579SKukjin Kim 		__raw_writel(S5P_CORE_LOCAL_PWR_EN,
139*83014579SKukjin Kim 			     S5P_ARM_CORE1_CONFIGURATION);
140*83014579SKukjin Kim 
141*83014579SKukjin Kim 		timeout = 10;
142*83014579SKukjin Kim 
143*83014579SKukjin Kim 		/* wait max 10 ms until cpu1 is on */
144*83014579SKukjin Kim 		while ((__raw_readl(S5P_ARM_CORE1_STATUS)
145*83014579SKukjin Kim 			& S5P_CORE_LOCAL_PWR_EN) != S5P_CORE_LOCAL_PWR_EN) {
146*83014579SKukjin Kim 			if (timeout-- == 0)
147*83014579SKukjin Kim 				break;
148*83014579SKukjin Kim 
149*83014579SKukjin Kim 			mdelay(1);
150*83014579SKukjin Kim 		}
151*83014579SKukjin Kim 
152*83014579SKukjin Kim 		if (timeout == 0) {
153*83014579SKukjin Kim 			printk(KERN_ERR "cpu1 power enable failed");
154*83014579SKukjin Kim 			spin_unlock(&boot_lock);
155*83014579SKukjin Kim 			return -ETIMEDOUT;
156*83014579SKukjin Kim 		}
157*83014579SKukjin Kim 	}
158*83014579SKukjin Kim 	/*
159*83014579SKukjin Kim 	 * Send the secondary CPU a soft interrupt, thereby causing
160*83014579SKukjin Kim 	 * the boot monitor to read the system wide flags register,
161*83014579SKukjin Kim 	 * and branch to the address found there.
162*83014579SKukjin Kim 	 */
163*83014579SKukjin Kim 
164*83014579SKukjin Kim 	timeout = jiffies + (1 * HZ);
165*83014579SKukjin Kim 	while (time_before(jiffies, timeout)) {
166*83014579SKukjin Kim 		smp_rmb();
167*83014579SKukjin Kim 
168*83014579SKukjin Kim 		__raw_writel(BSYM(virt_to_phys(exynos4_secondary_startup)),
169*83014579SKukjin Kim 			CPU1_BOOT_REG);
170*83014579SKukjin Kim 		gic_raise_softirq(cpumask_of(cpu), 1);
171*83014579SKukjin Kim 
172*83014579SKukjin Kim 		if (pen_release == -1)
173*83014579SKukjin Kim 			break;
174*83014579SKukjin Kim 
175*83014579SKukjin Kim 		udelay(10);
176*83014579SKukjin Kim 	}
177*83014579SKukjin Kim 
178*83014579SKukjin Kim 	/*
179*83014579SKukjin Kim 	 * now the secondary core is starting up let it run its
180*83014579SKukjin Kim 	 * calibrations, then wait for it to finish
181*83014579SKukjin Kim 	 */
182*83014579SKukjin Kim 	spin_unlock(&boot_lock);
183*83014579SKukjin Kim 
184*83014579SKukjin Kim 	return pen_release != -1 ? -ENOSYS : 0;
185*83014579SKukjin Kim }
186*83014579SKukjin Kim 
187*83014579SKukjin Kim /*
188*83014579SKukjin Kim  * Initialise the CPU possible map early - this describes the CPUs
189*83014579SKukjin Kim  * which may be present or become present in the system.
190*83014579SKukjin Kim  */
191*83014579SKukjin Kim 
192*83014579SKukjin Kim void __init smp_init_cpus(void)
193*83014579SKukjin Kim {
194*83014579SKukjin Kim 	void __iomem *scu_base = scu_base_addr();
195*83014579SKukjin Kim 	unsigned int i, ncores;
196*83014579SKukjin Kim 
197*83014579SKukjin Kim 	ncores = scu_base ? scu_get_core_count(scu_base) : 1;
198*83014579SKukjin Kim 
199*83014579SKukjin Kim 	/* sanity check */
200*83014579SKukjin Kim 	if (ncores > nr_cpu_ids) {
201*83014579SKukjin Kim 		pr_warn("SMP: %u cores greater than maximum (%u), clipping\n",
202*83014579SKukjin Kim 			ncores, nr_cpu_ids);
203*83014579SKukjin Kim 		ncores = nr_cpu_ids;
204*83014579SKukjin Kim 	}
205*83014579SKukjin Kim 
206*83014579SKukjin Kim 	for (i = 0; i < ncores; i++)
207*83014579SKukjin Kim 		set_cpu_possible(i, true);
208*83014579SKukjin Kim 
209*83014579SKukjin Kim 	set_smp_cross_call(gic_raise_softirq);
210*83014579SKukjin Kim }
211*83014579SKukjin Kim 
212*83014579SKukjin Kim void __init platform_smp_prepare_cpus(unsigned int max_cpus)
213*83014579SKukjin Kim {
214*83014579SKukjin Kim 
215*83014579SKukjin Kim 	scu_enable(scu_base_addr());
216*83014579SKukjin Kim 
217*83014579SKukjin Kim 	/*
218*83014579SKukjin Kim 	 * Write the address of secondary startup into the
219*83014579SKukjin Kim 	 * system-wide flags register. The boot monitor waits
220*83014579SKukjin Kim 	 * until it receives a soft interrupt, and then the
221*83014579SKukjin Kim 	 * secondary CPU branches to this address.
222*83014579SKukjin Kim 	 */
223*83014579SKukjin Kim 	__raw_writel(BSYM(virt_to_phys(exynos4_secondary_startup)),
224*83014579SKukjin Kim 			CPU1_BOOT_REG);
225*83014579SKukjin Kim }
226