Commit | Line | Data |
---|---|---|
c8fe38ae MD |
1 | /* |
2 | * Copyright (c) 1996, by Steve Passe | |
c8fe38ae MD |
3 | * All rights reserved. |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. The name of the developer may NOT be used to endorse or promote products | |
11 | * derived from this software without specific prior written permission. | |
12 | * | |
13 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
14 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
15 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
16 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
17 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
18 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
19 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
20 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
21 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
22 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
23 | * SUCH DAMAGE. | |
24 | * | |
25 | * $FreeBSD: src/sys/i386/i386/mpapic.c,v 1.37.2.7 2003/01/25 02:31:47 peter Exp $ | |
46d4e165 | 26 | * $DragonFly: src/sys/platform/pc32/apic/mpapic.c,v 1.22 2008/04/20 13:44:26 swildner Exp $ |
c8fe38ae MD |
27 | */ |
28 | ||
29 | #include <sys/param.h> | |
30 | #include <sys/systm.h> | |
46d4e165 | 31 | #include <sys/kernel.h> |
c8fe38ae MD |
32 | #include <machine/globaldata.h> |
33 | #include <machine/smp.h> | |
46d4e165 | 34 | #include <machine/md_var.h> |
c2abfdd7 | 35 | #include <machine/pmap.h> |
c8fe38ae MD |
36 | #include <machine_base/apic/mpapic.h> |
37 | #include <machine/segments.h> | |
38 | #include <sys/thread2.h> | |
39 | ||
40 | #include <machine_base/isa/intr_machdep.h> /* Xspuriousint() */ | |
41 | ||
bfc09ba0 MD |
42 | #include "apicvar.h" |
43 | ||
c8fe38ae MD |
44 | /* EISA Edge/Level trigger control registers */ |
45 | #define ELCR0 0x4d0 /* eisa irq 0-7 */ | |
46 | #define ELCR1 0x4d1 /* eisa irq 8-15 */ | |
47 | ||
46d4e165 JG |
48 | volatile lapic_t *lapic; |
49 | ||
50 | static void lapic_timer_calibrate(void); | |
51 | static void lapic_timer_set_divisor(int); | |
52 | static void lapic_timer_fixup_handler(void *); | |
53 | static void lapic_timer_restart_handler(void *); | |
54 | ||
55 | void lapic_timer_process(void); | |
56 | void lapic_timer_process_frame(struct intrframe *); | |
57 | ||
58 | static int lapic_timer_enable = 1; | |
59 | TUNABLE_INT("hw.lapic_timer_enable", &lapic_timer_enable); | |
60 | ||
61 | static void lapic_timer_intr_reload(struct cputimer_intr *, sysclock_t); | |
62 | static void lapic_timer_intr_enable(struct cputimer_intr *); | |
63 | static void lapic_timer_intr_restart(struct cputimer_intr *); | |
64 | static void lapic_timer_intr_pmfixup(struct cputimer_intr *); | |
65 | ||
66 | static struct cputimer_intr lapic_cputimer_intr = { | |
67 | .freq = 0, | |
68 | .reload = lapic_timer_intr_reload, | |
69 | .enable = lapic_timer_intr_enable, | |
70 | .config = cputimer_intr_default_config, | |
71 | .restart = lapic_timer_intr_restart, | |
72 | .pmfixup = lapic_timer_intr_pmfixup, | |
73 | .initclock = cputimer_intr_default_initclock, | |
74 | .next = SLIST_ENTRY_INITIALIZER, | |
75 | .name = "lapic", | |
76 | .type = CPUTIMER_INTR_LAPIC, | |
77 | .prio = CPUTIMER_INTR_PRIO_LAPIC, | |
78 | .caps = CPUTIMER_INTR_CAP_NONE | |
79 | }; | |
80 | ||
c8fe38ae MD |
81 | /* |
82 | * pointers to pmapped apic hardware. | |
83 | */ | |
84 | ||
85 | volatile ioapic_t **ioapic; | |
86 | ||
46d4e165 JG |
87 | static int lapic_timer_divisor_idx = -1; |
88 | static const uint32_t lapic_timer_divisors[] = { | |
89 | APIC_TDCR_2, APIC_TDCR_4, APIC_TDCR_8, APIC_TDCR_16, | |
90 | APIC_TDCR_32, APIC_TDCR_64, APIC_TDCR_128, APIC_TDCR_1 | |
91 | }; | |
92 | #define APIC_TIMER_NDIVISORS \ | |
93 | (int)(sizeof(lapic_timer_divisors) / sizeof(lapic_timer_divisors[0])) | |
94 | ||
95 | ||
96 | void | |
97 | lapic_eoi(void) | |
98 | { | |
99 | ||
100 | lapic->eoi = 0; | |
101 | } | |
a9e511df | 102 | |
c8fe38ae MD |
103 | /* |
104 | * Enable APIC, configure interrupts. | |
105 | */ | |
106 | void | |
46d4e165 | 107 | apic_initialize(boolean_t bsp) |
c8fe38ae | 108 | { |
46d4e165 | 109 | uint32_t timer; |
c8fe38ae MD |
110 | u_int temp; |
111 | ||
112 | /* | |
113 | * setup LVT1 as ExtINT on the BSP. This is theoretically an | |
114 | * aggregate interrupt input from the 8259. The INTA cycle | |
115 | * will be routed to the external controller (the 8259) which | |
116 | * is expected to supply the vector. | |
117 | * | |
118 | * Must be setup edge triggered, active high. | |
119 | * | |
120 | * Disable LVT1 on the APs. It doesn't matter what delivery | |
121 | * mode we use because we leave it masked. | |
122 | */ | |
46d4e165 | 123 | temp = lapic->lvt_lint0; |
c8fe38ae MD |
124 | temp &= ~(APIC_LVT_MASKED | APIC_LVT_TRIG_MASK | |
125 | APIC_LVT_POLARITY_MASK | APIC_LVT_DM_MASK); | |
126 | if (mycpu->gd_cpuid == 0) | |
127 | temp |= APIC_LVT_DM_EXTINT; | |
128 | else | |
129 | temp |= APIC_LVT_DM_FIXED | APIC_LVT_MASKED; | |
46d4e165 | 130 | lapic->lvt_lint0 = temp; |
c8fe38ae MD |
131 | |
132 | /* | |
133 | * setup LVT2 as NMI, masked till later. Edge trigger, active high. | |
134 | */ | |
46d4e165 | 135 | temp = lapic->lvt_lint1; |
c8fe38ae MD |
136 | temp &= ~(APIC_LVT_MASKED | APIC_LVT_TRIG_MASK | |
137 | APIC_LVT_POLARITY_MASK | APIC_LVT_DM_MASK); | |
138 | temp |= APIC_LVT_MASKED | APIC_LVT_DM_NMI; | |
46d4e165 | 139 | lapic->lvt_lint1 = temp; |
c8fe38ae MD |
140 | |
141 | /* | |
142 | * Mask the apic error interrupt, apic performance counter | |
46d4e165 | 143 | * interrupt. |
c8fe38ae | 144 | */ |
46d4e165 JG |
145 | lapic->lvt_error = lapic->lvt_error | APIC_LVT_MASKED; |
146 | lapic->lvt_pcint = lapic->lvt_pcint | APIC_LVT_MASKED; | |
147 | ||
148 | /* Set apic timer vector and mask the apic timer interrupt. */ | |
149 | timer = lapic->lvt_timer; | |
150 | timer &= ~APIC_LVTT_VECTOR; | |
151 | timer |= XTIMER_OFFSET; | |
152 | timer |= APIC_LVTT_MASKED; | |
153 | lapic->lvt_timer = timer; | |
c8fe38ae MD |
154 | |
155 | /* | |
156 | * Set the Task Priority Register as needed. At the moment allow | |
157 | * interrupts on all cpus (the APs will remain CLId until they are | |
158 | * ready to deal). We could disable all but IPIs by setting | |
159 | * temp |= TPR_IPI_ONLY for cpu != 0. | |
160 | */ | |
46d4e165 | 161 | temp = lapic->tpr; |
c8fe38ae MD |
162 | temp &= ~APIC_TPR_PRIO; /* clear priority field */ |
163 | #ifndef APIC_IO | |
164 | /* | |
165 | * If we are NOT running the IO APICs, the LAPIC will only be used | |
166 | * for IPIs. Set the TPR to prevent any unintentional interrupts. | |
167 | */ | |
168 | temp |= TPR_IPI_ONLY; | |
169 | #endif | |
170 | ||
46d4e165 | 171 | lapic->tpr = temp; |
c8fe38ae MD |
172 | |
173 | /* | |
174 | * enable the local APIC | |
175 | */ | |
46d4e165 | 176 | temp = lapic->svr; |
c8fe38ae MD |
177 | temp |= APIC_SVR_ENABLE; /* enable the APIC */ |
178 | temp &= ~APIC_SVR_FOCUS_DISABLE; /* enable lopri focus processor */ | |
179 | ||
180 | /* | |
181 | * Set the spurious interrupt vector. The low 4 bits of the vector | |
182 | * must be 1111. | |
183 | */ | |
184 | if ((XSPURIOUSINT_OFFSET & 0x0F) != 0x0F) | |
185 | panic("bad XSPURIOUSINT_OFFSET: 0x%08x", XSPURIOUSINT_OFFSET); | |
186 | temp &= ~APIC_SVR_VECTOR; | |
187 | temp |= XSPURIOUSINT_OFFSET; | |
188 | ||
46d4e165 | 189 | lapic->svr = temp; |
c8fe38ae MD |
190 | |
191 | /* | |
192 | * Pump out a few EOIs to clean out interrupts that got through | |
193 | * before we were able to set the TPR. | |
194 | */ | |
46d4e165 JG |
195 | lapic_eoi(); |
196 | lapic_eoi(); | |
197 | lapic_eoi(); | |
198 | ||
199 | if (bsp) { | |
200 | lapic_timer_calibrate(); | |
201 | if (lapic_timer_enable) { | |
202 | cputimer_intr_register(&lapic_cputimer_intr); | |
203 | cputimer_intr_select(&lapic_cputimer_intr, 0); | |
204 | } | |
205 | } else { | |
206 | lapic_timer_set_divisor(lapic_timer_divisor_idx); | |
207 | } | |
c8fe38ae MD |
208 | |
209 | if (bootverbose) | |
210 | apic_dump("apic_initialize()"); | |
211 | } | |
212 | ||
46d4e165 JG |
213 | |
214 | static void | |
215 | lapic_timer_set_divisor(int divisor_idx) | |
216 | { | |
217 | KKASSERT(divisor_idx >= 0 && divisor_idx < APIC_TIMER_NDIVISORS); | |
218 | lapic->dcr_timer = lapic_timer_divisors[divisor_idx]; | |
219 | } | |
220 | ||
221 | static void | |
222 | lapic_timer_oneshot(u_int count) | |
223 | { | |
224 | uint32_t value; | |
225 | ||
226 | value = lapic->lvt_timer; | |
227 | value &= ~APIC_LVTT_PERIODIC; | |
228 | lapic->lvt_timer = value; | |
229 | lapic->icr_timer = count; | |
230 | } | |
231 | ||
232 | static void | |
233 | lapic_timer_oneshot_quick(u_int count) | |
234 | { | |
235 | lapic->icr_timer = count; | |
236 | } | |
237 | ||
238 | static void | |
239 | lapic_timer_calibrate(void) | |
240 | { | |
241 | sysclock_t value; | |
242 | ||
243 | /* Try to calibrate the local APIC timer. */ | |
244 | for (lapic_timer_divisor_idx = 0; | |
245 | lapic_timer_divisor_idx < APIC_TIMER_NDIVISORS; | |
246 | lapic_timer_divisor_idx++) { | |
247 | lapic_timer_set_divisor(lapic_timer_divisor_idx); | |
248 | lapic_timer_oneshot(APIC_TIMER_MAX_COUNT); | |
249 | DELAY(2000000); | |
250 | value = APIC_TIMER_MAX_COUNT - lapic->ccr_timer; | |
251 | if (value != APIC_TIMER_MAX_COUNT) | |
252 | break; | |
253 | } | |
254 | if (lapic_timer_divisor_idx >= APIC_TIMER_NDIVISORS) | |
255 | panic("lapic: no proper timer divisor?!\n"); | |
256 | lapic_cputimer_intr.freq = value / 2; | |
257 | ||
258 | kprintf("lapic: divisor index %d, frequency %u Hz\n", | |
259 | lapic_timer_divisor_idx, lapic_cputimer_intr.freq); | |
260 | } | |
261 | ||
262 | static void | |
263 | lapic_timer_process_oncpu(struct globaldata *gd, struct intrframe *frame) | |
264 | { | |
265 | sysclock_t count; | |
266 | ||
267 | gd->gd_timer_running = 0; | |
268 | ||
269 | count = sys_cputimer->count(); | |
270 | if (TAILQ_FIRST(&gd->gd_systimerq) != NULL) | |
271 | systimer_intr(&count, 0, frame); | |
272 | } | |
273 | ||
a9e511df | 274 | void |
46d4e165 JG |
275 | lapic_timer_process(void) |
276 | { | |
277 | lapic_timer_process_oncpu(mycpu, NULL); | |
278 | } | |
279 | ||
280 | void | |
281 | lapic_timer_process_frame(struct intrframe *frame) | |
282 | { | |
283 | lapic_timer_process_oncpu(mycpu, frame); | |
284 | } | |
285 | ||
286 | static void | |
287 | lapic_timer_intr_reload(struct cputimer_intr *cti, sysclock_t reload) | |
288 | { | |
289 | struct globaldata *gd = mycpu; | |
290 | ||
291 | reload = (int64_t)reload * cti->freq / sys_cputimer->freq; | |
292 | if (reload < 2) | |
293 | reload = 2; | |
294 | ||
295 | if (gd->gd_timer_running) { | |
296 | if (reload < lapic->ccr_timer) | |
297 | lapic_timer_oneshot_quick(reload); | |
298 | } else { | |
299 | gd->gd_timer_running = 1; | |
300 | lapic_timer_oneshot_quick(reload); | |
301 | } | |
302 | } | |
303 | ||
304 | static void | |
305 | lapic_timer_intr_enable(struct cputimer_intr *cti __unused) | |
306 | { | |
307 | uint32_t timer; | |
308 | ||
309 | timer = lapic->lvt_timer; | |
310 | timer &= ~(APIC_LVTT_MASKED | APIC_LVTT_PERIODIC); | |
311 | lapic->lvt_timer = timer; | |
312 | ||
313 | lapic_timer_fixup_handler(NULL); | |
314 | } | |
315 | ||
316 | static void | |
317 | lapic_timer_fixup_handler(void *arg) | |
a9e511df | 318 | { |
46d4e165 JG |
319 | int *started = arg; |
320 | ||
321 | if (started != NULL) | |
322 | *started = 0; | |
323 | ||
324 | if (strcmp(cpu_vendor, "AuthenticAMD") == 0) { | |
325 | /* | |
326 | * Detect the presence of C1E capability mostly on latest | |
327 | * dual-cores (or future) k8 family. This feature renders | |
328 | * the local APIC timer dead, so we disable it by reading | |
329 | * the Interrupt Pending Message register and clearing both | |
330 | * C1eOnCmpHalt (bit 28) and SmiOnCmpHalt (bit 27). | |
331 | * | |
332 | * Reference: | |
333 | * "BIOS and Kernel Developer's Guide for AMD NPT | |
334 | * Family 0Fh Processors" | |
335 | * #32559 revision 3.00 | |
336 | */ | |
337 | if ((cpu_id & 0x00000f00) == 0x00000f00 && | |
338 | (cpu_id & 0x0fff0000) >= 0x00040000) { | |
339 | uint64_t msr; | |
340 | ||
341 | msr = rdmsr(0xc0010055); | |
342 | if (msr & 0x18000000) { | |
343 | struct globaldata *gd = mycpu; | |
344 | ||
345 | kprintf("cpu%d: AMD C1E detected\n", | |
346 | gd->gd_cpuid); | |
347 | wrmsr(0xc0010055, msr & ~0x18000000ULL); | |
348 | ||
349 | /* | |
350 | * We are kinda stalled; | |
351 | * kick start again. | |
352 | */ | |
353 | gd->gd_timer_running = 1; | |
354 | lapic_timer_oneshot_quick(2); | |
355 | ||
356 | if (started != NULL) | |
357 | *started = 1; | |
358 | } | |
359 | } | |
360 | } | |
a9e511df | 361 | } |
c8fe38ae | 362 | |
46d4e165 JG |
363 | static void |
364 | lapic_timer_restart_handler(void *dummy __unused) | |
365 | { | |
366 | int started; | |
367 | ||
368 | lapic_timer_fixup_handler(&started); | |
369 | if (!started) { | |
370 | struct globaldata *gd = mycpu; | |
371 | ||
372 | gd->gd_timer_running = 1; | |
373 | lapic_timer_oneshot_quick(2); | |
374 | } | |
375 | } | |
376 | ||
377 | /* | |
378 | * This function is called only by ACPI-CA code currently: | |
379 | * - AMD C1E fixup. AMD C1E only seems to happen after ACPI | |
380 | * module controls PM. So once ACPI-CA is attached, we try | |
381 | * to apply the fixup to prevent LAPIC timer from hanging. | |
382 | */ | |
383 | static void | |
384 | lapic_timer_intr_pmfixup(struct cputimer_intr *cti __unused) | |
385 | { | |
386 | lwkt_send_ipiq_mask(smp_active_mask, | |
387 | lapic_timer_fixup_handler, NULL); | |
388 | } | |
389 | ||
390 | static void | |
391 | lapic_timer_intr_restart(struct cputimer_intr *cti __unused) | |
392 | { | |
393 | lwkt_send_ipiq_mask(smp_active_mask, lapic_timer_restart_handler, NULL); | |
394 | } | |
395 | ||
396 | ||
c8fe38ae MD |
397 | /* |
398 | * dump contents of local APIC registers | |
399 | */ | |
400 | void | |
401 | apic_dump(char* str) | |
402 | { | |
403 | kprintf("SMP: CPU%d %s:\n", mycpu->gd_cpuid, str); | |
404 | kprintf(" lint0: 0x%08x lint1: 0x%08x TPR: 0x%08x SVR: 0x%08x\n", | |
46d4e165 | 405 | lapic->lvt_lint0, lapic->lvt_lint1, lapic->tpr, lapic->svr); |
c8fe38ae MD |
406 | } |
407 | ||
408 | ||
409 | #if defined(APIC_IO) | |
410 | ||
411 | /* | |
412 | * IO APIC code, | |
413 | */ | |
414 | ||
415 | #define IOAPIC_ISA_INTS 16 | |
416 | #define REDIRCNT_IOAPIC(A) \ | |
417 | ((int)((io_apic_versions[(A)] & IOART_VER_MAXREDIR) >> MAXREDIRSHIFT) + 1) | |
418 | ||
419 | static int trigger (int apic, int pin, u_int32_t * flags); | |
420 | static void polarity (int apic, int pin, u_int32_t * flags, int level); | |
421 | ||
422 | #define DEFAULT_FLAGS \ | |
423 | ((u_int32_t) \ | |
424 | (IOART_INTMSET | \ | |
425 | IOART_DESTPHY | \ | |
426 | IOART_DELLOPRI)) | |
427 | ||
428 | #define DEFAULT_ISA_FLAGS \ | |
429 | ((u_int32_t) \ | |
430 | (IOART_INTMSET | \ | |
431 | IOART_TRGREDG | \ | |
432 | IOART_INTAHI | \ | |
433 | IOART_DESTPHY | \ | |
434 | IOART_DELLOPRI)) | |
435 | ||
436 | void | |
437 | io_apic_set_id(int apic, int id) | |
438 | { | |
439 | u_int32_t ux; | |
440 | ||
441 | ux = io_apic_read(apic, IOAPIC_ID); /* get current contents */ | |
442 | if (((ux & APIC_ID_MASK) >> 24) != id) { | |
443 | kprintf("Changing APIC ID for IO APIC #%d" | |
444 | " from %d to %d on chip\n", | |
445 | apic, ((ux & APIC_ID_MASK) >> 24), id); | |
446 | ux &= ~APIC_ID_MASK; /* clear the ID field */ | |
447 | ux |= (id << 24); | |
448 | io_apic_write(apic, IOAPIC_ID, ux); /* write new value */ | |
449 | ux = io_apic_read(apic, IOAPIC_ID); /* re-read && test */ | |
450 | if (((ux & APIC_ID_MASK) >> 24) != id) | |
451 | panic("can't control IO APIC #%d ID, reg: 0x%08x", | |
452 | apic, ux); | |
453 | } | |
454 | } | |
455 | ||
456 | ||
457 | int | |
458 | io_apic_get_id(int apic) | |
459 | { | |
460 | return (io_apic_read(apic, IOAPIC_ID) & APIC_ID_MASK) >> 24; | |
461 | } | |
462 | ||
463 | ||
464 | ||
465 | /* | |
466 | * Setup the IO APIC. | |
467 | */ | |
c8fe38ae MD |
468 | void |
469 | io_apic_setup_intpin(int apic, int pin) | |
470 | { | |
471 | int bus, bustype, irq; | |
472 | u_char select; /* the select register is 8 bits */ | |
473 | u_int32_t flags; /* the window register is 32 bits */ | |
474 | u_int32_t target; /* the window register is 32 bits */ | |
475 | u_int32_t vector; /* the window register is 32 bits */ | |
476 | int level; | |
46d4e165 JG |
477 | int cpuid; |
478 | char envpath[32]; | |
c8fe38ae MD |
479 | |
480 | select = pin * 2 + IOAPIC_REDTBL0; /* register */ | |
481 | ||
482 | /* | |
483 | * Always clear an IO APIC pin before [re]programming it. This is | |
484 | * particularly important if the pin is set up for a level interrupt | |
485 | * as the IOART_REM_IRR bit might be set. When we reprogram the | |
486 | * vector any EOI from pending ints on this pin could be lost and | |
487 | * IRR might never get reset. | |
488 | * | |
489 | * To fix this problem, clear the vector and make sure it is | |
490 | * programmed as an edge interrupt. This should theoretically | |
491 | * clear IRR so we can later, safely program it as a level | |
492 | * interrupt. | |
493 | */ | |
494 | imen_lock(); | |
495 | ||
496 | flags = io_apic_read(apic, select) & IOART_RESV; | |
497 | flags |= IOART_INTMSET | IOART_TRGREDG | IOART_INTAHI; | |
498 | flags |= IOART_DESTPHY | IOART_DELFIXED; | |
499 | ||
500 | target = io_apic_read(apic, select + 1) & IOART_HI_DEST_RESV; | |
501 | target |= 0; /* fixed mode cpu mask of 0 - don't deliver anywhere */ | |
502 | ||
503 | vector = 0; | |
504 | ||
505 | io_apic_write(apic, select, flags | vector); | |
506 | io_apic_write(apic, select + 1, target); | |
507 | ||
508 | imen_unlock(); | |
509 | ||
510 | /* | |
511 | * We only deal with vectored interrupts here. ? documentation is | |
512 | * lacking, I'm guessing an interrupt type of 0 is the 'INT' type, | |
513 | * vs ExTINT, etc. | |
514 | * | |
515 | * This test also catches unconfigured pins. | |
516 | */ | |
517 | if (apic_int_type(apic, pin) != 0) | |
518 | return; | |
519 | ||
520 | /* | |
521 | * Leave the pin unprogrammed if it does not correspond to | |
522 | * an IRQ. | |
523 | */ | |
524 | irq = apic_irq(apic, pin); | |
525 | if (irq < 0) | |
526 | return; | |
527 | ||
528 | /* determine the bus type for this pin */ | |
529 | bus = apic_src_bus_id(apic, pin); | |
530 | if (bus < 0) | |
531 | return; | |
532 | bustype = apic_bus_type(bus); | |
533 | ||
534 | if ((bustype == ISA) && | |
535 | (pin < IOAPIC_ISA_INTS) && | |
536 | (irq == pin) && | |
537 | (apic_polarity(apic, pin) == 0x1) && | |
538 | (apic_trigger(apic, pin) == 0x3)) { | |
539 | /* | |
540 | * A broken BIOS might describe some ISA | |
541 | * interrupts as active-high level-triggered. | |
542 | * Use default ISA flags for those interrupts. | |
543 | */ | |
544 | flags = DEFAULT_ISA_FLAGS; | |
545 | } else { | |
546 | /* | |
547 | * Program polarity and trigger mode according to | |
548 | * interrupt entry. | |
549 | */ | |
550 | flags = DEFAULT_FLAGS; | |
551 | level = trigger(apic, pin, &flags); | |
552 | if (level == 1) | |
54d63b2f | 553 | int_to_apicintpin[irq].flags |= AIMI_FLAG_LEVEL; |
c8fe38ae MD |
554 | polarity(apic, pin, &flags, level); |
555 | } | |
46d4e165 JG |
556 | |
557 | cpuid = 0; | |
558 | ksnprintf(envpath, sizeof(envpath), "hw.irq.%d.dest", irq); | |
559 | kgetenv_int(envpath, &cpuid); | |
560 | ||
561 | /* ncpus may not be available yet */ | |
562 | if (cpuid > mp_naps) | |
563 | cpuid = 0; | |
564 | ||
c8fe38ae | 565 | if (bootverbose) { |
46d4e165 JG |
566 | kprintf("IOAPIC #%d intpin %d -> irq %d (CPU%d)\n", |
567 | apic, pin, irq, cpuid); | |
c8fe38ae MD |
568 | } |
569 | ||
570 | /* | |
571 | * Program the appropriate registers. This routing may be | |
572 | * overridden when an interrupt handler for a device is | |
573 | * actually added (see register_int(), which calls through | |
574 | * the MACHINTR ABI to set up an interrupt handler/vector). | |
575 | * | |
576 | * The order in which we must program the two registers for | |
577 | * safety is unclear! XXX | |
578 | */ | |
579 | imen_lock(); | |
580 | ||
581 | vector = IDT_OFFSET + irq; /* IDT vec */ | |
582 | target = io_apic_read(apic, select + 1) & IOART_HI_DEST_RESV; | |
46d4e165 JG |
583 | /* Deliver all interrupts to CPU0 (BSP) */ |
584 | target |= (CPU_TO_ID(cpuid) << IOART_HI_DEST_SHIFT) & | |
585 | IOART_HI_DEST_MASK; | |
c8fe38ae MD |
586 | flags |= io_apic_read(apic, select) & IOART_RESV; |
587 | io_apic_write(apic, select, flags | vector); | |
588 | io_apic_write(apic, select + 1, target); | |
589 | ||
590 | imen_unlock(); | |
591 | } | |
592 | ||
593 | int | |
594 | io_apic_setup(int apic) | |
595 | { | |
596 | int maxpin; | |
597 | int pin; | |
598 | ||
c8fe38ae MD |
599 | maxpin = REDIRCNT_IOAPIC(apic); /* pins in APIC */ |
600 | kprintf("Programming %d pins in IOAPIC #%d\n", maxpin, apic); | |
601 | ||
602 | for (pin = 0; pin < maxpin; ++pin) { | |
603 | io_apic_setup_intpin(apic, pin); | |
604 | } | |
605 | while (pin < 32) { | |
606 | if (apic_int_type(apic, pin) >= 0) { | |
607 | kprintf("Warning: IOAPIC #%d pin %d does not exist," | |
608 | " cannot program!\n", apic, pin); | |
609 | } | |
610 | ++pin; | |
611 | } | |
612 | ||
613 | /* return GOOD status */ | |
614 | return 0; | |
615 | } | |
616 | #undef DEFAULT_ISA_FLAGS | |
617 | #undef DEFAULT_FLAGS | |
618 | ||
619 | ||
620 | #define DEFAULT_EXTINT_FLAGS \ | |
621 | ((u_int32_t) \ | |
622 | (IOART_INTMSET | \ | |
623 | IOART_TRGREDG | \ | |
624 | IOART_INTAHI | \ | |
625 | IOART_DESTPHY | \ | |
626 | IOART_DELLOPRI)) | |
627 | ||
628 | /* | |
46d4e165 | 629 | * XXX this function is only used by 8254 setup |
c8fe38ae MD |
630 | * Setup the source of External INTerrupts. |
631 | */ | |
632 | int | |
633 | ext_int_setup(int apic, int intr) | |
634 | { | |
635 | u_char select; /* the select register is 8 bits */ | |
636 | u_int32_t flags; /* the window register is 32 bits */ | |
637 | u_int32_t target; /* the window register is 32 bits */ | |
638 | u_int32_t vector; /* the window register is 32 bits */ | |
46d4e165 JG |
639 | int cpuid; |
640 | char envpath[32]; | |
c8fe38ae MD |
641 | |
642 | if (apic_int_type(apic, intr) != 3) | |
643 | return -1; | |
644 | ||
46d4e165 JG |
645 | cpuid = 0; |
646 | ksnprintf(envpath, sizeof(envpath), "hw.irq.%d.dest", intr); | |
647 | kgetenv_int(envpath, &cpuid); | |
648 | ||
649 | /* ncpus may not be available yet */ | |
650 | if (cpuid > mp_naps) | |
651 | cpuid = 0; | |
652 | ||
653 | /* Deliver interrupts to CPU0 (BSP) */ | |
654 | target = (CPU_TO_ID(cpuid) << IOART_HI_DEST_SHIFT) & | |
655 | IOART_HI_DEST_MASK; | |
c8fe38ae MD |
656 | select = IOAPIC_REDTBL0 + (2 * intr); |
657 | vector = IDT_OFFSET + intr; | |
658 | flags = DEFAULT_EXTINT_FLAGS; | |
659 | ||
660 | io_apic_write(apic, select, flags | vector); | |
661 | io_apic_write(apic, select + 1, target); | |
662 | ||
663 | return 0; | |
664 | } | |
665 | #undef DEFAULT_EXTINT_FLAGS | |
666 | ||
667 | ||
668 | /* | |
669 | * Set the trigger level for an IO APIC pin. | |
670 | */ | |
671 | static int | |
672 | trigger(int apic, int pin, u_int32_t * flags) | |
673 | { | |
674 | int id; | |
675 | int eirq; | |
676 | int level; | |
677 | static int intcontrol = -1; | |
678 | ||
679 | switch (apic_trigger(apic, pin)) { | |
680 | ||
681 | case 0x00: | |
682 | break; | |
683 | ||
684 | case 0x01: | |
685 | *flags &= ~IOART_TRGRLVL; /* *flags |= IOART_TRGREDG */ | |
686 | return 0; | |
687 | ||
688 | case 0x03: | |
689 | *flags |= IOART_TRGRLVL; | |
690 | return 1; | |
691 | ||
692 | case -1: | |
693 | default: | |
694 | goto bad; | |
695 | } | |
696 | ||
697 | if ((id = apic_src_bus_id(apic, pin)) == -1) | |
698 | goto bad; | |
699 | ||
700 | switch (apic_bus_type(id)) { | |
701 | case ISA: | |
702 | *flags &= ~IOART_TRGRLVL; /* *flags |= IOART_TRGREDG; */ | |
703 | return 0; | |
704 | ||
705 | case EISA: | |
706 | eirq = apic_src_bus_irq(apic, pin); | |
707 | ||
708 | if (eirq < 0 || eirq > 15) { | |
709 | kprintf("EISA IRQ %d?!?!\n", eirq); | |
710 | goto bad; | |
711 | } | |
712 | ||
713 | if (intcontrol == -1) { | |
714 | intcontrol = inb(ELCR1) << 8; | |
715 | intcontrol |= inb(ELCR0); | |
716 | kprintf("EISA INTCONTROL = %08x\n", intcontrol); | |
717 | } | |
718 | ||
719 | /* Use ELCR settings to determine level or edge mode */ | |
720 | level = (intcontrol >> eirq) & 1; | |
721 | ||
722 | /* | |
723 | * Note that on older Neptune chipset based systems, any | |
724 | * pci interrupts often show up here and in the ELCR as well | |
725 | * as level sensitive interrupts attributed to the EISA bus. | |
726 | */ | |
727 | ||
728 | if (level) | |
729 | *flags |= IOART_TRGRLVL; | |
730 | else | |
731 | *flags &= ~IOART_TRGRLVL; | |
732 | ||
733 | return level; | |
734 | ||
735 | case PCI: | |
736 | *flags |= IOART_TRGRLVL; | |
737 | return 1; | |
738 | ||
739 | case -1: | |
740 | default: | |
741 | goto bad; | |
742 | } | |
743 | ||
744 | bad: | |
745 | panic("bad APIC IO INT flags"); | |
746 | } | |
747 | ||
748 | ||
749 | /* | |
750 | * Set the polarity value for an IO APIC pin. | |
751 | */ | |
752 | static void | |
753 | polarity(int apic, int pin, u_int32_t * flags, int level) | |
754 | { | |
755 | int id; | |
756 | ||
757 | switch (apic_polarity(apic, pin)) { | |
758 | ||
759 | case 0x00: | |
760 | break; | |
761 | ||
762 | case 0x01: | |
763 | *flags &= ~IOART_INTALO; /* *flags |= IOART_INTAHI */ | |
764 | return; | |
765 | ||
766 | case 0x03: | |
767 | *flags |= IOART_INTALO; | |
768 | return; | |
769 | ||
770 | case -1: | |
771 | default: | |
772 | goto bad; | |
773 | } | |
774 | ||
775 | if ((id = apic_src_bus_id(apic, pin)) == -1) | |
776 | goto bad; | |
777 | ||
778 | switch (apic_bus_type(id)) { | |
779 | case ISA: | |
780 | *flags &= ~IOART_INTALO; /* *flags |= IOART_INTAHI */ | |
781 | return; | |
782 | ||
783 | case EISA: | |
784 | /* polarity converter always gives active high */ | |
785 | *flags &= ~IOART_INTALO; | |
786 | return; | |
787 | ||
788 | case PCI: | |
789 | *flags |= IOART_INTALO; | |
790 | return; | |
791 | ||
792 | case -1: | |
793 | default: | |
794 | goto bad; | |
795 | } | |
796 | ||
797 | bad: | |
798 | panic("bad APIC IO INT flags"); | |
799 | } | |
800 | ||
801 | ||
802 | /* | |
803 | * Print contents of apic_imen. | |
804 | */ | |
805 | extern u_int apic_imen; /* keep apic_imen 'opaque' */ | |
806 | void | |
807 | imen_dump(void) | |
808 | { | |
809 | int x; | |
810 | ||
811 | kprintf("SMP: enabled INTs: "); | |
812 | for (x = 0; x < 24; ++x) | |
813 | if ((apic_imen & (1 << x)) == 0) | |
814 | kprintf("%d, ", x); | |
815 | kprintf("apic_imen: 0x%08x\n", apic_imen); | |
816 | } | |
817 | ||
818 | ||
819 | /* | |
820 | * Inter Processor Interrupt functions. | |
821 | */ | |
822 | ||
823 | #endif /* APIC_IO */ | |
824 | ||
825 | /* | |
826 | * Send APIC IPI 'vector' to 'destType' via 'deliveryMode'. | |
827 | * | |
828 | * destType is 1 of: APIC_DEST_SELF, APIC_DEST_ALLISELF, APIC_DEST_ALLESELF | |
829 | * vector is any valid SYSTEM INT vector | |
830 | * delivery_mode is 1 of: APIC_DELMODE_FIXED, APIC_DELMODE_LOWPRIO | |
831 | * | |
832 | * A backlog of requests can create a deadlock between cpus. To avoid this | |
833 | * we have to be able to accept IPIs at the same time we are trying to send | |
834 | * them. The critical section prevents us from attempting to send additional | |
835 | * IPIs reentrantly, but also prevents IPIQ processing so we have to call | |
836 | * lwkt_process_ipiq() manually. It's rather messy and expensive for this | |
837 | * to occur but fortunately it does not happen too often. | |
838 | */ | |
839 | int | |
840 | apic_ipi(int dest_type, int vector, int delivery_mode) | |
841 | { | |
842 | u_long icr_lo; | |
843 | ||
844 | crit_enter(); | |
46d4e165 JG |
845 | if ((lapic->icr_lo & APIC_DELSTAT_MASK) != 0) { |
846 | unsigned long rflags = read_rflags(); | |
c8fe38ae | 847 | cpu_enable_intr(); |
46d4e165 | 848 | while ((lapic->icr_lo & APIC_DELSTAT_MASK) != 0) { |
c8fe38ae MD |
849 | lwkt_process_ipiq(); |
850 | } | |
46d4e165 | 851 | write_rflags(rflags); |
c8fe38ae MD |
852 | } |
853 | ||
46d4e165 | 854 | icr_lo = (lapic->icr_lo & APIC_ICRLO_RESV_MASK) | dest_type | |
c8fe38ae | 855 | delivery_mode | vector; |
46d4e165 | 856 | lapic->icr_lo = icr_lo; |
c8fe38ae MD |
857 | crit_exit(); |
858 | return 0; | |
859 | } | |
860 | ||
861 | void | |
862 | single_apic_ipi(int cpu, int vector, int delivery_mode) | |
863 | { | |
864 | u_long icr_lo; | |
865 | u_long icr_hi; | |
866 | ||
867 | crit_enter(); | |
46d4e165 JG |
868 | if ((lapic->icr_lo & APIC_DELSTAT_MASK) != 0) { |
869 | unsigned long rflags = read_rflags(); | |
c8fe38ae | 870 | cpu_enable_intr(); |
46d4e165 | 871 | while ((lapic->icr_lo & APIC_DELSTAT_MASK) != 0) { |
c8fe38ae MD |
872 | lwkt_process_ipiq(); |
873 | } | |
46d4e165 | 874 | write_rflags(rflags); |
c8fe38ae | 875 | } |
46d4e165 | 876 | icr_hi = lapic->icr_hi & ~APIC_ID_MASK; |
c8fe38ae | 877 | icr_hi |= (CPU_TO_ID(cpu) << 24); |
46d4e165 | 878 | lapic->icr_hi = icr_hi; |
c8fe38ae | 879 | |
b2f93ae9 | 880 | /* build ICR_LOW */ |
46d4e165 | 881 | icr_lo = (lapic->icr_lo & APIC_ICRLO_RESV_MASK) |
c8fe38ae MD |
882 | | APIC_DEST_DESTFLD | delivery_mode | vector; |
883 | ||
884 | /* write APIC ICR */ | |
46d4e165 | 885 | lapic->icr_lo = icr_lo; |
c8fe38ae MD |
886 | crit_exit(); |
887 | } | |
888 | ||
889 | #if 0 | |
890 | ||
891 | /* | |
892 | * Returns 0 if the apic is busy, 1 if we were able to queue the request. | |
893 | * | |
894 | * NOT WORKING YET! The code as-is may end up not queueing an IPI at all | |
895 | * to the target, and the scheduler does not 'poll' for IPI messages. | |
896 | */ | |
897 | int | |
898 | single_apic_ipi_passive(int cpu, int vector, int delivery_mode) | |
899 | { | |
900 | u_long icr_lo; | |
901 | u_long icr_hi; | |
902 | ||
903 | crit_enter(); | |
46d4e165 | 904 | if ((lapic->icr_lo & APIC_DELSTAT_MASK) != 0) { |
c8fe38ae MD |
905 | crit_exit(); |
906 | return(0); | |
907 | } | |
46d4e165 | 908 | icr_hi = lapic->icr_hi & ~APIC_ID_MASK; |
c8fe38ae | 909 | icr_hi |= (CPU_TO_ID(cpu) << 24); |
46d4e165 | 910 | lapic->icr_hi = icr_hi; |
c8fe38ae MD |
911 | |
912 | /* build IRC_LOW */ | |
46d4e165 | 913 | icr_lo = (lapic->icr_lo & APIC_RESV2_MASK) |
c8fe38ae MD |
914 | | APIC_DEST_DESTFLD | delivery_mode | vector; |
915 | ||
916 | /* write APIC ICR */ | |
46d4e165 | 917 | lapic->icr_lo = icr_lo; |
c8fe38ae MD |
918 | crit_exit(); |
919 | return(1); | |
920 | } | |
921 | ||
922 | #endif | |
923 | ||
924 | /* | |
925 | * Send APIC IPI 'vector' to 'target's via 'delivery_mode'. | |
926 | * | |
927 | * target is a bitmask of destination cpus. Vector is any | |
928 | * valid system INT vector. Delivery mode may be either | |
929 | * APIC_DELMODE_FIXED or APIC_DELMODE_LOWPRIO. | |
930 | */ | |
931 | void | |
932 | selected_apic_ipi(u_int target, int vector, int delivery_mode) | |
933 | { | |
934 | crit_enter(); | |
935 | while (target) { | |
936 | int n = bsfl(target); | |
937 | target &= ~(1 << n); | |
938 | single_apic_ipi(n, vector, delivery_mode); | |
939 | } | |
940 | crit_exit(); | |
941 | } | |
942 | ||
943 | /* | |
944 | * Timer code, in development... | |
945 | * - suggested by rgrimes@gndrsh.aac.dev.com | |
946 | */ | |
bb467734 MD |
947 | int |
948 | get_apic_timer_frequency(void) | |
949 | { | |
950 | return(lapic_cputimer_intr.freq); | |
951 | } | |
c8fe38ae | 952 | |
c8fe38ae MD |
953 | /* |
954 | * Load a 'downcount time' in uSeconds. | |
955 | */ | |
956 | void | |
46d4e165 | 957 | set_apic_timer(int us) |
c8fe38ae | 958 | { |
46d4e165 | 959 | u_int count; |
c8fe38ae MD |
960 | |
961 | /* | |
46d4e165 JG |
962 | * When we reach here, lapic timer's frequency |
963 | * must have been calculated as well as the | |
964 | * divisor (lapic->dcr_timer is setup during the | |
965 | * divisor calculation). | |
c8fe38ae | 966 | */ |
46d4e165 JG |
967 | KKASSERT(lapic_cputimer_intr.freq != 0 && |
968 | lapic_timer_divisor_idx >= 0); | |
969 | ||
970 | count = ((us * (int64_t)lapic_cputimer_intr.freq) + 999999) / 1000000; | |
971 | lapic_timer_oneshot(count); | |
c8fe38ae MD |
972 | } |
973 | ||
974 | ||
975 | /* | |
976 | * Read remaining time in timer. | |
977 | */ | |
978 | int | |
979 | read_apic_timer(void) | |
980 | { | |
981 | #if 0 | |
982 | /** XXX FIXME: we need to return the actual remaining time, | |
983 | * for now we just return the remaining count. | |
984 | */ | |
985 | #else | |
46d4e165 | 986 | return lapic->ccr_timer; |
c8fe38ae MD |
987 | #endif |
988 | } | |
989 | ||
990 | ||
991 | /* | |
992 | * Spin-style delay, set delay time in uS, spin till it drains. | |
993 | */ | |
994 | void | |
995 | u_sleep(int count) | |
996 | { | |
997 | set_apic_timer(count); | |
998 | while (read_apic_timer()) | |
999 | /* spin */ ; | |
1000 | } | |
c2abfdd7 MN |
1001 | |
1002 | void | |
1003 | lapic_init(vm_offset_t lapic_addr) | |
1004 | { | |
1005 | /* | |
1006 | * lapic not mapped yet (pmap_init is called too late) | |
1007 | */ | |
1008 | lapic = pmap_mapdev_uncacheable(lapic_addr, sizeof(struct LAPIC)); | |
1009 | ||
1010 | #if 0 | |
1011 | /* Local apic is mapped on last page */ | |
1012 | SMPpt[NPTEPG - 1] = (pt_entry_t)(PG_V | PG_RW | PG_N | | |
1013 | pmap_get_pgeflag() | (lapic_addr & PG_FRAME)); | |
1014 | #endif | |
1015 | ||
1016 | kprintf("lapic: at 0x%08lx\n", lapic_addr); | |
1017 | } | |
91903a05 MN |
1018 | |
1019 | static TAILQ_HEAD(, lapic_enumerator) lapic_enumerators = | |
1020 | TAILQ_HEAD_INITIALIZER(lapic_enumerators); | |
1021 | ||
1022 | void | |
1023 | lapic_config(void) | |
1024 | { | |
1025 | struct lapic_enumerator *e; | |
1026 | int error; | |
1027 | ||
1028 | TAILQ_FOREACH(e, &lapic_enumerators, lapic_link) { | |
1029 | error = e->lapic_probe(e); | |
1030 | if (!error) | |
1031 | break; | |
1032 | } | |
1033 | if (e == NULL) | |
1034 | panic("can't config lapic\n"); | |
1035 | ||
1036 | e->lapic_enumerate(e); | |
1037 | } | |
1038 | ||
1039 | void | |
1040 | lapic_enumerator_register(struct lapic_enumerator *ne) | |
1041 | { | |
1042 | struct lapic_enumerator *e; | |
1043 | ||
1044 | TAILQ_FOREACH(e, &lapic_enumerators, lapic_link) { | |
1045 | if (e->lapic_prio < ne->lapic_prio) { | |
1046 | TAILQ_INSERT_BEFORE(e, ne, lapic_link); | |
1047 | return; | |
1048 | } | |
1049 | } | |
1050 | TAILQ_INSERT_TAIL(&lapic_enumerators, ne, lapic_link); | |
1051 | } |