Set up ACPI interrupt configuration
[akaros.git] / kern / arch / x86 / apic9.c
1 #include <vfs.h>
2 #include <kfs.h>
3 #include <slab.h>
4 #include <kmalloc.h>
5 #include <kref.h>
6 #include <string.h>
7 #include <stdio.h>
8 #include <assert.h>
9 #include <error.h>
10 #include <cpio.h>
11 #include <pmap.h>
12 #include <smp.h>
13 #include <ip.h>
14
15 enum {                                          /* Local APIC registers */
16         Id              = 0x0020,               /* Identification */
17         Ver             = 0x0030,               /* Version */
18         Tp              = 0x0080,               /* Task Priority */
19         Ap              = 0x0090,               /* Arbitration Priority */
20         Pp              = 0x00a0,               /* Processor Priority */
21         Eoi             = 0x00b0,               /* EOI */
22         Ld              = 0x00d0,               /* Logical Destination */
23         Df              = 0x00e0,               /* Destination Format */
24         Siv             = 0x00f0,               /* Spurious Interrupt Vector */
25         Is              = 0x0100,               /* Interrupt Status (8) */
26         Tm              = 0x0180,               /* Trigger Mode (8) */
27         Ir              = 0x0200,               /* Interrupt Request (8) */
28         Es              = 0x0280,               /* Error Status */
29         Iclo            = 0x0300,               /* Interrupt Command */
30         Ichi            = 0x0310,               /* Interrupt Command [63:32] */
31         Lvt0            = 0x0320,               /* Local Vector Table 0 */
32         Lvt5            = 0x0330,               /* Local Vector Table 5 */
33         Lvt4            = 0x0340,               /* Local Vector Table 4 */
34         Lvt1            = 0x0350,               /* Local Vector Table 1 */
35         Lvt2            = 0x0360,               /* Local Vector Table 2 */
36         Lvt3            = 0x0370,               /* Local Vector Table 3 */
37         Tic             = 0x0380,               /* Timer Initial Count */
38         Tcc             = 0x0390,               /* Timer Current Count */
39         Tdc             = 0x03e0,               /* Timer Divide Configuration */
40
41         Tlvt            = Lvt0,                 /* Timer */
42         Lint0           = Lvt1,                 /* Local Interrupt 0 */
43         Lint1           = Lvt2,                 /* Local Interrupt 1 */
44         Elvt            = Lvt3,                 /* Error */
45         Pclvt           = Lvt4,                 /* Performance Counter */
46         Tslvt           = Lvt5,                 /* Thermal Sensor */
47 };
48
49 enum {                                          /* Siv */
50         Swen            = 0x00000100,           /* Software Enable */
51         Fdis            = 0x00000200,           /* Focus Disable */
52 };
53
54 enum {                                          /* Iclo */
55         Lassert         = 0x00004000,           /* Assert level */
56
57         DSnone          = 0x00000000,           /* Use Destination Field */
58         DSself          = 0x00040000,           /* Self is only destination */
59         DSallinc        = 0x00080000,           /* All including self */
60         DSallexc        = 0x000c0000,           /* All Excluding self */
61 };
62
63 enum {                                          /* Tlvt */
64         Periodic        = 0x00020000,           /* Periodic Timer Mode */
65 };
66
67 enum {                                          /* Tdc */
68         DivX2           = 0x00000000,           /* Divide by 2 */
69         DivX4           = 0x00000001,           /* Divide by 4 */
70         DivX8           = 0x00000002,           /* Divide by 8 */
71         DivX16          = 0x00000003,           /* Divide by 16 */
72         DivX32          = 0x00000008,           /* Divide by 32 */
73         DivX64          = 0x00000009,           /* Divide by 64 */
74         DivX128         = 0x0000000a,           /* Divide by 128 */
75         DivX1           = 0x0000000b,           /* Divide by 1 */
76 };
77
78 static uint8_t* apicbase;
79 static int apmachno = 1;
80
81 struct apic     xlapic[Napic];
82
83 static uint32_t
84 apicrget(int r)
85 {
86         if (! apicbase)
87                 panic("apicrget: no apic");
88         return *((uint32_t*)(apicbase+r));
89 }
90
91 static void
92 apicrput(int r, uint32_t data)
93 {
94         if (! apicbase)
95                 panic("apicrput: no apic");
96         *((uint32_t*)(apicbase+r)) = data;
97 }
98
99 int
100 apiceoi(int vecno)
101 {
102         apicrput(Eoi, 0);
103
104         return vecno;
105 }
106
107 int
108 apicisr(int vecno)
109 {
110         int isr;
111
112         isr = apicrget(Is + (vecno/32)*16);
113
114         return isr & (1<<(vecno%32));
115 }
116
117 void
118 apicinit(int apicno, uintptr_t pa, int isbp)
119 {
120         struct apic *apic;
121
122         /*
123          * Mark the APIC useable if it has a good ID
124          * and the registers can be mapped.
125          * The APIC Extended Broadcast and ID bits in the HyperTransport
126          * Transaction Control register determine whether 4 or 8 bits
127          * are used for the APIC ID. There is also xAPIC and x2APIC
128          * to be dealt with sometime.
129          */
130         printk("apicinit: apicno %d pa %#p isbp %d\n", apicno, pa, isbp);
131         if(apicno >= Napic){
132                 printd("apicinit%d: out of range\n", apicno);
133                 return;
134         }
135         if((apic = &xlapic[apicno])->useable){
136                 printd("apicinit%d: already initialised\n", apicno);
137                 return;
138         }
139         if(apicbase == NULL){
140                 if((apicbase = KADDR(pa)) == NULL){
141                         printd("apicinit%d: can't map apicbase\n", apicno);
142                         return;
143                 }
144                 printk("apicinit%d: apicbase %#p -> %#p\n", apicno, pa, apicbase);
145         }
146         apic->useable = 1;
147
148         /*
149          * Assign a machno to the processor associated with this
150          * APIC, it may not be an identity map.
151          * Machno 0 is always the bootstrap processor.
152
153         if(isbp){
154                 apic->machno = 0;
155                 m->apicno = apicno;
156         }
157         else
158                 apic->machno = apmachno++;
159          */
160 }
161 #if 0
162 static void
163 apicdump0(struct apic *apic, int i)
164 {
165         if(!apic->useable || apic->addr != 0)
166                 return;
167         printk("apic%d: machno %d lint0 %#8.8ux lint1 %#8.8ux\n",
168                 i, apic->machno, apic->lvt[0], apic->lvt[1]);
169         printk(" tslvt %#8.8ux pclvt %#8.8ux elvt %#8.8ux\n",
170                 apicrget(Tslvt), apicrget(Pclvt), apicrget(Elvt));
171         printk(" tlvt %#8.8ux lint0 %#8.8ux lint1 %#8.8ux siv %#8.8ux\n",
172                 apicrget(Tlvt), apicrget(Lint0),
173                 apicrget(Lint1), apicrget(Siv));
174 }
175
176 void
177 apicdump(void)
178 {
179         int i;
180
181         if(!2)
182                 return;
183
184         printk("apicbase %#p apmachno %d\n", apicbase, apmachno);
185         for(i = 0; i < Napic; i++)
186                 apicdump0(xlapic + i, i);
187         for(i = 0; i < Napic; i++)
188                 apicdump0(xioapic + i, i);
189 }
190
191 static void
192 apictimer(Ureg* ureg, void*)
193 {
194         timerintr(ureg, 0);
195 }
196
197 int
198 apiconline(void)
199 {
200         struct apic *apic;
201         uint64_t tsc;
202         uint32_t dfr, ver;
203         int apicno, nlvt;
204
205         if(apicbase == NULL)
206                 return 0;
207         if((apicno = ((apicrget(Id)>>24) & 0xff)) >= Napic)
208                 return 0;
209         apic = &xlapic[apicno];
210         if(!apic->useable || apic->addr != NULL)
211                 return 0;
212
213         /*
214          * Things that can only be done when on the processor
215          * owning the APIC, apicinit above runs on the bootstrap
216          * processor.
217          */
218         ver = apicrget(Ver);
219         nlvt = ((ver>>16) & 0xff) + 1;
220         if(nlvt > ARRAY_SIZE(apic->lvt)){
221                 printd("apicinit%d: nlvt %d > max (%d)\n",
222                         apicno, nlvt, ARRAY_SIZE(apic->lvt));
223                 nlvt = ARRAY_SIZE(apic->lvt);
224         }
225         apic->nlvt = nlvt;
226         apic->ver = ver & 0xff;
227
228         /*
229          * These don't really matter in Physical mode;
230          * set the defaults anyway.
231          */
232         if(memcmp(m->cpuinfo, "AuthenticAMD", 12) == 0)
233                 dfr = 0xf0000000;
234         else
235                 dfr = 0xffffffff;
236         apicrput(Df, dfr);
237         apicrput(Ld, 0x00000000);
238
239         /*
240          * Disable interrupts until ready by setting the Task Priority
241          * register to 0xff.
242          */
243         apicrput(Tp, 0xff);
244
245         /*
246          * Software-enable the APIC in the Spurious Interrupt Vector
247          * register and set the vector number. The vector number must have
248          * bits 3-0 0x0f unless the Extended Spurious Vector Enable bit
249          * is set in the HyperTransport Transaction Control register.
250          */
251         apicrput(Siv, Swen|IdtSPURIOUS);
252
253         /*
254          * Acknowledge any outstanding interrupts.
255          */
256         apicrput(Eoi, 0);
257
258         /*
259          * Use the TSC to determine the APIC timer frequency.
260          * It might be possible to snarf this from a chipset
261          * register instead.
262          */
263         apicrput(Tdc, DivX1);
264         apicrput(Tlvt, Im);
265         tsc = rdtsc() + m->cpuhz/10;
266         apicrput(Tic, 0xffffffff);
267
268         while(rdtsc() < tsc)
269                 ;
270
271         apic->hz = (0xffffffff-apicrget(Tcc))*10;
272         apic->max = apic->hz/HZ;
273         apic->min = apic->hz/(100*HZ);
274         apic->div = ((m->cpuhz/apic->max)+HZ/2)/HZ;
275
276         if(m->machno == 0 || 2){
277                 printd("apic%d: hz %lld max %lld min %lld div %lld\n", apicno,
278                         apic->hz, apic->max, apic->min, apic->div);
279         }
280
281         /*
282          * Mask interrupts on Performance Counter overflow and
283          * Thermal Sensor if implemented, and on Lintr0 (Legacy INTR),
284          * and Lintr1 (Legacy NMI).
285          * Clear any Error Status (write followed by read) and enable
286          * the Error interrupt.
287          */
288         switch(apic->nlvt){
289         case 6:
290                 apicrput(Tslvt, Im);
291                 /*FALLTHROUGH*/
292         case 5:
293                 apicrput(Pclvt, Im);
294                 /*FALLTHROUGH*/
295         default:
296                 break;
297         }
298         apicrput(Lint1, apic->lvt[1]|Im|IdtLINT1);
299         apicrput(Lint0, apic->lvt[0]|Im|IdtLINT0);
300
301         apicrput(Es, 0);
302         apicrget(Es);
303         apicrput(Elvt, IdtERROR);
304
305         /*
306          * Issue an INIT Level De-Assert to synchronise arbitration ID's.
307          * (Necessary in this implementation? - not if Pentium 4 or Xeon
308          * (APIC Version >= 0x14), or AMD).
309         apicrput(Ichi, 0);
310         apicrput(Iclo, DSallinc|Lassert|MTir);
311         while(apicrget(Iclo) & Ds)
312                 ;
313          */
314
315         /*
316          * Reload the timer to de-synchronise the processors,
317          * then lower the task priority to allow interrupts to be
318          * accepted by the APIC.
319          */
320         microdelay((TK2MS(1)*1000/apmachno) * m->machno);
321
322         if(apic->machno == 0){
323                 apicrput(Tic, apic->max);
324                 intrenable(IdtTIMER, apictimer, 0, -1, "APIC timer");
325                 apicrput(Tlvt, Periodic|IrqTIMER);
326         }
327
328         if(m->machno == 0)
329                 apicrput(Tp, 0);
330
331         xlapicmachptr[apicno] = m;
332
333         return 1;
334 }
335
336 /* To start timers on TCs as part of the boot process. */
337 void
338 apictimerenab(void)
339 {
340         struct apic *apic;
341
342         apic = &xlapic[(apicrget(Id)>>24) & 0xff];
343
344         apiceoi(IdtTIMER);
345         apicrput(Tic, apic->max);
346         apicrput(Tlvt, Periodic|IrqTIMER);
347
348 }
349
350 void
351 apictimerset(uint64_t next)
352 {
353         Mpl pl;
354         struct apic *apic;
355         int64_t period;
356
357         apic = &xlapic[(apicrget(Id)>>24) & 0xff];
358
359         pl = splhi();
360         spin_lock(&(&m->apictimerlock)->lock);
361
362         period = apic->max;
363         if(next != 0){
364                 period = next - fastticks(NULL);        /* fastticks is just rdtsc() */
365                 period /= apic->div;
366
367                 if(period < apic->min)
368                         period = apic->min;
369                 else if(period > apic->max - apic->min)
370                         period = apic->max;
371         }
372         apicrput(Tic, period);
373
374         spin_unlock(&(&m->apictimerlock)->lock);
375         splx(pl);
376 }
377
378 void
379 apicsipi(int apicno, uintptr_t pa)
380 {
381         int i;
382         uint32_t crhi, crlo;
383
384         /*
385          * SIPI - Start-up IPI.
386          * To do: checks on apic validity.
387          */
388         crhi = apicno<<24;
389         apicrput(Ichi, crhi);
390         apicrput(Iclo, DSnone|TMlevel|Lassert|MTir);
391         microdelay(200);
392         apicrput(Iclo, DSnone|TMlevel|MTir);
393         millidelay(10);
394
395         crlo = DSnone|TMedge|MTsipi|((uint32_t)pa/(4*KiB));
396         for(i = 0; i < 2; i++){
397                 apicrput(Ichi, crhi);
398                 apicrput(Iclo, crlo);
399                 microdelay(200);
400         }
401 }
402
403 void
404 apicipi(int apicno)
405 {
406         apicrput(Ichi, apicno<<24);
407         apicrput(Iclo, DSnone|TMedge|Lassert|MTf|IdtIPI);
408         while(apicrget(Iclo) & Ds)
409                 ;
410 }
411
412 void
413 apicpri(int pri)
414 {
415         apicrput(Tp, pri);
416 }
417 #endif