1/* $NetBSD: ioapic.c,v 1.52 2015/07/27 15:45:20 msaitoh Exp $ */
2
3/*-
4 * Copyright (c) 2000, 2009 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by RedBack Networks Inc, and by Andrew Doran.
9 *
10 * Author: Bill Sommerfeld
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 * notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 * notice, this list of conditions and the following disclaimer in the
19 * documentation and/or other materials provided with the distribution.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
23 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
25 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 * POSSIBILITY OF SUCH DAMAGE.
32 */
33
34/*
35 * Copyright (c) 1999 Stefan Grefen
36 *
37 * Redistribution and use in source and binary forms, with or without
38 * modification, are permitted provided that the following conditions
39 * are met:
40 * 1. Redistributions of source code must retain the above copyright
41 * notice, this list of conditions and the following disclaimer.
42 * 2. Redistributions in binary form must reproduce the above copyright
43 * notice, this list of conditions and the following disclaimer in the
44 * documentation and/or other materials provided with the distribution.
45 * 3. All advertising materials mentioning features or use of this software
46 * must display the following acknowledgement:
47 * This product includes software developed by the NetBSD
48 * Foundation, Inc. and its contributors.
49 * 4. Neither the name of The NetBSD Foundation nor the names of its
50 * contributors may be used to endorse or promote products derived
51 * from this software without specific prior written permission.
52 *
53 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
54 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
55 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
56 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR AND CONTRIBUTORS BE LIABLE
57 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
58 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
59 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
60 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
61 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
62 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
63 * SUCH DAMAGE.
64 */
65
66#include <sys/cdefs.h>
67__KERNEL_RCSID(0, "$NetBSD: ioapic.c,v 1.52 2015/07/27 15:45:20 msaitoh Exp $");
68
69#include "opt_ddb.h"
70
71#include <sys/param.h>
72#include <sys/systm.h>
73#include <sys/device.h>
74#include <sys/malloc.h>
75#include <sys/kernel.h>
76#include <sys/bus.h>
77
78#include <uvm/uvm_extern.h>
79
80#include <machine/isa_machdep.h> /* XXX intrhand */
81#include <machine/i82093reg.h>
82#include <machine/i82093var.h>
83#include <machine/i82489reg.h>
84#include <machine/i82489var.h>
85#include <machine/i8259.h>
86#include <machine/mpbiosvar.h>
87#include <machine/pio.h>
88#include <machine/pmap.h>
89#include <machine/lock.h>
90
91#include "acpica.h"
92#include "opt_mpbios.h"
93#include "opt_acpi.h"
94
95#if !defined(MPBIOS) && NACPICA == 0
96#error "ioapic needs at least one of the MPBIOS or ACPI options"
97#endif
98
99/*
100 * XXX locking
101 */
102
103int ioapic_match(device_t, cfdata_t, void *);
104void ioapic_attach(device_t, device_t, void *);
105
106extern int x86_mem_add_mapping(bus_addr_t, bus_size_t,
107 int, bus_space_handle_t *); /* XXX XXX */
108
109void ioapic_hwmask(struct pic *, int);
110void ioapic_hwunmask(struct pic *, int);
111bool ioapic_trymask(struct pic *, int);
112static void ioapic_addroute(struct pic *, struct cpu_info *, int, int, int);
113static void ioapic_delroute(struct pic *, struct cpu_info *, int, int, int);
114
115int apic_verbose = 0;
116
117struct ioapic_softc *ioapics; /* head of linked list */
118int nioapics = 0; /* number attached */
119static int ioapic_vecbase;
120
121static inline u_long
122ioapic_lock(struct ioapic_softc *sc)
123{
124 u_long flags;
125
126 flags = x86_read_psl();
127 x86_disable_intr();
128 __cpu_simple_lock(&sc->sc_pic.pic_lock);
129 return flags;
130}
131
132static inline void
133ioapic_unlock(struct ioapic_softc *sc, u_long flags)
134{
135 __cpu_simple_unlock(&sc->sc_pic.pic_lock);
136 x86_write_psl(flags);
137}
138
139#ifndef _IOAPIC_CUSTOM_RW
140/*
141 * Register read/write routines.
142 */
143static inline uint32_t
144ioapic_read_ul(struct ioapic_softc *sc,int regid)
145{
146 uint32_t val;
147
148 *(sc->sc_reg) = regid;
149 val = *sc->sc_data;
150
151 return val;
152}
153
154static inline void
155ioapic_write_ul(struct ioapic_softc *sc,int regid, uint32_t val)
156{
157 *(sc->sc_reg) = regid;
158 *(sc->sc_data) = val;
159}
160#endif /* !_IOAPIC_CUSTOM_RW */
161
162static inline uint32_t
163ioapic_read(struct ioapic_softc *sc, int regid)
164{
165 uint32_t val;
166 u_long flags;
167
168 flags = ioapic_lock(sc);
169 val = ioapic_read_ul(sc, regid);
170 ioapic_unlock(sc, flags);
171 return val;
172}
173
174static inline void
175ioapic_write(struct ioapic_softc *sc,int regid, int val)
176{
177 u_long flags;
178
179 flags = ioapic_lock(sc);
180 ioapic_write_ul(sc, regid, val);
181 ioapic_unlock(sc, flags);
182}
183
184struct ioapic_softc *
185ioapic_find(int apicid)
186{
187 struct ioapic_softc *sc;
188
189 if (apicid == MPS_ALL_APICS) { /* XXX mpbios-specific */
190 /*
191 * XXX kludge for all-ioapics interrupt support
192 * on single ioapic systems
193 */
194 if (nioapics <= 1)
195 return ioapics;
196 panic("unsupported: all-ioapics interrupt with >1 ioapic");
197 }
198
199 for (sc = ioapics; sc != NULL; sc = sc->sc_next)
200 if (sc->sc_pic.pic_apicid == apicid)
201 return sc;
202
203 return NULL;
204}
205
206/*
207 * For the case the I/O APICs were configured using ACPI, there must
208 * be an option to match global ACPI interrupts with APICs.
209 */
210struct ioapic_softc *
211ioapic_find_bybase(int vec)
212{
213 struct ioapic_softc *sc;
214
215 for (sc = ioapics; sc != NULL; sc = sc->sc_next) {
216 if (vec >= sc->sc_pic.pic_vecbase &&
217 vec < (sc->sc_pic.pic_vecbase + sc->sc_apic_sz))
218 return sc;
219 }
220
221 return NULL;
222}
223
224static inline void
225ioapic_add(struct ioapic_softc *sc)
226{
227 struct ioapic_softc **scp;
228
229 sc->sc_next = NULL;
230
231 for (scp = &ioapics; *scp != NULL; scp = &(*scp)->sc_next)
232 ;
233 *scp = sc;
234 nioapics++;
235}
236
237void
238ioapic_print_redir (struct ioapic_softc *sc, const char *why, int pin)
239{
240 uint32_t redirlo = ioapic_read(sc, IOAPIC_REDLO(pin));
241 uint32_t redirhi = ioapic_read(sc, IOAPIC_REDHI(pin));
242
243 apic_format_redir(device_xname(sc->sc_dev), why, pin, redirhi,
244 redirlo);
245}
246
247CFATTACH_DECL_NEW(ioapic, sizeof(struct ioapic_softc),
248 ioapic_match, ioapic_attach, NULL, NULL);
249
250int
251ioapic_match(device_t parent, cfdata_t match, void *aux)
252{
253
254 return 1;
255}
256
257/*
258 * can't use bus_space_xxx as we don't have a bus handle ...
259 */
260void
261ioapic_attach(device_t parent, device_t self, void *aux)
262{
263 struct ioapic_softc *sc = device_private(self);
264 struct apic_attach_args *aaa = (struct apic_attach_args *)aux;
265 int apic_id;
266 uint32_t ver_sz;
267 int i;
268
269 sc->sc_dev = self;
270 sc->sc_flags = aaa->flags;
271 sc->sc_pic.pic_apicid = aaa->apic_id;
272 sc->sc_pic.pic_name = device_xname(self);
273 sc->sc_pic.pic_ioapic = sc;
274
275 aprint_naive("\n");
276
277 if (ioapic_find(aaa->apic_id) != NULL) {
278 aprint_error(": duplicate apic id (ignored)\n");
279 return;
280 }
281
282 aprint_verbose(": pa 0x%jx", (uintmax_t)aaa->apic_address);
283#ifndef _IOAPIC_CUSTOM_RW
284 {
285 bus_space_handle_t bh;
286
287 if (x86_mem_add_mapping(aaa->apic_address, PAGE_SIZE, 0, &bh) != 0) {
288 aprint_error(": map failed\n");
289 return;
290 }
291 sc->sc_reg = (volatile uint32_t *)(bh + IOAPIC_REG);
292 sc->sc_data = (volatile uint32_t *)(bh + IOAPIC_DATA);
293 }
294#endif
295 sc->sc_pa = aaa->apic_address;
296
297 sc->sc_pic.pic_type = PIC_IOAPIC;
298 __cpu_simple_lock_init(&sc->sc_pic.pic_lock);
299 sc->sc_pic.pic_hwmask = ioapic_hwmask;
300 sc->sc_pic.pic_hwunmask = ioapic_hwunmask;
301 sc->sc_pic.pic_addroute = ioapic_addroute;
302 sc->sc_pic.pic_delroute = ioapic_delroute;
303 sc->sc_pic.pic_trymask = ioapic_trymask;
304 sc->sc_pic.pic_edge_stubs = ioapic_edge_stubs;
305 sc->sc_pic.pic_level_stubs = ioapic_level_stubs;
306
307 apic_id = (ioapic_read(sc, IOAPIC_ID) & IOAPIC_ID_MASK)
308 >> IOAPIC_ID_SHIFT;
309 ver_sz = ioapic_read(sc, IOAPIC_VER);
310
311 if (ver_sz == 0xffffffff) {
312 aprint_error(": failed to read version/size\n");
313 goto out;
314 }
315
316 ioapic_add(sc);
317
318 sc->sc_apic_vers = (ver_sz & IOAPIC_VER_MASK) >> IOAPIC_VER_SHIFT;
319 sc->sc_apic_sz = (ver_sz & IOAPIC_MAX_MASK) >> IOAPIC_MAX_SHIFT;
320 sc->sc_apic_sz++;
321
322 if (aaa->apic_vecbase != -1)
323 sc->sc_pic.pic_vecbase = aaa->apic_vecbase;
324 else {
325 /*
326 * XXX this assumes ordering of ioapics in the table.
327 * Only needed for broken BIOS workaround (see mpbios.c)
328 */
329 sc->sc_pic.pic_vecbase = ioapic_vecbase;
330 ioapic_vecbase += sc->sc_apic_sz;
331 }
332
333 if (mp_verbose) {
334 printf(", %s mode",
335 aaa->flags & IOAPIC_PICMODE ? "PIC" : "virtual wire");
336 }
337
338 aprint_verbose(", version 0x%x, %d pins", sc->sc_apic_vers,
339 sc->sc_apic_sz);
340 aprint_normal("\n");
341
342 sc->sc_pins = malloc(sizeof(struct ioapic_pin) * sc->sc_apic_sz,
343 M_DEVBUF, M_WAITOK);
344
345 for (i=0; i<sc->sc_apic_sz; i++) {
346 uint32_t redlo, redhi;
347
348 sc->sc_pins[i].ip_next = NULL;
349 sc->sc_pins[i].ip_map = NULL;
350 sc->sc_pins[i].ip_vector = 0;
351 sc->sc_pins[i].ip_type = IST_NONE;
352
353 /* Mask all pins by default. */
354 redlo = IOAPIC_REDLO_MASK;
355 /*
356 * ISA interrupts are connect to pin 0-15 and
357 * edge triggered on high, which is the default.
358 *
359 * Expect all other interrupts to be PCI-like
360 * level triggered on low.
361 */
362 if (i >= 16)
363 redlo |= IOAPIC_REDLO_LEVEL | IOAPIC_REDLO_ACTLO;
364 redhi = (cpu_info_primary.ci_cpuid << IOAPIC_REDHI_DEST_SHIFT);
365 ioapic_write(sc, IOAPIC_REDHI(i), redhi);
366 ioapic_write(sc, IOAPIC_REDLO(i), redlo);
367 }
368
369 /*
370 * In case the APIC is not initialized to the correct ID
371 * do it now.
372 * Maybe we should record the original ID for interrupt
373 * mapping later ...
374 */
375 if (apic_id != sc->sc_pic.pic_apicid) {
376 aprint_debug_dev(sc->sc_dev, "misconfigured as apic %d\n",
377 apic_id);
378
379 ioapic_write(sc, IOAPIC_ID,
380 (ioapic_read(sc, IOAPIC_ID) & ~IOAPIC_ID_MASK)
381 | (sc->sc_pic.pic_apicid << IOAPIC_ID_SHIFT));
382
383 apic_id = (ioapic_read(sc, IOAPIC_ID) & IOAPIC_ID_MASK)
384 >> IOAPIC_ID_SHIFT;
385
386 if (apic_id != sc->sc_pic.pic_apicid)
387 aprint_error_dev(sc->sc_dev,
388 "can't remap to apid %d\n", sc->sc_pic.pic_apicid);
389 else
390 aprint_debug_dev(sc->sc_dev, "remapped to apic %d\n",
391 sc->sc_pic.pic_apicid);
392 }
393
394 out:
395 if (!pmf_device_register(self, NULL, NULL))
396 aprint_error_dev(self, "couldn't establish power handler\n");
397
398#if 0
399 /* output of this was boring. */
400 if (mp_verbose)
401 for (i=0; i<sc->sc_apic_sz; i++)
402 ioapic_print_redir(sc, "boot", i);
403#endif
404}
405
406static void
407apic_set_redir(struct ioapic_softc *sc, int pin, int idt_vec,
408 struct cpu_info *ci)
409{
410 uint32_t redlo;
411 uint32_t redhi;
412 int delmode;
413 struct ioapic_pin *pp;
414 struct mp_intr_map *map;
415
416 pp = &sc->sc_pins[pin];
417 map = pp->ip_map;
418 redlo = map == NULL ? IOAPIC_REDLO_MASK : map->redir;
419 redhi = 0;
420 delmode = (redlo & IOAPIC_REDLO_DEL_MASK) >> IOAPIC_REDLO_DEL_SHIFT;
421
422 if (delmode == IOAPIC_REDLO_DEL_FIXED ||
423 delmode == IOAPIC_REDLO_DEL_LOPRI) {
424 if (pp->ip_type == IST_NONE) {
425 redlo |= IOAPIC_REDLO_MASK;
426 } else {
427 redhi = (ci->ci_cpuid << IOAPIC_REDHI_DEST_SHIFT);
428 redlo |= (idt_vec & 0xff);
429 redlo |= IOAPIC_REDLO_DEL_FIXED
430 << IOAPIC_REDLO_DEL_SHIFT;
431 redlo &= ~IOAPIC_REDLO_DSTMOD;
432
433 /* XXX derive this bit from BIOS info */
434 if (pp->ip_type == IST_LEVEL)
435 redlo |= IOAPIC_REDLO_LEVEL;
436 else
437 redlo &= ~IOAPIC_REDLO_LEVEL;
438 if ((map != NULL)
439 && ((map->flags & 3) == MPS_INTPO_DEF)) {
440 if (pp->ip_type == IST_LEVEL)
441 redlo |= IOAPIC_REDLO_ACTLO;
442 else
443 redlo &= ~IOAPIC_REDLO_ACTLO;
444 }
445 }
446 }
447 ioapic_write(sc, IOAPIC_REDHI(pin), redhi);
448 ioapic_write(sc, IOAPIC_REDLO(pin), redlo);
449 if (mp_verbose)
450 ioapic_print_redir(sc, "int", pin);
451}
452
453/*
454 * Throw the switch and enable interrupts..
455 */
456
457void
458ioapic_enable(void)
459{
460 if (ioapics == NULL)
461 return;
462
463 i8259_setmask(0xffff);
464
465 if (ioapics->sc_flags & IOAPIC_PICMODE) {
466 aprint_debug_dev(ioapics->sc_dev,
467 "writing to IMCR to disable pics\n");
468 outb(IMCR_ADDR, IMCR_REGISTER);
469 outb(IMCR_DATA, IMCR_APIC);
470 }
471}
472
473void
474ioapic_reenable(void)
475{
476 int p, apic_id;
477 struct ioapic_softc *sc;
478
479 if (ioapics == NULL)
480 return;
481
482 aprint_normal("%s reenabling\n", device_xname(ioapics->sc_dev));
483
484 for (sc = ioapics; sc != NULL; sc = sc->sc_next) {
485 apic_id = (ioapic_read(sc, IOAPIC_ID) & IOAPIC_ID_MASK)
486 >> IOAPIC_ID_SHIFT;
487 if (apic_id != sc->sc_pic.pic_apicid) {
488 ioapic_write(sc, IOAPIC_ID,
489 (ioapic_read(sc, IOAPIC_ID) & ~IOAPIC_ID_MASK)
490 | (sc->sc_pic.pic_apicid << IOAPIC_ID_SHIFT));
491 }
492
493 for (p = 0; p < sc->sc_apic_sz; p++)
494 apic_set_redir(sc, p, sc->sc_pins[p].ip_vector,
495 sc->sc_pins[p].ip_cpu);
496 }
497
498 ioapic_enable();
499}
500
501void
502ioapic_hwmask(struct pic *pic, int pin)
503{
504 uint32_t redlo;
505 struct ioapic_softc *sc = pic->pic_ioapic;
506 u_long flags;
507
508 flags = ioapic_lock(sc);
509 redlo = ioapic_read_ul(sc, IOAPIC_REDLO(pin));
510 redlo |= IOAPIC_REDLO_MASK;
511 ioapic_write_ul(sc, IOAPIC_REDLO(pin), redlo);
512 ioapic_unlock(sc, flags);
513}
514
515bool
516ioapic_trymask(struct pic *pic, int pin)
517{
518 uint32_t redlo;
519 struct ioapic_softc *sc = pic->pic_ioapic;
520 u_long flags;
521 bool rv;
522
523 /* Mask it. */
524 flags = ioapic_lock(sc);
525 redlo = ioapic_read_ul(sc, IOAPIC_REDLO(pin));
526 redlo |= IOAPIC_REDLO_MASK;
527 ioapic_write_ul(sc, IOAPIC_REDLO(pin), redlo);
528
529 /* If pending, unmask and abort. */
530 redlo = ioapic_read_ul(sc, IOAPIC_REDLO(pin));
531 if ((redlo & (IOAPIC_REDLO_RIRR|IOAPIC_REDLO_DELSTS)) != 0) {
532 redlo &= ~IOAPIC_REDLO_MASK;
533 ioapic_write_ul(sc, IOAPIC_REDLO(pin), redlo);
534 rv = false;
535 } else {
536 rv = true;
537 }
538 ioapic_unlock(sc, flags);
539 return rv;
540}
541
542void
543ioapic_hwunmask(struct pic *pic, int pin)
544{
545 uint32_t redlo;
546 struct ioapic_softc *sc = pic->pic_ioapic;
547 u_long flags;
548
549 flags = ioapic_lock(sc);
550 redlo = ioapic_read_ul(sc, IOAPIC_REDLO(pin));
551 redlo &= ~IOAPIC_REDLO_MASK;
552 ioapic_write_ul(sc, IOAPIC_REDLO(pin), redlo);
553 ioapic_unlock(sc, flags);
554}
555
556static void
557ioapic_addroute(struct pic *pic, struct cpu_info *ci, int pin,
558 int idtvec, int type)
559{
560 struct ioapic_softc *sc = pic->pic_ioapic;
561 struct ioapic_pin *pp;
562
563 pp = &sc->sc_pins[pin];
564 pp->ip_type = type;
565 pp->ip_vector = idtvec;
566 pp->ip_cpu = ci;
567 apic_set_redir(sc, pin, idtvec, ci);
568}
569
570static void
571ioapic_delroute(struct pic *pic, struct cpu_info *ci, int pin,
572 int idtvec, int type)
573{
574
575 ioapic_hwmask(pic, pin);
576}
577
578#ifdef DDB
579void ioapic_dump(void);
580void ioapic_dump_raw(void);
581
582void
583ioapic_dump(void)
584{
585 struct ioapic_softc *sc;
586 struct ioapic_pin *ip;
587 int p;
588
589 for (sc = ioapics; sc != NULL; sc = sc->sc_next) {
590 for (p = 0; p < sc->sc_apic_sz; p++) {
591 ip = &sc->sc_pins[p];
592 if (ip->ip_type != IST_NONE)
593 ioapic_print_redir(sc, "dump", p);
594 }
595 }
596}
597
598void
599ioapic_dump_raw(void)
600{
601 struct ioapic_softc *sc;
602 int i;
603 uint32_t reg;
604
605 for (sc = ioapics; sc != NULL; sc = sc->sc_next) {
606 printf("Register dump of %s\n", device_xname(sc->sc_dev));
607 i = 0;
608 do {
609 if (i % 0x08 == 0)
610 printf("%02x", i);
611 reg = ioapic_read(sc, i);
612 printf(" %08x", (u_int)reg);
613 if (++i % 0x08 == 0)
614 printf("\n");
615 } while (i < 0x40);
616 }
617}
618#endif
619