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