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