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