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