blob: af2f4961748c9475bf386c9aae9f0b0157c59a46 [file] [log] [blame]
Syed Rameez Mustafab5b095b2012-10-10 19:59:09 -07001/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/init.h>
14#include <linux/errno.h>
15#include <linux/delay.h>
16#include <linux/device.h>
17#include <linux/jiffies.h>
18#include <linux/smp.h>
19#include <linux/io.h>
20#include <linux/interrupt.h>
21#include <linux/irq.h>
22
23#include <asm/cacheflush.h>
24#include <asm/smp_plat.h>
25#include <asm/hardware/gic.h>
26#include <mach/msm_iomap.h>
27#include "pm.h"
28
29#define BOOT_REMAP_ENABLE 0x01
30
31/*
32 * control for which core is the next to come out of the secondary
33 * boot "holding pen"
34 */
David Ng792de7a2012-11-01 15:29:22 -070035volatile int pen_release = -1;
Syed Rameez Mustafab5b095b2012-10-10 19:59:09 -070036
37/*
38 * Write pen_release in a way that is guaranteed to be visible to all
39 * observers, irrespective of whether they're taking part in coherency
40 * or not. This is necessary for the hotplug code to work reliably.
41 */
42static void __cpuinit write_pen_release(int val)
43{
44 pen_release = val;
45 smp_wmb();
46 __cpuc_flush_dcache_area((void *)&pen_release, sizeof(pen_release));
47 outer_clean_range(__pa(&pen_release), __pa(&pen_release + 1));
48}
49
50static DEFINE_SPINLOCK(boot_lock);
51
52void __cpuinit platform_secondary_init(unsigned int cpu)
53{
54 WARN_ON(msm_platform_secondary_init(cpu));
55
56 /*
57 * if any interrupts are already enabled for the primary
58 * core (e.g. timer irq), then they will not have been enabled
59 * for us: do so
60 */
61 gic_secondary_init(0);
62
63 /*
64 * let the primary processor know we're out of the
65 * pen, then head off into the C entry point
66 */
67 write_pen_release(-1);
68
69 /*
70 * Synchronise with the boot thread.
71 */
72 spin_lock(&boot_lock);
73 spin_unlock(&boot_lock);
74}
75
76static int __cpuinit release_secondary_sim(unsigned long base, int cpu)
77{
78 void *base_ptr;
79
80 base_ptr = ioremap_nocache(base + (cpu * 0x10000), SZ_4K);
81 if (!base_ptr) {
82 pr_err("Failed to release core %u\n", cpu);
83 return -ENODEV;
84 }
85
86 writel_relaxed(0x800, base_ptr+0x04);
87 writel_relaxed(0x3FFF, base_ptr+0x14);
88 mb();
89
90 return 0;
91}
92
93DEFINE_PER_CPU(int, cold_boot_done);
94
95int __cpuinit boot_secondary(unsigned int cpu, struct task_struct *idle)
96{
97 unsigned long timeout;
98
99 preset_lpj = loops_per_jiffy;
100
101 if (per_cpu(cold_boot_done, cpu) == false) {
102 release_secondary_sim(0xF9088000, cpu);
103 per_cpu(cold_boot_done, cpu) = true;
104 }
105
106 /*
107 * Set synchronisation state between this boot processor
108 * and the secondary one
109 */
110 spin_lock(&boot_lock);
111
112 /*
113 * The secondary processor is waiting to be released from
114 * the holding pen - release it, then wait for it to flag
115 * that it has been released by resetting pen_release.
116 *
117 * Note that "pen_release" is the hardware CPU ID, whereas
118 * "cpu" is Linux's internal ID.
119 */
120 write_pen_release(cpu_logical_map(cpu));
121
122 /*
123 * Send the secondary CPU a soft interrupt, thereby causing
124 * the boot monitor to read the system wide flags register,
125 * and branch to the address found there.
126 */
127
128 gic_raise_softirq(cpumask_of(cpu), 1);
129
130 timeout = jiffies + (1 * HZ);
131 while (time_before(jiffies, timeout)) {
132 smp_rmb();
133 if (pen_release == -1)
134 break;
135
136 udelay(10);
137 }
138
139 /*
140 * now the secondary core is starting up let it run its
141 * calibrations, then wait for it to finish
142 */
143 spin_unlock(&boot_lock);
144
145 return 0;
146}
147
148/*
149 * Initialise the CPU possible map early - this describes the CPUs
150 * which may be present or become present in the system
151 */
152void __init smp_init_cpus(void)
153{
154 unsigned int i, ncores;
155
156 ncores = (__raw_readl(MSM_APCS_GCC_BASE + 0x30)) & 0xF;
157
158 for (i = 0; i < ncores; i++)
159 set_cpu_possible(i, true);
160
161 set_smp_cross_call(gic_raise_softirq);
162}
163
164void __init platform_smp_prepare_cpus(unsigned int max_cpus)
165{
166 int i;
167 void __iomem *remap_ptr;
168
169 /*
170 * Initialise the present map, which describes the set of CPUs
171 * actually populated at the present time
172 */
173 for (i = 0; i < max_cpus; i++)
174 set_cpu_present(i, true);
175
176 /*
177 * Enable boot remapping and write the address of secondary
178 * startup into boot remapper register
179 */
180 remap_ptr = ioremap_nocache(0xF9010000, SZ_4K); /* APCS_CFG_SECURE */
181 if (!remap_ptr) {
182 pr_err("Failed to ioremap for secondary cores\n");
183 return;
184 }
185
186 __raw_writel((virt_to_phys(msm_secondary_startup)|BOOT_REMAP_ENABLE),
187 (remap_ptr + 0x4));
188 mb();
189 iounmap(remap_ptr);
190}