]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/pc/apic.c
merge
[plan9front.git] / sys / src / 9 / pc / apic.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7
8 #include "mp.h"
9
10 enum {                                  /* Local APIC registers */
11         LapicID         = 0x0020,       /* ID */
12         LapicVER        = 0x0030,       /* Version */
13         LapicTPR        = 0x0080,       /* Task Priority */
14         LapicAPR        = 0x0090,       /* Arbitration Priority */
15         LapicPPR        = 0x00A0,       /* Processor Priority */
16         LapicEOI        = 0x00B0,       /* EOI */
17         LapicLDR        = 0x00D0,       /* Logical Destination */
18         LapicDFR        = 0x00E0,       /* Destination Format */
19         LapicSVR        = 0x00F0,       /* Spurious Interrupt Vector */
20         LapicISR        = 0x0100,       /* Interrupt Status (8 registers) */
21         LapicTMR        = 0x0180,       /* Trigger Mode (8 registers) */
22         LapicIRR        = 0x0200,       /* Interrupt Request (8 registers) */
23         LapicESR        = 0x0280,       /* Error Status */
24         LapicICRLO      = 0x0300,       /* Interrupt Command */
25         LapicICRHI      = 0x0310,       /* Interrupt Command [63:32] */
26         LapicTIMER      = 0x0320,       /* Local Vector Table 0 (TIMER) */
27         LapicPCINT      = 0x0340,       /* Performance Counter LVT */
28         LapicLINT0      = 0x0350,       /* Local Vector Table 1 (LINT0) */
29         LapicLINT1      = 0x0360,       /* Local Vector Table 2 (LINT1) */
30         LapicERROR      = 0x0370,       /* Local Vector Table 3 (ERROR) */
31         LapicTICR       = 0x0380,       /* Timer Initial Count */
32         LapicTCCR       = 0x0390,       /* Timer Current Count */
33         LapicTDCR       = 0x03E0,       /* Timer Divide Configuration */
34 };
35
36 enum {                                  /* LapicSVR */
37         LapicENABLE     = 0x00000100,   /* Unit Enable */
38         LapicFOCUS      = 0x00000200,   /* Focus Processor Checking Disable */
39 };
40
41 enum {                                  /* LapicICRLO */
42                                         /* [14] IPI Trigger Mode Level (RW) */
43         LapicDEASSERT   = 0x00000000,   /* Deassert level-sensitive interrupt */
44         LapicASSERT     = 0x00004000,   /* Assert level-sensitive interrupt */
45
46                                         /* [17:16] Remote Read Status */
47         LapicINVALID    = 0x00000000,   /* Invalid */
48         LapicWAIT       = 0x00010000,   /* In-Progress */
49         LapicVALID      = 0x00020000,   /* Valid */
50
51                                         /* [19:18] Destination Shorthand */
52         LapicFIELD      = 0x00000000,   /* No shorthand */
53         LapicSELF       = 0x00040000,   /* Self is single destination */
54         LapicALLINC     = 0x00080000,   /* All including self */
55         LapicALLEXC     = 0x000C0000,   /* All Excluding self */
56 };
57
58 enum {                                  /* LapicESR */
59         LapicSENDCS     = 0x00000001,   /* Send CS Error */
60         LapicRCVCS      = 0x00000002,   /* Receive CS Error */
61         LapicSENDACCEPT = 0x00000004,   /* Send Accept Error */
62         LapicRCVACCEPT  = 0x00000008,   /* Receive Accept Error */
63         LapicSENDVECTOR = 0x00000020,   /* Send Illegal Vector */
64         LapicRCVVECTOR  = 0x00000040,   /* Receive Illegal Vector */
65         LapicREGISTER   = 0x00000080,   /* Illegal Register Address */
66 };
67
68 enum {                                  /* LapicTIMER */
69                                         /* [17] Timer Mode (RW) */
70         LapicONESHOT    = 0x00000000,   /* One-shot */
71         LapicPERIODIC   = 0x00020000,   /* Periodic */
72
73                                         /* [19:18] Timer Base (RW) */
74         LapicCLKIN      = 0x00000000,   /* use CLKIN as input */
75         LapicTMBASE     = 0x00040000,   /* use TMBASE */
76         LapicDIVIDER    = 0x00080000,   /* use output of the divider */
77 };
78
79 static uchar lapictdxtab[] = {          /* LapicTDCR */
80         0x0B,   /* divide by 1 */
81         0x00,   /* divide by 2 */
82         0x01,   /* divide by 4 */
83         0x02,   /* divide by 8 */
84         0x03,   /* divide by 16 */
85         0x08,   /* divide by 32 */
86         0x09,   /* divide by 64 */
87         0x0A,   /* divide by 128 */
88 };
89
90 static ulong* lapicbase;
91
92 typedef struct Apictimer Apictimer;
93 struct Apictimer
94 {
95         uvlong  hz;
96         ulong   max;
97         ulong   min;
98         ulong   div;
99         int     tdx;
100 };
101
102 static Apictimer lapictimer[MAXMACH];
103
104 static ulong
105 lapicr(int r)
106 {
107         return *(lapicbase+(r/sizeof(*lapicbase)));
108 }
109
110 static void
111 lapicw(int r, ulong data)
112 {
113         *(lapicbase+(r/sizeof(*lapicbase))) = data;
114         data = *(lapicbase+(LapicID/sizeof(*lapicbase)));
115         USED(data);
116 }
117
118 void
119 lapiconline(void)
120 {
121         Apictimer *a;
122
123         a = &lapictimer[m->machno];
124
125         /*
126          * Reload the timer to de-synchronise the processors,
127          * then lower the task priority to allow interrupts to be
128          * accepted by the APIC.
129          */
130         microdelay((TK2MS(1)*1000/conf.nmach) * m->machno);
131         lapicw(LapicTICR, a->max);
132         lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER));
133
134         /*
135          * not strickly neccesary, but reported (osdev.org) to be
136          * required for some machines.
137          */
138         lapicw(LapicTDCR, lapictdxtab[a->tdx]);
139
140         lapicw(LapicTPR, 0);
141 }
142
143 /*
144  *  use the i8253/tsc clock to figure out our lapic timer rate.
145  */
146 static void
147 lapictimerinit(void)
148 {
149         uvlong x, v, hz;
150         Apictimer *a;
151         int s;
152
153         if(m->machno != 0){
154                 lapictimer[m->machno] = lapictimer[0];
155                 return;
156         }
157
158         s = splhi();
159         a = &lapictimer[m->machno];
160         a->tdx = 0;
161 Retry:
162         lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER));
163         lapicw(LapicTDCR, lapictdxtab[a->tdx]);
164
165         x = fastticks(&hz);
166         x += hz/10;
167         lapicw(LapicTICR, 0xffffffff);
168         do{
169                 v = fastticks(nil);
170         }while(v < x);
171
172         v = (0xffffffffUL-lapicr(LapicTCCR))*10;
173         if(v > hz-(hz/10)){
174                 if(v > hz+(hz/10) && a->tdx < nelem(lapictdxtab)-1){
175                         a->tdx++;
176                         goto Retry;
177                 }
178                 v = hz;
179         }
180
181         assert(v >= (100*HZ));
182
183         a->hz = v;
184         a->div = hz/a->hz;
185         a->max = a->hz/HZ;
186         a->min = a->hz/(100*HZ);
187
188         splx(s);
189
190         v = (v+500000LL)/1000000LL;
191         print("cpu%d: lapic clock at %lludMHz\n", m->machno, v);
192 }
193
194 void
195 lapicinit(Apic* apic)
196 {
197         ulong dfr, ldr, lvt;
198
199         if(lapicbase == 0)
200                 lapicbase = apic->addr;
201
202         /*
203          * These don't really matter in Physical mode;
204          * set the defaults anyway.
205          */
206         if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
207                 dfr = 0xf0000000;
208         else
209                 dfr = 0xffffffff;
210         ldr = 0x00000000;
211
212         lapicw(LapicDFR, dfr);
213         lapicw(LapicLDR, ldr);
214         lapicw(LapicTPR, 0xff);
215         lapicw(LapicSVR, LapicENABLE|(VectorPIC+IrqSPURIOUS));
216
217         lapictimerinit();
218
219         /*
220          * Some Pentium revisions have a bug whereby spurious
221          * interrupts are generated in the through-local mode.
222          */
223         switch(m->cpuidax & 0xFFF){
224         case 0x526:                             /* stepping cB1 */
225         case 0x52B:                             /* stepping E0 */
226         case 0x52C:                             /* stepping cC0 */
227                 wrmsr(0x0E, 1<<14);             /* TR12 */
228                 break;
229         }
230
231         /*
232          * Set the local interrupts. It's likely these should just be
233          * masked off for SMP mode as some Pentium Pros have problems if
234          * LINT[01] are set to ExtINT.
235          * Acknowledge any outstanding interrupts.
236         lapicw(LapicLINT0, apic->lintr[0]);
237         lapicw(LapicLINT1, apic->lintr[1]);
238          */
239         lapiceoi(0);
240
241         lvt = (lapicr(LapicVER)>>16) & 0xFF;
242         if(lvt >= 4)
243                 lapicw(LapicPCINT, ApicIMASK);
244         lapicw(LapicERROR, VectorPIC+IrqERROR);
245         lapicw(LapicESR, 0);
246         lapicr(LapicESR);
247
248         /*
249          * Issue an INIT Level De-Assert to synchronise arbitration ID's.
250          */
251         lapicw(LapicICRHI, 0);
252         lapicw(LapicICRLO, LapicALLINC|ApicLEVEL|LapicDEASSERT|ApicINIT);
253         while(lapicr(LapicICRLO) & ApicDELIVS)
254                 ;
255
256         /*
257          * Do not allow acceptance of interrupts until all initialisation
258          * for this processor is done. For the bootstrap processor this can be
259          * early duing initialisation. For the application processors this should
260          * be after the bootstrap processor has lowered priority and is accepting
261          * interrupts.
262         lapicw(LapicTPR, 0);
263          */
264 }
265
266 void
267 lapicstartap(Apic* apic, int v)
268 {
269         int i;
270         ulong crhi;
271
272         /* make apic's processor do a warm reset */
273         crhi = apic->apicno<<24;
274         lapicw(LapicICRHI, crhi);
275         lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT);
276         microdelay(200);
277         lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT);
278         delay(10);
279
280         /* assumes apic is not an 82489dx */
281         for(i = 0; i < 2; i++){
282                 lapicw(LapicICRHI, crhi);
283                 /* make apic's processor start at v in real mode */
284                 lapicw(LapicICRLO, LapicFIELD|ApicEDGE|ApicSTARTUP|(v/BY2PG));
285                 microdelay(200);
286         }
287 }
288
289 void
290 lapicerror(Ureg*, void*)
291 {
292         ulong esr;
293
294         lapicw(LapicESR, 0);
295         esr = lapicr(LapicESR);
296         switch(m->cpuidax & 0xFFF){
297         case 0x526:                             /* stepping cB1 */
298         case 0x52B:                             /* stepping E0 */
299         case 0x52C:                             /* stepping cC0 */
300                 return;
301         }
302         print("cpu%d: lapicerror: 0x%8.8luX\n", m->machno, esr);
303 }
304
305 void
306 lapicspurious(Ureg*, void*)
307 {
308         print("cpu%d: lapicspurious\n", m->machno);
309 }
310
311 int
312 lapicisr(int v)
313 {
314         ulong isr;
315
316         isr = lapicr(LapicISR + (v/32));
317
318         return isr & (1<<(v%32));
319 }
320
321 int
322 lapiceoi(int v)
323 {
324         lapicw(LapicEOI, 0);
325
326         return v;
327 }
328
329 void
330 lapicicrw(ulong hi, ulong lo)
331 {
332         lapicw(LapicICRHI, hi);
333         lapicw(LapicICRLO, lo);
334 }
335
336 void
337 ioapicrdtr(Apic* apic, int sel, int* hi, int* lo)
338 {
339         ulong *iowin;
340
341         iowin = apic->addr+(0x10/sizeof(ulong));
342         sel = IoapicRDT + 2*sel;
343
344         lock(apic);
345         *apic->addr = sel+1;
346         if(hi)
347                 *hi = *iowin;
348         *apic->addr = sel;
349         if(lo)
350                 *lo = *iowin;
351         unlock(apic);
352 }
353
354 void
355 ioapicrdtw(Apic* apic, int sel, int hi, int lo)
356 {
357         ulong *iowin;
358
359         iowin = apic->addr+(0x10/sizeof(ulong));
360         sel = IoapicRDT + 2*sel;
361
362         lock(apic);
363         *apic->addr = sel+1;
364         *iowin = hi;
365         *apic->addr = sel;
366         *iowin = lo;
367         unlock(apic);
368 }
369
370 void
371 ioapicinit(Apic* apic, int apicno)
372 {
373         int hi, lo, v;
374         ulong *iowin;
375
376         /*
377          * Initialise the I/O APIC.
378          * The MultiProcessor Specification says it is the responsibility
379          * of the O/S to set the APIC id.
380          * Make sure interrupts are all masked off for now.
381          */
382         iowin = apic->addr+(0x10/sizeof(ulong));
383         lock(apic);
384         *apic->addr = IoapicVER;
385         apic->mre = (*iowin>>16) & 0xFF;
386
387         *apic->addr = IoapicID;
388         *iowin = apicno<<24;
389         unlock(apic);
390
391         hi = 0;
392         lo = ApicIMASK;
393         for(v = 0; v <= apic->mre; v++)
394                 ioapicrdtw(apic, v, hi, lo);
395 }
396
397 void
398 lapictimerset(uvlong next)
399 {
400         vlong period;
401         Apictimer *a;
402
403         a = &lapictimer[m->machno];
404         period = next - fastticks(nil);
405         period /= a->div;
406         if(period < a->min)
407                 period = a->min;
408         else if(period > a->max - a->min)
409                 period = a->max;
410         lapicw(LapicTICR, period);
411 }
412
413 void
414 lapicclock(Ureg *u, void*)
415 {
416         /*
417          * since the MTRR updates need to be synchronized across processors,
418          * we want to do this within the clock tick.
419          */
420         mtrrclock();
421         timerintr(u, 0);
422 }
423
424 void
425 lapicintron(void)
426 {
427         lapicw(LapicTPR, 0);
428 }
429
430 void
431 lapicintroff(void)
432 {
433         lapicw(LapicTPR, 0xFF);
434 }
435
436 void
437 lapicnmienable(void)
438 {
439         lapicw(LapicPCINT, ApicNMI);
440 }
441
442 void
443 lapicnmidisable(void)
444 {
445         lapicw(LapicPCINT, ApicIMASK);
446 }