xref: /plan9/sys/src/9/pc/apic.c (revision 850dd0ca1bdf035e1410f3ad77ab360045f4571d)
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 	timerintr(u, 0);
383 }
384 
385 void
386 lapicintron(void)
387 {
388 	lapicw(LapicTPR, 0);
389 }
390 
391 void
392 lapicintroff(void)
393 {
394 	lapicw(LapicTPR, 0xFF);
395 }
396 
397 void
398 lapicnmienable(void)
399 {
400 	lapicw(LapicPCINT, ApicNMI);
401 }
402 
403 void
404 lapicnmidisable(void)
405 {
406 	lapicw(LapicPCINT, ApicIMASK);
407 }
408