xref: /plan9-contrib/sys/src/9k/k10/apic.c (revision 9ef1f84b659abcb917c5c090acbce0772e494f21)
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 
7 #include "apic.h"
8 #include "io.h"
9 
10 enum {						/* Local APIC registers */
11 	Id		= 0x0020,		/* Identification */
12 	Ver		= 0x0030,		/* Version */
13 	Tp		= 0x0080,		/* Task Priority */
14 	Ap		= 0x0090,		/* Arbitration Priority */
15 	Pp		= 0x00a0,		/* Processor Priority */
16 	Eoi		= 0x00b0,		/* EOI */
17 	Ld		= 0x00d0,		/* Logical Destination */
18 	Df		= 0x00e0,		/* Destination Format */
19 	Siv		= 0x00f0,		/* Spurious Interrupt Vector */
20 	Is		= 0x0100,		/* Interrupt Status (8) */
21 	Tm		= 0x0180,		/* Trigger Mode (8) */
22 	Ir		= 0x0200,		/* Interrupt Request (8) */
23 	Es		= 0x0280,		/* Error Status */
24 	Iclo		= 0x0300,		/* Interrupt Command */
25 	Ichi		= 0x0310,		/* Interrupt Command [63:32] */
26 	Lvt0		= 0x0320,		/* Local Vector Table 0 */
27 	Lvt5		= 0x0330,		/* Local Vector Table 5 */
28 	Lvt4		= 0x0340,		/* Local Vector Table 4 */
29 	Lvt1		= 0x0350,		/* Local Vector Table 1 */
30 	Lvt2		= 0x0360,		/* Local Vector Table 2 */
31 	Lvt3		= 0x0370,		/* Local Vector Table 3 */
32 	Tic		= 0x0380,		/* Timer Initial Count */
33 	Tcc		= 0x0390,		/* Timer Current Count */
34 	Tdc		= 0x03e0,		/* Timer Divide Configuration */
35 
36 	Tlvt		= Lvt0,			/* Timer */
37 	Lint0		= Lvt1,			/* Local Interrupt 0 */
38 	Lint1		= Lvt2,			/* Local Interrupt 1 */
39 	Elvt		= Lvt3,			/* Error */
40 	Pmclvt		= Lvt4,			/* Performance Mon. Counter */
41 	Thslvt		= Lvt5,			/* Thermal Sensor */
42 };
43 
44 enum {						/* Siv */
45 	Swen		= 0x00000100,		/* Software Enable */
46 	Fdis		= 0x00000200,		/* Focus Disable */
47 };
48 
49 enum {						/* Iclo */
50 	Lassert		= 0x00004000,		/* Assert level */
51 
52 	DSnone		= 0x00000000,		/* Use Destination Field */
53 	DSself		= 0x00040000,		/* Self is only destination */
54 	DSallinc	= 0x00080000,		/* All including self */
55 	DSallexc	= 0x000c0000,		/* All Excluding self */
56 };
57 
58 enum {						/* Tlvt */
59 	Periodic	= 0x00020000,		/* Periodic Timer Mode */
60 };
61 
62 enum {						/* Tdc */
63 	DivX2		= 0x00000000,		/* Divide by 2 */
64 	DivX4		= 0x00000001,		/* Divide by 4 */
65 	DivX8		= 0x00000002,		/* Divide by 8 */
66 	DivX16		= 0x00000003,		/* Divide by 16 */
67 	DivX32		= 0x00000008,		/* Divide by 32 */
68 	DivX64		= 0x00000009,		/* Divide by 64 */
69 	DivX128		= 0x0000000a,		/* Divide by 128 */
70 	DivX1		= 0x0000000b,		/* Divide by 1 */
71 };
72 
73 static u8int* apicbase;
74 
75 static u32int
apicrget(int r)76 apicrget(int r)
77 {
78 	return *((u32int*)(apicbase+r));
79 }
80 
81 static void
apicrput(int r,u32int data)82 apicrput(int r, u32int data)
83 {
84 	*((u32int*)(apicbase+r)) = data;
85 }
86 
87 void
apictprput(int priority)88 apictprput(int priority)
89 {
90 	apicrput(Tp, priority);
91 }
92 
93 int
apiceoi(int vecno)94 apiceoi(int vecno)
95 {
96 	apicrput(Eoi, 0);
97 
98 	return vecno;
99 }
100 
101 int
apicisr(int vecno)102 apicisr(int vecno)
103 {
104 	int isr;
105 
106 	isr = apicrget(Is + (vecno/32)*16);
107 
108 	return isr & (1<<(vecno%32));
109 }
110 
111 void
apicinit(int apicno,uintmem pa,int isbp)112 apicinit(int apicno, uintmem pa, int isbp)
113 {
114 	Apic *apic;
115 
116 	/*
117 	 * Mark the APIC useable if it has a good ID
118 	 * and the registers can be mapped.
119 	 * The APIC Extended Broadcast and ID bits in the HyperTransport
120 	 * Transaction Control register determine whether 4 or 8 bits
121 	 * are used for the APIC ID. There is also xAPIC and x2APIC
122 	 * to be dealt with sometime.
123 	 */
124 	DBG("apic%d: pa %#P isbp %d\n", apicno, pa, isbp);
125 	if(apicno >= Napic){
126 		print("apic%d: out of range\n", apicno);
127 		return;
128 	}
129 	if((apic = &xapic[apicno])->useable){
130 		print("apicinit%d: already initialised\n", apicno);
131 		return;
132 	}
133 	if(apicbase == nil){
134 		if((apicbase = vmap(pa, 1024)) == nil){
135 			print("apic%d: can't map apicbase\n", apicno);
136 			return;
137 		}
138 		DBG("apic%d: apicbase %#P -> %#p\n", apicno, pa, apicbase);
139 	}
140 	apic->useable = 1;
141 
142 	/*
143 	 * Assign a machno to the processor associated with this
144 	 * APIC, it may not be an identity map.
145 	 * Machno 0 is always the bootstrap processor.
146 	 */
147 	if(isbp){
148 		apic->machno = 0;
149 		m->apicno = apicno;
150 	}
151 	else
152 		apic->machno = sys->nmach++;
153 }
154 
155 void
apicdump(void)156 apicdump(void)
157 {
158 	int i;
159 	Apic *apic;
160 
161 	if(!DBGFLG)
162 		return;
163 
164 	DBG("apicbase %#p sys->nmach %d\n", apicbase, sys->nmach);
165 	for(i = 0; i < Napic; i++){
166 		apic = &xapic[i];
167 		if(!apic->useable || apic->addr != 0)
168 			continue;
169 		DBG("apic%d: machno %d lint0 %#8.8ux lint1 %#8.8ux\n",
170 			i, apic->machno, apic->lvt[0], apic->lvt[1]);
171 		DBG(" thslvt %#8.8ux pmclvt %#8.8ux elvt %#8.8ux\n",
172 			apicrget(Thslvt), apicrget(Pmclvt), apicrget(Elvt));
173 		DBG(" tlvt %#8.8ux lint0 %#8.8ux lint1 %#8.8ux siv %#8.8ux\n",
174 			apicrget(Tlvt), apicrget(Lint0),
175 			apicrget(Lint1), apicrget(Siv));
176 	}
177 }
178 
179 int
apiconline(void)180 apiconline(void)
181 {
182 	Apic *apic;
183 	u64int tsc;
184 	u32int dfr, ver;
185 	int apicno, nlvt;
186 
187 	if(apicbase == nil)
188 		return 0;
189 	if((apicno = ((apicrget(Id)>>24) & 0xff)) >= Napic)
190 		return 0;
191 	apic = &xapic[apicno];
192 	if(!apic->useable || apic->addr != nil)
193 		return 0;
194 
195 	/*
196 	 * Things that can only be done when on the processor
197 	 * owning the APIC, apicinit above runs on the bootstrap
198 	 * processor.
199 	 */
200 	ver = apicrget(Ver);
201 	nlvt = ((ver>>16) & 0xff) + 1;
202 	if(nlvt > nelem(apic->lvt)){
203 		print("apicinit%d: nlvt %d > max (%d)\n",
204 			apicno, nlvt, nelem(apic->lvt));
205 		nlvt = nelem(apic->lvt);
206 	}
207 	apic->nlvt = nlvt;
208 	apic->ver = ver & 0xff;
209 
210 	/*
211 	 * These don't really matter in Physical mode;
212 	 * set the defaults anyway.
213 	 */
214 	if(memcmp(&m->cpuinfo[0][1], "AuthenticAMD", 12) == 0)
215 		dfr = 0xf0000000;
216 	else
217 		dfr = 0xffffffff;
218 	apicrput(Df, dfr);
219 	apicrput(Ld, 0x00000000);
220 
221 	/*
222 	 * Disable interrupts until ready by setting the Task Priority
223 	 * register to 0xff.
224 	 */
225 	apicrput(Tp, 0xff);
226 
227 	/*
228 	 * Software-enable the APIC in the Spurious Interrupt Vector
229 	 * register and set the vector number. The vector number must have
230 	 * bits 3-0 0x0f unless the Extended Spurious Vector Enable bit
231 	 * is set in the HyperTransport Transaction Control register.
232 	 */
233 	apicrput(Siv, Swen|IdtSPURIOUS);
234 
235 	/*
236 	 * Acknowledge any outstanding interrupts.
237 	 */
238 	apicrput(Eoi, 0);
239 
240 	/*
241 	 * Use the TSC to determine the APIC timer frequency.
242 	 * It might be possible to snarf this from a chipset
243 	 * register instead.
244 	 */
245 	apicrput(Tdc, DivX1);
246 	apicrput(Tlvt, Im|IdtTIMER);
247 	tsc = rdtsc() + m->cpuhz/10;
248 	apicrput(Tic, 0xffffffff);
249 
250 	while(rdtsc() < tsc)
251 		;
252 
253 	apic->hz = (0xffffffff-apicrget(Tcc))*10;
254 	apic->max = apic->hz/HZ;
255 	apic->min = apic->hz/(100*HZ);
256 	apic->div = ((m->cpuhz/apic->max)+HZ/2)/HZ;
257 
258 	if(m->machno == 0 || DBGFLG){
259 		print("apic%d: hz %lld max %lld min %lld div %lld\n", apicno,
260 			apic->hz, apic->max, apic->min, apic->div);
261 	}
262 
263 	/*
264 	 * Mask interrupts on Performance Monitor Counter overflow and
265 	 * Thermal Sensor if implemented, and on Lintr0 (Legacy INTR),
266 	 * and Lintr1 (Legacy NMI).
267 	 * Clear any Error Status (write followed by read) and enable
268 	 * the Error interrupt.
269 	 */
270 	switch(apic->nlvt){
271 	case 6:
272 		apicrput(Thslvt, Im|IdtTHS);
273 		/*FALLTHROUGH*/
274 	case 5:
275 		apicrput(Pmclvt, Im|IdtPMC);
276 		/*FALLTHROUGH*/
277 	default:
278 		break;
279 	}
280 	apicrput(Lint1, apic->lvt[1]|Im|IdtLINT1);
281 	apicrput(Lint0, apic->lvt[0]|Im|IdtLINT0);
282 
283 	apicrput(Es, 0);
284 	apicrget(Es);
285 	apicrput(Elvt, IdtERROR);
286 
287 	/*
288 	 * Issue an INIT Level De-Assert to synchronise arbitration ID's.
289 	 * (Necessary in this implementation? - not if Pentium 4 or Xeon
290 	 * (APIC Version >= 0x14), or AMD).
291 	apicrput(Ichi, 0);
292 	apicrput(Iclo, DSallinc|Lassert|MTir);
293 	while(apicrget(Iclo) & Ds)
294 		;
295 	 */
296 
297 	/*
298 	 * Reload the timer to de-synchronise the processors.
299 	 * When the caller is ready for the APIC to accept interrupts,
300 	 * it should call apictprput to lower the task priority.
301 	 *
302 	 * The timer is enabled later by the core-specific startup
303 	 * i.e. don't start the timer unless the core needs it,
304 	 * to reduce the likelihood of at least one (spurious) interrupt
305 	 * from the timer when priority is lowered.
306 	 */
307 	microdelay((TK2MS(1)*1000/sys->nmach) * m->machno);
308 	apicrput(Tic, apic->max);
309 
310 	return 1;
311 }
312 
313 void
apictimerenable(void)314 apictimerenable(void)
315 {
316 	/*
317 	 * Perhaps apictimerenable/apictimerdisable should just
318 	 * clear/set Im in the existing settings of Tlvt, there may
319 	 * be a time when the timer is used in a different mode;
320 	 * if so will need to ensure the mode is set when the timer
321 	 * is initialised.
322 	 */
323 	apicrput(Tlvt, Periodic|IdtTIMER);
324 }
325 
326 void
apictimerdisable(void)327 apictimerdisable(void)
328 {
329 	apicrput(Tlvt, Im|IdtTIMER);
330 }
331 
332 void
apictimerset(uvlong next)333 apictimerset(uvlong next)
334 {
335 	Mpl pl;
336 	Apic *apic;
337 	vlong period;
338 
339 	/*
340 	 * APIC Timer.
341 	 */
342 	apic = &xapic[m->apicno];
343 
344 	pl = splhi();
345 	lock(&m->apictimerlock);
346 
347 	period = apic->max;
348 	if(next != 0){
349 		period = next - fastticks(nil);	/* fastticks is just rdtsc() */
350 		period /= apic->div;
351 
352 		if(period < apic->min)
353 			period = apic->min;
354 		else if(period > apic->max - apic->min)
355 			period = apic->max;
356 	}
357 	apicrput(Tic, period);
358 
359 	unlock(&m->apictimerlock);
360 	splx(pl);
361 }
362 
363 void
apictimerintr(Ureg * ureg,void *)364 apictimerintr(Ureg* ureg, void*)
365 {
366 	timerintr(ureg, 0);
367 }
368 
369 void
apicsipi(int apicno,uintptr pa)370 apicsipi(int apicno, uintptr pa)
371 {
372 	int i;
373 	u32int crhi, crlo;
374 
375 	/*
376 	 * SIPI - Start-up IPI.
377 	 * To do: checks on apic validity.
378 	 */
379 	crhi = apicno<<24;
380 	apicrput(Ichi, crhi);
381 	apicrput(Iclo, DSnone|TMlevel|Lassert|MTir);
382 	microdelay(200);
383 	apicrput(Iclo, DSnone|TMlevel|MTir);
384 	millidelay(10);
385 
386 	crlo = DSnone|TMedge|MTsipi|((u32int)pa/(4*KiB));
387 	for(i = 0; i < 2; i++){
388 		apicrput(Ichi, crhi);
389 		apicrput(Iclo, crlo);
390 		microdelay(200);
391 	}
392 }
393 
394 void
apicipi(int apicno)395 apicipi(int apicno)
396 {
397 	apicrput(Ichi, apicno<<24);
398 	apicrput(Iclo, DSnone|TMedge|Lassert|MTf|IdtIPI);
399 	while(apicrget(Iclo) & Ds)
400 		;
401 }
402