blob: 4e7e696c47964f8adfeb054f270272c4587d98a1 [file] [log] [blame]
Eric Biedermanfcd5ace2004-10-14 19:29:29 +00001#include <cpu/x86/lapic.h>
2#include <delay.h>
3#include <string.h>
4#include <console/console.h>
5#include <arch/hlt.h>
6#include <device/device.h>
7#include <device/path.h>
8#include <smp/atomic.h>
9#include <smp/spinlock.h>
10#include <cpu/cpu.h>
11
12
13#if CONFIG_SMP == 1
arch import user (historical)ef03afa2005-07-06 17:15:30 +000014
Eric Biedermanfcd5ace2004-10-14 19:29:29 +000015/* This is a lot more paranoid now, since Linux can NOT handle
16 * being told there is a CPU when none exists. So any errors
17 * will return 0, meaning no CPU.
18 *
19 * We actually handling that case by noting which cpus startup
20 * and not telling anyone about the ones that dont.
21 */
22static int lapic_start_cpu(unsigned long apicid)
23{
24 int timeout;
25 unsigned long send_status, accept_status, start_eip;
26 int j, num_starts, maxlvt;
27 extern char _secondary_start[];
28
29 /*
30 * Starting actual IPI sequence...
31 */
32
33 printk_spew("Asserting INIT.\n");
34
35 /*
36 * Turn INIT on target chip
37 */
38 lapic_write_around(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(apicid));
39
40 /*
41 * Send IPI
42 */
43
44 lapic_write_around(LAPIC_ICR, LAPIC_INT_LEVELTRIG | LAPIC_INT_ASSERT
45 | LAPIC_DM_INIT);
46
47 printk_spew("Waiting for send to finish...\n");
48 timeout = 0;
49 do {
50 printk_spew("+");
51 udelay(100);
52 send_status = lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY;
53 } while (send_status && (timeout++ < 1000));
54 if (timeout >= 1000) {
55 printk_err("CPU %d: First apic write timed out. Disabling\n",
56 apicid);
57 // too bad.
58 printk_err("ESR is 0x%x\n", lapic_read(LAPIC_ESR));
59 if (lapic_read(LAPIC_ESR)) {
60 printk_err("Try to reset ESR\n");
61 lapic_write_around(LAPIC_ESR, 0);
62 printk_err("ESR is 0x%x\n", lapic_read(LAPIC_ESR));
63 }
64 return 0;
65 }
66 mdelay(10);
67
68 printk_spew("Deasserting INIT.\n");
69
70 /* Target chip */
71 lapic_write_around(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(apicid));
72
73 /* Send IPI */
74 lapic_write_around(LAPIC_ICR, LAPIC_INT_LEVELTRIG | LAPIC_DM_INIT);
75
76 printk_spew("Waiting for send to finish...\n");
77 timeout = 0;
78 do {
79 printk_spew("+");
80 udelay(100);
81 send_status = lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY;
82 } while (send_status && (timeout++ < 1000));
83 if (timeout >= 1000) {
84 printk_err("CPU %d: Second apic write timed out. Disabling\n",
85 apicid);
86 // too bad.
87 return 0;
88 }
89
90 start_eip = (unsigned long)_secondary_start;
91 printk_spew("start_eip=0x%08lx\n", start_eip);
92
93 num_starts = 2;
94
95 /*
96 * Run STARTUP IPI loop.
97 */
98 printk_spew("#startup loops: %d.\n", num_starts);
99
100 maxlvt = 4;
101
102 for (j = 1; j <= num_starts; j++) {
103 printk_spew("Sending STARTUP #%d to %u.\n", j, apicid);
104 lapic_read_around(LAPIC_SPIV);
105 lapic_write(LAPIC_ESR, 0);
106 lapic_read(LAPIC_ESR);
107 printk_spew("After apic_write.\n");
108
109 /*
110 * STARTUP IPI
111 */
112
113 /* Target chip */
114 lapic_write_around(LAPIC_ICR2, SET_LAPIC_DEST_FIELD(apicid));
115
116 /* Boot on the stack */
117 /* Kick the second */
118 lapic_write_around(LAPIC_ICR, LAPIC_DM_STARTUP
119 | (start_eip >> 12));
120
121 /*
122 * Give the other CPU some time to accept the IPI.
123 */
124 udelay(300);
125
126 printk_spew("Startup point 1.\n");
127
128 printk_spew("Waiting for send to finish...\n");
129 timeout = 0;
130 do {
131 printk_spew("+");
132 udelay(100);
133 send_status = lapic_read(LAPIC_ICR) & LAPIC_ICR_BUSY;
134 } while (send_status && (timeout++ < 1000));
135
136 /*
137 * Give the other CPU some time to accept the IPI.
138 */
139 udelay(200);
140 /*
141 * Due to the Pentium erratum 3AP.
142 */
143 if (maxlvt > 3) {
144 lapic_read_around(LAPIC_SPIV);
145 lapic_write(LAPIC_ESR, 0);
146 }
147 accept_status = (lapic_read(LAPIC_ESR) & 0xEF);
148 if (send_status || accept_status)
149 break;
150 }
151 printk_spew("After Startup.\n");
152 if (send_status)
153 printk_warning("APIC never delivered???\n");
154 if (accept_status)
155 printk_warning("APIC delivery error (%lx).\n", accept_status);
156 if (send_status || accept_status)
157 return 0;
158 return 1;
159}
160
161/* Number of cpus that are currently running in linuxbios */
162static atomic_t active_cpus = ATOMIC_INIT(1);
163
164/* start_cpu_lock covers last_cpu_index and secondary_stack.
165 * Only starting one cpu at a time let's me remove the logic
166 * for select the stack from assembly language.
167 *
168 * In addition communicating by variables to the cpu I
169 * am starting allows me to veryify it has started before
170 * start_cpu returns.
171 */
172
173static spinlock_t start_cpu_lock = SPIN_LOCK_UNLOCKED;
174static unsigned last_cpu_index = 0;
175volatile unsigned long secondary_stack;
176
177int start_cpu(device_t cpu)
178{
179 extern unsigned char _estack[];
180 struct cpu_info *info;
181 unsigned long stack_end;
182 unsigned long apicid;
183 unsigned long index;
184 unsigned long count;
185 int result;
186
187 spin_lock(&start_cpu_lock);
188
189 /* Get the cpu's apicid */
190 apicid = cpu->path.u.apic.apic_id;
191
192 /* Get an index for the new processor */
193 index = ++last_cpu_index;
194
195 /* Find end of the new processors stack */
196 stack_end = ((unsigned long)_estack) - (STACK_SIZE*index) - sizeof(struct cpu_info);
197
198 /* Record the index and which cpu structure we are using */
199 info = (struct cpu_info *)stack_end;
200 info->index = index;
201 info->cpu = cpu;
202
203 /* Advertise the new stack to start_cpu */
204 secondary_stack = stack_end;
205
206 /* Until the cpu starts up report the cpu is not enabled */
207 cpu->enabled = 0;
208 cpu->initialized = 0;
209
210 /* Start the cpu */
211 result = lapic_start_cpu(apicid);
212
213 if (result) {
214 result = 0;
215 /* Wait 1s or until the new the new cpu calls in */
216 for(count = 0; count < 100000 ; count++) {
217 if (secondary_stack == 0) {
218 result = 1;
219 break;
220 }
221 udelay(10);
222 }
223 }
224 secondary_stack = 0;
225 spin_unlock(&start_cpu_lock);
226 return result;
227}
228
229/* C entry point of secondary cpus */
230void secondary_cpu_init(void)
231{
232 atomic_inc(&active_cpus);
arch import user (historical)ef03afa2005-07-06 17:15:30 +0000233#if SERIAL_CPU_INIT == 1
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000234 #if CONFIG_MAX_CPUS>2
235 spin_lock(&start_cpu_lock);
236 #endif
Yinghai Lua3354022005-01-03 20:54:43 +0000237#endif
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000238 cpu_initialize();
arch import user (historical)ef03afa2005-07-06 17:15:30 +0000239#if SERIAL_CPU_INIT == 1
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000240 #if CONFIG_MAX_CPUS>2
241 spin_unlock(&start_cpu_lock);
242 #endif
Yinghai Lua3354022005-01-03 20:54:43 +0000243#endif
Steven J. Magnania7c70bc2005-09-12 18:38:10 +0000244
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000245 atomic_dec(&active_cpus);
246 stop_this_cpu();
247}
248
Eric Biederman7003ba42004-10-16 06:20:29 +0000249static void initialize_other_cpus(struct bus *cpu_bus)
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000250{
251 int old_active_count, active_count;
252 device_t cpu;
253 /* Loop through the cpus once getting them started */
Eric Biederman7003ba42004-10-16 06:20:29 +0000254 for(cpu = cpu_bus->children; cpu ; cpu = cpu->sibling) {
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000255 if (cpu->path.type != DEVICE_PATH_APIC) {
256 continue;
257 }
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000258
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000259 if (!cpu->enabled) {
260 continue;
261 }
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000262
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000263 if (cpu->initialized) {
264 continue;
265 }
Stefan Reinauer7ce8c542005-12-02 21:52:30 +0000266
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000267 if (!start_cpu(cpu)) {
268 /* Record the error in cpu? */
269 printk_err("CPU %u would not start!\n",
270 cpu->path.u.apic.apic_id);
271 }
arch import user (historical)ef03afa2005-07-06 17:15:30 +0000272#if SERIAL_CPU_INIT == 1
273 #if CONFIG_MAX_CPUS>2
Yinghai Lua3354022005-01-03 20:54:43 +0000274 udelay(10);
arch import user (historical)ef03afa2005-07-06 17:15:30 +0000275 #endif
Yinghai Lua3354022005-01-03 20:54:43 +0000276#endif
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000277 }
278
279 /* Now loop until the other cpus have finished initializing */
280 old_active_count = 1;
281 active_count = atomic_read(&active_cpus);
282 while(active_count > 1) {
283 if (active_count != old_active_count) {
Eric Biedermanf3ed1cf2004-10-16 08:38:58 +0000284 printk_info("Waiting for %d CPUS to stop\n", active_count - 1);
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000285 old_active_count = active_count;
286 }
287 udelay(10);
288 active_count = atomic_read(&active_cpus);
289 }
Eric Biederman7003ba42004-10-16 06:20:29 +0000290 for(cpu = cpu_bus->children; cpu; cpu = cpu->sibling) {
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000291 if (cpu->path.type != DEVICE_PATH_APIC) {
292 continue;
293 }
294 if (!cpu->initialized) {
295 printk_err("CPU %u did not initialize!\n",
296 cpu->path.u.apic.apic_id);
297#warning "FIXME do I need a mainboard_cpu_fixup function?"
298 }
299 }
300 printk_debug("All AP CPUs stopped\n");
301}
302
303#else /* CONFIG_SMP */
304#define initialize_other_cpus(root) do {} while(0)
305#endif /* CONFIG_SMP */
306
Eric Biederman7003ba42004-10-16 06:20:29 +0000307void initialize_cpus(struct bus *cpu_bus)
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000308{
309 struct device_path cpu_path;
310 struct cpu_info *info;
311
312 /* Find the info struct for this cpu */
313 info = cpu_info();
314
315#if NEED_LAPIC == 1
316 /* Ensure the local apic is enabled */
317 enable_lapic();
318
319 /* Get the device path of the boot cpu */
320 cpu_path.type = DEVICE_PATH_APIC;
321 cpu_path.u.apic.apic_id = lapicid();
322#else
323 /* Get the device path of the boot cpu */
Eric Biedermana9e632c2004-11-18 22:38:08 +0000324 cpu_path.type = DEVICE_PATH_CPU;
325 cpu_path.u.cpu.id = 0;
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000326#endif
327
328 /* Find the device structure for the boot cpu */
Eric Biederman7003ba42004-10-16 06:20:29 +0000329 info->cpu = alloc_find_dev(cpu_bus, &cpu_path);
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000330
331 /* Initialize the bootstrap processor */
332 cpu_initialize();
333
334 /* Now initialize the rest of the cpus */
Eric Biederman7003ba42004-10-16 06:20:29 +0000335 initialize_other_cpus(cpu_bus);
Eric Biedermanfcd5ace2004-10-14 19:29:29 +0000336}
337