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