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