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