2 #include "../port/lib.h"
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 */
37 LapicENABLE = 0x00000100, /* Unit Enable */
38 LapicFOCUS = 0x00000200, /* Focus Processor Checking Disable */
41 enum { /* LapicICRLO */
42 /* [14] IPI Trigger Mode Level (RW) */
43 LapicDEASSERT = 0x00000000, /* Deassert level-sensitive interrupt */
44 LapicASSERT = 0x00004000, /* Assert level-sensitive interrupt */
46 /* [17:16] Remote Read Status */
47 LapicINVALID = 0x00000000, /* Invalid */
48 LapicWAIT = 0x00010000, /* In-Progress */
49 LapicVALID = 0x00020000, /* Valid */
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 */
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 */
68 enum { /* LapicTIMER */
69 /* [17] Timer Mode (RW) */
70 LapicONESHOT = 0x00000000, /* One-shot */
71 LapicPERIODIC = 0x00020000, /* Periodic */
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 */
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 */
90 static ulong* lapicbase;
92 typedef struct Apictimer Apictimer;
102 static Apictimer lapictimer[MAXMACH];
107 return *(lapicbase+(r/sizeof(*lapicbase)));
111 lapicw(int r, ulong data)
113 *(lapicbase+(r/sizeof(*lapicbase))) = data;
114 data = *(lapicbase+(LapicID/sizeof(*lapicbase)));
123 a = &lapictimer[m->machno];
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.
130 microdelay((TK2MS(1)*1000/conf.nmach) * m->machno);
131 lapicw(LapicTICR, a->max);
132 lapicw(LapicTIMER, LapicCLKIN|LapicPERIODIC|(VectorPIC+IrqTIMER));
135 * not strickly neccesary, but reported (osdev.org) to be
136 * required for some machines.
138 lapicw(LapicTDCR, lapictdxtab[a->tdx]);
144 * use the i8253/tsc clock to figure out our lapic timer rate.
154 lapictimer[m->machno] = lapictimer[0];
159 a = &lapictimer[m->machno];
162 lapicw(LapicTIMER, ApicIMASK|LapicCLKIN|LapicONESHOT|(VectorPIC+IrqTIMER));
163 lapicw(LapicTDCR, lapictdxtab[a->tdx]);
167 lapicw(LapicTICR, 0xffffffff);
172 v = (0xffffffffUL-lapicr(LapicTCCR))*10;
174 if(v > hz+(hz/10) && a->tdx < nelem(lapictdxtab)-1){
181 assert(v >= (100*HZ));
186 a->min = a->hz/(100*HZ);
190 v = (v+500000LL)/1000000LL;
191 print("cpu%d: lapic clock at %lludMHz\n", m->machno, v);
195 lapicinit(Apic* apic)
200 lapicbase = apic->addr;
203 * These don't really matter in Physical mode;
204 * set the defaults anyway.
206 if(strncmp(m->cpuidid, "AuthenticAMD", 12) == 0)
212 lapicw(LapicDFR, dfr);
213 lapicw(LapicLDR, ldr);
214 lapicw(LapicTPR, 0xff);
215 lapicw(LapicSVR, LapicENABLE|(VectorPIC+IrqSPURIOUS));
220 * Some Pentium revisions have a bug whereby spurious
221 * interrupts are generated in the through-local mode.
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 */
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]);
241 lvt = (lapicr(LapicVER)>>16) & 0xFF;
243 lapicw(LapicPCINT, ApicIMASK);
244 lapicw(LapicERROR, VectorPIC+IrqERROR);
249 * Issue an INIT Level De-Assert to synchronise arbitration ID's.
251 lapicw(LapicICRHI, 0);
252 lapicw(LapicICRLO, LapicALLINC|ApicLEVEL|LapicDEASSERT|ApicINIT);
253 while(lapicr(LapicICRLO) & ApicDELIVS)
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
267 lapicstartap(Apic* apic, int v)
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);
277 lapicw(LapicICRLO, LapicFIELD|ApicLEVEL|LapicDEASSERT|ApicINIT);
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));
290 lapicerror(Ureg*, void*)
295 esr = lapicr(LapicESR);
296 switch(m->cpuidax & 0xFFF){
297 case 0x526: /* stepping cB1 */
298 case 0x52B: /* stepping E0 */
299 case 0x52C: /* stepping cC0 */
302 print("cpu%d: lapicerror: 0x%8.8luX\n", m->machno, esr);
306 lapicspurious(Ureg*, void*)
308 print("cpu%d: lapicspurious\n", m->machno);
316 isr = lapicr(LapicISR + (v/32));
318 return isr & (1<<(v%32));
330 lapicicrw(ulong hi, ulong lo)
332 lapicw(LapicICRHI, hi);
333 lapicw(LapicICRLO, lo);
337 ioapicrdtr(Apic* apic, int sel, int* hi, int* lo)
341 iowin = apic->addr+(0x10/sizeof(ulong));
342 sel = IoapicRDT + 2*sel;
355 ioapicrdtw(Apic* apic, int sel, int hi, int lo)
359 iowin = apic->addr+(0x10/sizeof(ulong));
360 sel = IoapicRDT + 2*sel;
371 ioapicinit(Apic* apic, int apicno)
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.
382 iowin = apic->addr+(0x10/sizeof(ulong));
384 *apic->addr = IoapicVER;
385 apic->mre = (*iowin>>16) & 0xFF;
387 *apic->addr = IoapicID;
393 for(v = 0; v <= apic->mre; v++)
394 ioapicrdtw(apic, v, hi, lo);
398 lapictimerset(uvlong next)
403 a = &lapictimer[m->machno];
404 period = next - fastticks(nil);
408 else if(period > a->max - a->min)
410 lapicw(LapicTICR, period);
414 lapicclock(Ureg *u, void*)
417 * since the MTRR updates need to be synchronized across processors,
418 * we want to do this within the clock tick.
433 lapicw(LapicTPR, 0xFF);
439 lapicw(LapicPCINT, ApicNMI);
443 lapicnmidisable(void)
445 lapicw(LapicPCINT, ApicIMASK);