]> 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 struct
93 {
94         uvlong  hz;
95         ulong   max;
96         ulong   min;
97         ulong   div;
98         int     tdx;
99 } lapictimer;
100
101 static ulong
102 lapicr(int r)
103 {
104         return *(lapicbase+(r/sizeof(*lapicbase)));
105 }
106
107 static void
108 lapicw(int r, ulong data)
109 {
110         *(lapicbase+(r/sizeof(*lapicbase))) = data;
111         data = *(lapicbase+(LapicID/sizeof(*lapicbase)));
112         USED(data);
113 }
114
115 void
116 lapiconline(void)
117 {
118         /*
119          * Reload the timer to de-synchronise the processors,
120          * then lower the task priority to allow interrupts to be
121          * accepted by the APIC.
122          */
123         microdelay((TK2MS(1)*1000/conf.nmach) * m->machno);
124         lapicw(LapicTICR, lapictimer.max);
125         lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER));
126
127         lapicw(LapicTPR, 0);
128 }
129
130 /*
131  *  use the i8253 clock to figure out our lapic timer rate.
132  */
133 static void
134 lapictimerinit(void)
135 {
136 Retry:
137         lapicw(LapicTDCR, lapictdxtab[lapictimer.tdx]);
138         lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER));
139
140         if(lapictimer.hz == 0ULL){
141                 uvlong x, v, hz;
142
143                 x = fastticks(&hz);
144                 x += hz/10;
145                 lapicw(LapicTICR, 0xffffffff);
146                 do{
147                         v = fastticks(nil);
148                 }while(v < x);
149
150                 v = (0xffffffffUL-lapicr(LapicTCCR))*10;
151                 if(v > hz-(hz/10)){
152                         if(v > hz+(hz/10) && lapictimer.tdx < nelem(lapictdxtab)-1){
153                                 lapictimer.tdx++;
154                                 goto Retry;
155                         }
156                         v = hz;
157                 }
158                 assert(v != 0);
159
160                 lapictimer.hz = v;
161                 lapictimer.div = hz/lapictimer.hz;
162                 lapictimer.max = lapictimer.hz/HZ;
163                 lapictimer.min = lapictimer.hz/(100*HZ);
164         }
165 }
166
167 void
168 lapicinit(Apic* apic)
169 {
170         ulong dfr, ldr, lvt;
171
172         if(lapicbase == 0)
173                 lapicbase = apic->addr;
174
175         /*
176          * These don't really matter in Physical mode;
177          * set the defaults anyway.
178          */
179         if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
180                 dfr = 0xf0000000;
181         else
182                 dfr = 0xffffffff;
183         ldr = 0x00000000;
184
185         lapicw(LapicDFR, dfr);
186         lapicw(LapicLDR, ldr);
187         lapicw(LapicTPR, 0xff);
188         lapicw(LapicSVR, LapicENABLE|(VectorPIC+IrqSPURIOUS));
189
190         lapictimerinit();
191
192         /*
193          * Some Pentium revisions have a bug whereby spurious
194          * interrupts are generated in the through-local mode.
195          */
196         switch(m->cpuidax & 0xFFF){
197         case 0x526:                             /* stepping cB1 */
198         case 0x52B:                             /* stepping E0 */
199         case 0x52C:                             /* stepping cC0 */
200                 wrmsr(0x0E, 1<<14);             /* TR12 */
201                 break;
202         }
203
204         /*
205          * Set the local interrupts. It's likely these should just be
206          * masked off for SMP mode as some Pentium Pros have problems if
207          * LINT[01] are set to ExtINT.
208          * Acknowledge any outstanding interrupts.
209         lapicw(LapicLINT0, apic->lintr[0]);
210         lapicw(LapicLINT1, apic->lintr[1]);
211          */
212         lapiceoi(0);
213
214         lvt = (lapicr(LapicVER)>>16) & 0xFF;
215         if(lvt >= 4)
216                 lapicw(LapicPCINT, ApicIMASK);
217         lapicw(LapicERROR, VectorPIC+IrqERROR);
218         lapicw(LapicESR, 0);
219         lapicr(LapicESR);
220
221         /*
222          * Issue an INIT Level De-Assert to synchronise arbitration ID's.
223          */
224         lapicw(LapicICRHI, 0);
225         lapicw(LapicICRLO, LapicALLINC|ApicLEVEL|LapicDEASSERT|ApicINIT);
226         while(lapicr(LapicICRLO) & ApicDELIVS)
227                 ;
228
229         /*
230          * Do not allow acceptance of interrupts until all initialisation
231          * for this processor is done. For the bootstrap processor this can be
232          * early duing initialisation. For the application processors this should
233          * be after the bootstrap processor has lowered priority and is accepting
234          * interrupts.
235         lapicw(LapicTPR, 0);
236          */
237 }
238
239 void
240 lapicstartap(Apic* apic, int v)
241 {
242         int i;
243         ulong crhi;
244
245         /* make apic's processor do a warm reset */
246         crhi = apic->apicno<<24;
247         lapicw(LapicICRHI, crhi);
248         lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicASSERT|ApicINIT);
249         microdelay(200);
250         lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT);
251         delay(10);
252
253         /* assumes apic is not an 82489dx */
254         for(i = 0; i < 2; i++){
255                 lapicw(LapicICRHI, crhi);
256                 /* make apic's processor start at v in real mode */
257                 lapicw(LapicICRLO, LapicFIELD|ApicEDGE|ApicSTARTUP|(v/BY2PG));
258                 microdelay(200);
259         }
260 }
261
262 void
263 lapicerror(Ureg*, void*)
264 {
265         ulong esr;
266
267         lapicw(LapicESR, 0);
268         esr = lapicr(LapicESR);
269         switch(m->cpuidax & 0xFFF){
270         case 0x526:                             /* stepping cB1 */
271         case 0x52B:                             /* stepping E0 */
272         case 0x52C:                             /* stepping cC0 */
273                 return;
274         }
275         print("cpu%d: lapicerror: 0x%8.8luX\n", m->machno, esr);
276 }
277
278 void
279 lapicspurious(Ureg*, void*)
280 {
281         print("cpu%d: lapicspurious\n", m->machno);
282 }
283
284 int
285 lapicisr(int v)
286 {
287         ulong isr;
288
289         isr = lapicr(LapicISR + (v/32));
290
291         return isr & (1<<(v%32));
292 }
293
294 int
295 lapiceoi(int v)
296 {
297         lapicw(LapicEOI, 0);
298
299         return v;
300 }
301
302 void
303 lapicicrw(ulong hi, ulong lo)
304 {
305         lapicw(LapicICRHI, hi);
306         lapicw(LapicICRLO, lo);
307 }
308
309 void
310 ioapicrdtr(Apic* apic, int sel, int* hi, int* lo)
311 {
312         ulong *iowin;
313
314         iowin = apic->addr+(0x10/sizeof(ulong));
315         sel = IoapicRDT + 2*sel;
316
317         lock(apic);
318         *apic->addr = sel+1;
319         if(hi)
320                 *hi = *iowin;
321         *apic->addr = sel;
322         if(lo)
323                 *lo = *iowin;
324         unlock(apic);
325 }
326
327 void
328 ioapicrdtw(Apic* apic, int sel, int hi, int lo)
329 {
330         ulong *iowin;
331
332         iowin = apic->addr+(0x10/sizeof(ulong));
333         sel = IoapicRDT + 2*sel;
334
335         lock(apic);
336         *apic->addr = sel+1;
337         *iowin = hi;
338         *apic->addr = sel;
339         *iowin = lo;
340         unlock(apic);
341 }
342
343 void
344 ioapicinit(Apic* apic, int apicno)
345 {
346         int hi, lo, v;
347         ulong *iowin;
348
349         /*
350          * Initialise the I/O APIC.
351          * The MultiProcessor Specification says it is the responsibility
352          * of the O/S to set the APIC id.
353          * Make sure interrupts are all masked off for now.
354          */
355         iowin = apic->addr+(0x10/sizeof(ulong));
356         lock(apic);
357         *apic->addr = IoapicVER;
358         apic->mre = (*iowin>>16) & 0xFF;
359
360         *apic->addr = IoapicID;
361         *iowin = apicno<<24;
362         unlock(apic);
363
364         hi = 0;
365         lo = ApicIMASK;
366         for(v = 0; v <= apic->mre; v++)
367                 ioapicrdtw(apic, v, hi, lo);
368 }
369
370 void
371 lapictimerset(uvlong next)
372 {
373         vlong period;
374         int x;
375
376         x = splhi();
377         lock(&m->apictimerlock);
378
379         period = lapictimer.max;
380         if(next != 0){
381                 period = next - fastticks(nil);
382                 period /= lapictimer.div;
383
384                 if(period < lapictimer.min)
385                         period = lapictimer.min;
386                 else if(period > lapictimer.max - lapictimer.min)
387                         period = lapictimer.max;
388         }
389         lapicw(LapicTICR, period);
390
391         unlock(&m->apictimerlock);
392         splx(x);
393 }
394
395 void
396 lapicclock(Ureg *u, void*)
397 {
398         /*
399          * since the MTRR updates need to be synchronized across processors,
400          * we want to do this within the clock tick.
401          */
402         mtrrclock();
403         timerintr(u, 0);
404 }
405
406 void
407 lapicintron(void)
408 {
409         lapicw(LapicTPR, 0);
410 }
411
412 void
413 lapicintroff(void)
414 {
415         lapicw(LapicTPR, 0xFF);
416 }
417
418 void
419 lapicnmienable(void)
420 {
421         lapicw(LapicPCINT, ApicNMI);
422 }
423
424 void
425 lapicnmidisable(void)
426 {
427         lapicw(LapicPCINT, ApicIMASK);
428 }