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