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