- Moved hlt() to it's own header.
[coreboot.git] / src / arch / i386 / smp / start_stop.c
1 #include <smp/start_stop.h>
2 #include <arch/smp/mpspec.h>
3 #include <cpu/p6/apic.h>
4 #include <delay.h>
5 #include <string.h>
6 #include <console/console.h>
7 #include <arch/smp/lapic.h>
8 #include <arch/hlt.h>
9
10
11 unsigned long this_processors_id(void)
12 {
13         return lapicid();
14 }
15
16 int processor_index(unsigned long apicid)
17 {
18         int i;
19         for(i = 0; i < CONFIG_MAX_CPUS; i++) {
20                 if (initial_apicid[i] == apicid) {
21                         return i;
22                 }
23         }
24         return -1;
25 }
26
27 void stop_cpu(unsigned long apicid)
28 {
29         int timeout;
30         unsigned long send_status;
31
32         /* send an APIC INIT to myself */
33         apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));
34         apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_INT_ASSERT | APIC_DM_INIT);
35
36         /* wait for the ipi send to finish */
37         printk_spew("Waiting for send to finish...\n");
38         timeout = 0;
39         do {
40                 printk_spew("+");
41                 udelay(100);
42                 send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
43         } while (send_status && (timeout++ < 1000));
44         if (timeout >= 1000) {
45                 printk_err("timed out\n");
46         }
47         mdelay(10);
48
49         printk_spew("Deasserting INIT.\n");
50         /* Deassert the APIC INIT */
51         apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));      
52         apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT);
53
54         printk_spew("Waiting for send to finish...\n");
55         timeout = 0;
56         do {
57                 printk_spew("+");
58                 udelay(100);
59                 send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
60         } while (send_status && (timeout++ < 1000));
61         if (timeout >= 1000) {
62                 printk_err("timed out\n");
63         }
64
65         while(1) {
66                 hlt();
67         }
68 }
69
70 /* This is a lot more paranoid now, since Linux can NOT handle
71  * being told there is a CPU when none exists. So any errors 
72  * will return 0, meaning no CPU. 
73  *
74  * We actually handling that case by noting which cpus startup
75  * and not telling anyone about the ones that dont.
76  */ 
77 int start_cpu(unsigned long apicid)
78 {
79         int timeout;
80         unsigned long send_status, accept_status, start_eip;
81         int j, num_starts, maxlvt;
82         extern char _secondary_start[];
83         
84         /*
85          * Starting actual IPI sequence...
86          */
87
88         printk_spew("Asserting INIT.\n");
89
90         /*
91          * Turn INIT on target chip
92          */
93         apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));
94
95         /*
96          * Send IPI
97          */
98         
99         apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_INT_ASSERT
100                                 | APIC_DM_INIT);
101
102         printk_spew("Waiting for send to finish...\n");
103         timeout = 0;
104         do {
105                 printk_spew("+");
106                 udelay(100);
107                 send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
108         } while (send_status && (timeout++ < 1000));
109         if (timeout >= 1000) {
110                 printk_err("CPU %d: First apic write timed out. Disabling\n",
111                          apicid);
112                 // too bad. 
113                 printk_err("ESR is 0x%x\n", apic_read(APIC_ESR));
114                 if (apic_read(APIC_ESR)) {
115                         printk_err("Try to reset ESR\n");
116                         apic_write_around(APIC_ESR, 0);
117                         printk_err("ESR is 0x%x\n", apic_read(APIC_ESR));
118                 }
119                 return 0;
120         }
121         mdelay(10);
122
123         printk_spew("Deasserting INIT.\n");
124
125         /* Target chip */
126         apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));
127
128         /* Send IPI */
129         apic_write_around(APIC_ICR, APIC_INT_LEVELTRIG | APIC_DM_INIT);
130         
131         printk_spew("Waiting for send to finish...\n");
132         timeout = 0;
133         do {
134                 printk_spew("+");
135                 udelay(100);
136                 send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
137         } while (send_status && (timeout++ < 1000));
138         if (timeout >= 1000) {
139                 printk_err("CPU %d: Second apic write timed out. Disabling\n",
140                          apicid);
141                 // too bad. 
142                 return 0;
143         }
144
145         start_eip = (unsigned long)_secondary_start;
146         printk_spew("start_eip=0x%08lx\n", start_eip);
147        
148         num_starts = 2;
149
150         /*
151          * Run STARTUP IPI loop.
152          */
153         printk_spew("#startup loops: %d.\n", num_starts);
154
155         maxlvt = 4;
156
157         for (j = 1; j <= num_starts; j++) {
158                 printk_spew("Sending STARTUP #%d to %u.\n", j, apicid);
159                 apic_read_around(APIC_SPIV);
160                 apic_write(APIC_ESR, 0);
161                 apic_read(APIC_ESR);
162                 printk_spew("After apic_write.\n");
163
164                 /*
165                  * STARTUP IPI
166                  */
167
168                 /* Target chip */
169                 apic_write_around(APIC_ICR2, SET_APIC_DEST_FIELD(apicid));
170
171                 /* Boot on the stack */
172                 /* Kick the second */
173                 apic_write_around(APIC_ICR, APIC_DM_STARTUP
174                                         | (start_eip >> 12));
175
176                 /*
177                  * Give the other CPU some time to accept the IPI.
178                  */
179                 udelay(300);
180
181                 printk_spew("Startup point 1.\n");
182
183                 printk_spew("Waiting for send to finish...\n");
184                 timeout = 0;
185                 do {
186                         printk_spew("+");
187                         udelay(100);
188                         send_status = apic_read(APIC_ICR) & APIC_ICR_BUSY;
189                 } while (send_status && (timeout++ < 1000));
190
191                 /*
192                  * Give the other CPU some time to accept the IPI.
193                  */
194                 udelay(200);
195                 /*
196                  * Due to the Pentium erratum 3AP.
197                  */
198                 if (maxlvt > 3) {
199                         apic_read_around(APIC_SPIV);
200                         apic_write(APIC_ESR, 0);
201                 }
202                 accept_status = (apic_read(APIC_ESR) & 0xEF);
203                 if (send_status || accept_status)
204                         break;
205         }
206         printk_spew("After Startup.\n");
207         if (send_status)
208                 printk_warning("APIC never delivered???\n");
209         if (accept_status)
210                 printk_warning("APIC delivery error (%lx).\n", accept_status);
211         if (send_status || accept_status)
212                 return 0;
213         return 1;
214 }
215
216
217 void startup_other_cpus(unsigned long *processor_map)
218 {
219         unsigned long apicid = this_processors_id();
220         int i;
221
222         /* Assume the cpus are densly packed by apicid */
223         for(i = 0; i < CONFIG_MAX_CPUS; i++) {
224                 unsigned long cpu_apicid = initial_apicid[i];
225                 if (cpu_apicid == -1) {
226                         printk_err("CPU %d not found\n",i);
227                         processor_map[i] = 0;
228                         continue;
229                 }
230                 if (cpu_apicid == apicid ) {
231                         continue;
232                 }
233                 if (!start_cpu(cpu_apicid)) {
234                         /* Put an error in processor_map[i]? */
235                         printk_err("CPU %d/%u would not start!\n",
236                                 i, cpu_apicid);
237                         processor_map[i] = 0;
238                 }
239         }
240 }