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