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