x86_64: Move ioapic function declarations from smp.h to apic/ioapic.h
[dragonfly.git] / sys / platform / pc64 / apic / ioapic.c
CommitLineData
3e8e985f
SZ
1/*
2 * Copyright (c) 1996, by Steve Passe
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 $
26 */
27
28#include <sys/param.h>
29#include <sys/systm.h>
30#include <sys/kernel.h>
31#include <sys/bus.h>
32#include <sys/machintr.h>
33#include <machine/globaldata.h>
34#include <machine/smp.h>
35#include <machine/md_var.h>
36#include <machine/pmap.h>
2b6cd37e 37#include <machine_base/apic/lapic.h>
61452645 38#include <machine_base/apic/ioapic.h>
3e8e985f
SZ
39#include <machine_base/apic/ioapic_abi.h>
40#include <machine/segments.h>
41#include <sys/thread2.h>
42
43#include <machine/intr_machdep.h>
44
45#include "apicvar.h"
46
47#define IOAPIC_COUNT_MAX 16
48#define IOAPIC_ID_MASK (IOAPIC_COUNT_MAX - 1)
49
50struct ioapic_info {
51 int io_idx;
52 int io_apic_id;
53 void *io_addr;
54 int io_npin;
55 int io_gsi_base;
56
57 TAILQ_ENTRY(ioapic_info) io_link;
58};
59TAILQ_HEAD(ioapic_info_list, ioapic_info);
60
61struct ioapic_intsrc {
62 int int_gsi;
63 enum intr_trigger int_trig;
64 enum intr_polarity int_pola;
65};
66
67struct ioapic_conf {
68 struct ioapic_info_list ioc_list;
69 struct ioapic_intsrc ioc_intsrc[16]; /* XXX magic number */
70};
71
72static void ioapic_setup(const struct ioapic_info *);
73static int ioapic_alloc_apic_id(int);
74static void ioapic_set_apic_id(const struct ioapic_info *);
75static void ioapic_gsi_setup(int);
76static const struct ioapic_info *
77 ioapic_gsi_search(int);
78static void ioapic_pin_prog(void *, int, int,
79 enum intr_trigger, enum intr_polarity, uint32_t);
80
81static struct ioapic_conf ioapic_conf;
82
83static TAILQ_HEAD(, ioapic_enumerator) ioapic_enumerators =
84 TAILQ_HEAD_INITIALIZER(ioapic_enumerators);
85
86void
87ioapic_config(void)
88{
89 struct ioapic_enumerator *e;
90 struct ioapic_info *info;
91 int start_apic_id = 0;
92 int error, i;
93 register_t ef = 0;
94
95 TAILQ_INIT(&ioapic_conf.ioc_list);
96 /* XXX magic number */
97 for (i = 0; i < 16; ++i)
98 ioapic_conf.ioc_intsrc[i].int_gsi = -1;
99
100 TAILQ_FOREACH(e, &ioapic_enumerators, ioapic_link) {
101 error = e->ioapic_probe(e);
102 if (!error)
103 break;
104 }
105 if (e == NULL) {
106#ifdef notyet
107 panic("can't config I/O APIC\n");
108#else
109 kprintf("no I/O APIC\n");
110 return;
111#endif
112 }
113
114 crit_enter();
115
116 ef = read_rflags();
117 cpu_disable_intr();
118
119 /*
120 * Switch to I/O APIC MachIntrABI and reconfigure
121 * the default IDT entries.
122 */
123 MachIntrABI = MachIntrABI_IOAPIC;
124 MachIntrABI.setdefault();
125
126 e->ioapic_enumerate(e);
127
128 /*
129 * Setup index
130 */
131 i = 0;
132 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
133 info->io_idx = i++;
134
135 if (i > IOAPIC_COUNT_MAX) /* XXX magic number */
136 panic("ioapic_config: more than 16 I/O APIC\n");
137
138 /*
139 * Setup APIC ID
140 */
141 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
142 int apic_id;
143
144 apic_id = ioapic_alloc_apic_id(start_apic_id);
145 if (apic_id == NAPICID) {
146 kprintf("IOAPIC: can't alloc APIC ID for "
147 "%dth I/O APIC\n", info->io_idx);
148 break;
149 }
150 info->io_apic_id = apic_id;
151
152 start_apic_id = apic_id + 1;
153 }
154 if (info != NULL) {
155 /*
156 * xAPIC allows I/O APIC's APIC ID to be same
157 * as the LAPIC's APIC ID
158 */
159 kprintf("IOAPIC: use xAPIC model to alloc APIC ID "
160 "for I/O APIC\n");
161
162 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
163 info->io_apic_id = info->io_idx;
164 }
165
166 /*
167 * Warning about any GSI holes
168 */
169 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
170 const struct ioapic_info *prev_info;
171
172 prev_info = TAILQ_PREV(info, ioapic_info_list, io_link);
173 if (prev_info != NULL) {
174 if (info->io_gsi_base !=
175 prev_info->io_gsi_base + prev_info->io_npin) {
176 kprintf("IOAPIC: warning gsi hole "
177 "[%d, %d]\n",
178 prev_info->io_gsi_base +
179 prev_info->io_npin,
180 info->io_gsi_base - 1);
181 }
182 }
183 }
184
185 if (bootverbose) {
186 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
187 kprintf("IOAPIC: idx %d, apic id %d, "
188 "gsi base %d, npin %d\n",
189 info->io_idx,
190 info->io_apic_id,
191 info->io_gsi_base,
192 info->io_npin);
193 }
194 }
195
196 /*
197 * Setup all I/O APIC
198 */
199 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link)
200 ioapic_setup(info);
201 ioapic_abi_fixup_irqmap();
202
203 write_rflags(ef);
204
205 MachIntrABI.cleanup();
206
207 crit_exit();
208}
209
210void
211ioapic_enumerator_register(struct ioapic_enumerator *ne)
212{
213 struct ioapic_enumerator *e;
214
215 TAILQ_FOREACH(e, &ioapic_enumerators, ioapic_link) {
216 if (e->ioapic_prio < ne->ioapic_prio) {
217 TAILQ_INSERT_BEFORE(e, ne, ioapic_link);
218 return;
219 }
220 }
221 TAILQ_INSERT_TAIL(&ioapic_enumerators, ne, ioapic_link);
222}
223
224void
225ioapic_add(void *addr, int gsi_base, int npin)
226{
227 struct ioapic_info *info, *ninfo;
228 int gsi_end;
229
230 gsi_end = gsi_base + npin - 1;
231 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
232 if ((gsi_base >= info->io_gsi_base &&
233 gsi_base < info->io_gsi_base + info->io_npin) ||
234 (gsi_end >= info->io_gsi_base &&
235 gsi_end < info->io_gsi_base + info->io_npin)) {
236 panic("ioapic_add: overlapped gsi, base %d npin %d, "
237 "hit base %d, npin %d\n", gsi_base, npin,
238 info->io_gsi_base, info->io_npin);
239 }
240 if (info->io_addr == addr)
241 panic("ioapic_add: duplicated addr %p\n", addr);
242 }
243
244 ninfo = kmalloc(sizeof(*ninfo), M_DEVBUF, M_WAITOK | M_ZERO);
245 ninfo->io_addr = addr;
246 ninfo->io_npin = npin;
247 ninfo->io_gsi_base = gsi_base;
248 ninfo->io_apic_id = -1;
249
250 /*
251 * Create IOAPIC list in ascending order of GSI base
252 */
253 TAILQ_FOREACH_REVERSE(info, &ioapic_conf.ioc_list,
254 ioapic_info_list, io_link) {
255 if (ninfo->io_gsi_base > info->io_gsi_base) {
256 TAILQ_INSERT_AFTER(&ioapic_conf.ioc_list,
257 info, ninfo, io_link);
258 break;
259 }
260 }
261 if (info == NULL)
262 TAILQ_INSERT_HEAD(&ioapic_conf.ioc_list, ninfo, io_link);
263}
264
265void
266ioapic_intsrc(int irq, int gsi, enum intr_trigger trig, enum intr_polarity pola)
267{
268 struct ioapic_intsrc *int_src;
269
270 KKASSERT(irq < 16);
271 int_src = &ioapic_conf.ioc_intsrc[irq];
272
273 if (gsi == 0) {
274 /* Don't allow mixed mode */
275 kprintf("IOAPIC: warning intsrc irq %d -> gsi 0\n", irq);
276 return;
277 }
278
279 if (int_src->int_gsi != -1) {
280 if (int_src->int_gsi != gsi) {
281 kprintf("IOAPIC: warning intsrc irq %d, gsi "
282 "%d -> %d\n", irq, int_src->int_gsi, gsi);
283 }
284 if (int_src->int_trig != trig) {
285 kprintf("IOAPIC: warning intsrc irq %d, trig "
286 "%s -> %s\n", irq,
287 intr_str_trigger(int_src->int_trig),
288 intr_str_trigger(trig));
289 }
290 if (int_src->int_pola != pola) {
291 kprintf("IOAPIC: warning intsrc irq %d, pola "
292 "%s -> %s\n", irq,
293 intr_str_polarity(int_src->int_pola),
294 intr_str_polarity(pola));
295 }
296 }
297 int_src->int_gsi = gsi;
298 int_src->int_trig = trig;
299 int_src->int_pola = pola;
300}
301
302static void
303ioapic_set_apic_id(const struct ioapic_info *info)
304{
305 uint32_t id;
306 int apic_id;
307
308 id = ioapic_read(info->io_addr, IOAPIC_ID);
309
310 id &= ~APIC_ID_MASK;
311 id |= (info->io_apic_id << 24);
312
313 ioapic_write(info->io_addr, IOAPIC_ID, id);
314
315 /*
316 * Re-read && test
317 */
318 id = ioapic_read(info->io_addr, IOAPIC_ID);
319 apic_id = (id & APIC_ID_MASK) >> 24;
320
321 /*
322 * I/O APIC ID is a 4bits field
323 */
324 if ((apic_id & IOAPIC_ID_MASK) !=
325 (info->io_apic_id & IOAPIC_ID_MASK)) {
326 panic("ioapic_set_apic_id: can't set apic id to %d, "
327 "currently set to %d\n", info->io_apic_id, apic_id);
328 }
329}
330
331static void
332ioapic_gsi_setup(int gsi)
333{
334 enum intr_trigger trig;
335 enum intr_polarity pola;
336 int irq;
337
338 if (gsi == 0) {
339 /* ExtINT */
340 imen_lock();
341 ioapic_extpin_setup(ioapic_gsi_ioaddr(gsi),
342 ioapic_gsi_pin(gsi), 0);
343 imen_unlock();
344 return;
345 }
346
347 for (irq = 0; irq < 16; ++irq) {
348 const struct ioapic_intsrc *int_src =
349 &ioapic_conf.ioc_intsrc[irq];
350
351 if (gsi == int_src->int_gsi) {
352 trig = int_src->int_trig;
353 pola = int_src->int_pola;
354 break;
355 }
356 }
357
358 if (irq == 16) {
359 if (gsi < 16) {
360 trig = INTR_TRIGGER_EDGE;
361 pola = INTR_POLARITY_HIGH;
362 } else {
363 trig = INTR_TRIGGER_LEVEL;
364 pola = INTR_POLARITY_LOW;
365 }
366 irq = gsi;
367 }
368
369 ioapic_abi_set_irqmap(irq, gsi, trig, pola);
370}
371
372void *
373ioapic_gsi_ioaddr(int gsi)
374{
375 const struct ioapic_info *info;
376
377 info = ioapic_gsi_search(gsi);
378 return info->io_addr;
379}
380
381int
382ioapic_gsi_pin(int gsi)
383{
384 const struct ioapic_info *info;
385
386 info = ioapic_gsi_search(gsi);
387 return gsi - info->io_gsi_base;
388}
389
390static const struct ioapic_info *
391ioapic_gsi_search(int gsi)
392{
393 const struct ioapic_info *info;
394
395 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
396 if (gsi >= info->io_gsi_base &&
397 gsi < info->io_gsi_base + info->io_npin)
398 return info;
399 }
400 panic("ioapic_gsi_search: no I/O APIC\n");
401}
402
403int
404ioapic_gsi(int idx, int pin)
405{
406 const struct ioapic_info *info;
407
408 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
409 if (info->io_idx == idx)
410 break;
411 }
412 if (info == NULL)
413 return -1;
414 if (pin >= info->io_npin)
415 return -1;
416 return info->io_gsi_base + pin;
417}
418
419void
420ioapic_extpin_setup(void *addr, int pin, int vec)
421{
422 ioapic_pin_prog(addr, pin, vec,
423 INTR_TRIGGER_CONFORM, INTR_POLARITY_CONFORM, IOART_DELEXINT);
424}
425
426int
427ioapic_extpin_gsi(void)
428{
429 return 0;
430}
431
432void
433ioapic_pin_setup(void *addr, int pin, int vec,
434 enum intr_trigger trig, enum intr_polarity pola)
435{
436 /*
437 * Always clear an I/O APIC pin before [re]programming it. This is
438 * particularly important if the pin is set up for a level interrupt
439 * as the IOART_REM_IRR bit might be set. When we reprogram the
440 * vector any EOI from pending ints on this pin could be lost and
441 * IRR might never get reset.
442 *
443 * To fix this problem, clear the vector and make sure it is
444 * programmed as an edge interrupt. This should theoretically
445 * clear IRR so we can later, safely program it as a level
446 * interrupt.
447 */
448 ioapic_pin_prog(addr, pin, vec, INTR_TRIGGER_EDGE, INTR_POLARITY_HIGH,
449 IOART_DELFIXED);
450 ioapic_pin_prog(addr, pin, vec, trig, pola, IOART_DELFIXED);
451}
452
453static void
454ioapic_pin_prog(void *addr, int pin, int vec,
455 enum intr_trigger trig, enum intr_polarity pola, uint32_t del_mode)
456{
457 uint32_t flags, target;
458 int select;
459
460 KKASSERT(del_mode == IOART_DELEXINT || del_mode == IOART_DELFIXED);
461
462 select = IOAPIC_REDTBL0 + (2 * pin);
463
464 flags = ioapic_read(addr, select) & IOART_RESV;
465 flags |= IOART_INTMSET | IOART_DESTPHY;
466#ifdef foo
467 flags |= del_mode;
468#else
469 /*
470 * We only support limited I/O APIC mixed mode,
471 * so even for ExtINT, we still use "fixed"
472 * delivery mode.
473 */
474 flags |= IOART_DELFIXED;
475#endif
476
477 if (del_mode == IOART_DELEXINT) {
478 KKASSERT(trig == INTR_TRIGGER_CONFORM &&
479 pola == INTR_POLARITY_CONFORM);
480 flags |= IOART_TRGREDG | IOART_INTAHI;
481 } else {
482 switch (trig) {
483 case INTR_TRIGGER_EDGE:
484 flags |= IOART_TRGREDG;
485 break;
486
487 case INTR_TRIGGER_LEVEL:
488 flags |= IOART_TRGRLVL;
489 break;
490
491 case INTR_TRIGGER_CONFORM:
492 panic("ioapic_pin_prog: trig conform is not "
493 "supported\n");
494 }
495 switch (pola) {
496 case INTR_POLARITY_HIGH:
497 flags |= IOART_INTAHI;
498 break;
499
500 case INTR_POLARITY_LOW:
501 flags |= IOART_INTALO;
502 break;
503
504 case INTR_POLARITY_CONFORM:
505 panic("ioapic_pin_prog: pola conform is not "
506 "supported\n");
507 }
508 }
509
510 target = ioapic_read(addr, select + 1) & IOART_HI_DEST_RESV;
511 target |= (CPU_TO_ID(0) << IOART_HI_DEST_SHIFT) &
512 IOART_HI_DEST_MASK;
513
514 ioapic_write(addr, select, flags | vec);
515 ioapic_write(addr, select + 1, target);
516}
517
518static void
519ioapic_setup(const struct ioapic_info *info)
520{
521 int i;
522
523 ioapic_set_apic_id(info);
524
525 for (i = 0; i < info->io_npin; ++i)
526 ioapic_gsi_setup(info->io_gsi_base + i);
527}
528
529static int
530ioapic_alloc_apic_id(int start)
531{
532 for (;;) {
533 const struct ioapic_info *info;
534 int apic_id, apic_id16;
535
536 apic_id = lapic_unused_apic_id(start);
537 if (apic_id == NAPICID) {
538 kprintf("IOAPIC: can't find unused APIC ID\n");
539 return apic_id;
540 }
541 apic_id16 = apic_id & IOAPIC_ID_MASK;
542
543 /*
544 * Check against other I/O APIC's APIC ID's lower 4bits.
545 *
546 * The new APIC ID will have to be different from others
547 * in the lower 4bits, no matter whether xAPIC is used
548 * or not.
549 */
550 TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) {
551 if (info->io_apic_id == -1) {
552 info = NULL;
553 break;
554 }
555 if ((info->io_apic_id & IOAPIC_ID_MASK) == apic_id16)
556 break;
557 }
558 if (info == NULL)
559 return apic_id;
560
561 kprintf("IOAPIC: APIC ID %d has same lower 4bits as "
562 "%dth I/O APIC, keep searching...\n",
563 apic_id, info->io_idx);
564
565 start = apic_id + 1;
566 }
567 panic("ioapic_unused_apic_id: never reached\n");
568}