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