io_apic.c revision 187880
1/*-
2 * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
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. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the names of any co-contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD: head/sys/i386/i386/io_apic.c 187880 2009-01-29 09:22:56Z jeff $");
32
33#include "opt_isa.h"
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/bus.h>
38#include <sys/kernel.h>
39#include <sys/lock.h>
40#include <sys/malloc.h>
41#include <sys/module.h>
42#include <sys/mutex.h>
43#include <sys/sysctl.h>
44
45#include <dev/pci/pcireg.h>
46#include <dev/pci/pcivar.h>
47
48#include <vm/vm.h>
49#include <vm/pmap.h>
50
51#include <machine/apicreg.h>
52#include <machine/frame.h>
53#include <machine/intr_machdep.h>
54#include <machine/apicvar.h>
55#include <machine/resource.h>
56#include <machine/segments.h>
57
58#define IOAPIC_ISA_INTS		16
59#define	IOAPIC_MEM_REGION	32
60#define	IOAPIC_REDTBL_LO(i)	(IOAPIC_REDTBL + (i) * 2)
61#define	IOAPIC_REDTBL_HI(i)	(IOAPIC_REDTBL_LO(i) + 1)
62
63#define	IRQ_EXTINT		(NUM_IO_INTS + 1)
64#define	IRQ_NMI			(NUM_IO_INTS + 2)
65#define	IRQ_SMI			(NUM_IO_INTS + 3)
66#define	IRQ_DISABLED		(NUM_IO_INTS + 4)
67
68static MALLOC_DEFINE(M_IOAPIC, "io_apic", "I/O APIC structures");
69
70/*
71 * I/O APIC interrupt source driver.  Each pin is assigned an IRQ cookie
72 * as laid out in the ACPI System Interrupt number model where each I/O
73 * APIC has a contiguous chunk of the System Interrupt address space.
74 * We assume that IRQs 1 - 15 behave like ISA IRQs and that all other
75 * IRQs behave as PCI IRQs by default.  We also assume that the pin for
76 * IRQ 0 is actually an ExtINT pin.  The apic enumerators override the
77 * configuration of individual pins as indicated by their tables.
78 *
79 * Documentation for the I/O APIC: "82093AA I/O Advanced Programmable
80 * Interrupt Controller (IOAPIC)", May 1996, Intel Corp.
81 * ftp://download.intel.com/design/chipsets/datashts/29056601.pdf
82 */
83
84struct ioapic_intsrc {
85	struct intsrc io_intsrc;
86	u_int io_irq;
87	u_int io_intpin:8;
88	u_int io_vector:8;
89	u_int io_cpu:8;
90	u_int io_activehi:1;
91	u_int io_edgetrigger:1;
92	u_int io_masked:1;
93	int io_bus:4;
94	uint32_t io_lowreg;
95};
96
97struct ioapic {
98	struct pic io_pic;
99	u_int io_id:8;			/* logical ID */
100	u_int io_apic_id:4;
101	u_int io_intbase:8;		/* System Interrupt base */
102	u_int io_numintr:8;
103	volatile ioapic_t *io_addr;	/* XXX: should use bus_space */
104	vm_paddr_t io_paddr;
105	STAILQ_ENTRY(ioapic) io_next;
106	struct ioapic_intsrc io_pins[0];
107};
108
109static u_int	ioapic_read(volatile ioapic_t *apic, int reg);
110static void	ioapic_write(volatile ioapic_t *apic, int reg, u_int val);
111static const char *ioapic_bus_string(int bus_type);
112static void	ioapic_print_irq(struct ioapic_intsrc *intpin);
113static void	ioapic_enable_source(struct intsrc *isrc);
114static void	ioapic_disable_source(struct intsrc *isrc, int eoi);
115static void	ioapic_eoi_source(struct intsrc *isrc);
116static void	ioapic_enable_intr(struct intsrc *isrc);
117static void	ioapic_disable_intr(struct intsrc *isrc);
118static int	ioapic_vector(struct intsrc *isrc);
119static int	ioapic_source_pending(struct intsrc *isrc);
120static int	ioapic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
121		    enum intr_polarity pol);
122static void	ioapic_resume(struct pic *pic);
123static void	ioapic_assign_cpu(struct intsrc *isrc, u_int apic_id);
124static void	ioapic_program_intpin(struct ioapic_intsrc *intpin);
125
126static STAILQ_HEAD(,ioapic) ioapic_list = STAILQ_HEAD_INITIALIZER(ioapic_list);
127struct pic ioapic_template = { ioapic_enable_source, ioapic_disable_source,
128			       ioapic_eoi_source, ioapic_enable_intr,
129			       ioapic_disable_intr, ioapic_vector,
130			       ioapic_source_pending, NULL, ioapic_resume,
131			       ioapic_config_intr, ioapic_assign_cpu };
132
133static int next_ioapic_base;
134static u_int next_id;
135
136SYSCTL_NODE(_hw, OID_AUTO, apic, CTLFLAG_RD, 0, "APIC options");
137static int enable_extint;
138SYSCTL_INT(_hw_apic, OID_AUTO, enable_extint, CTLFLAG_RDTUN, &enable_extint, 0,
139    "Enable the ExtINT pin in the first I/O APIC");
140TUNABLE_INT("hw.apic.enable_extint", &enable_extint);
141
142static __inline void
143_ioapic_eoi_source(struct intsrc *isrc)
144{
145	lapic_eoi();
146}
147
148static u_int
149ioapic_read(volatile ioapic_t *apic, int reg)
150{
151
152	mtx_assert(&icu_lock, MA_OWNED);
153	apic->ioregsel = reg;
154	return (apic->iowin);
155}
156
157static void
158ioapic_write(volatile ioapic_t *apic, int reg, u_int val)
159{
160
161	mtx_assert(&icu_lock, MA_OWNED);
162	apic->ioregsel = reg;
163	apic->iowin = val;
164}
165
166static const char *
167ioapic_bus_string(int bus_type)
168{
169
170	switch (bus_type) {
171	case APIC_BUS_ISA:
172		return ("ISA");
173	case APIC_BUS_EISA:
174		return ("EISA");
175	case APIC_BUS_PCI:
176		return ("PCI");
177	default:
178		return ("unknown");
179	}
180}
181
182static void
183ioapic_print_irq(struct ioapic_intsrc *intpin)
184{
185
186	switch (intpin->io_irq) {
187	case IRQ_DISABLED:
188		printf("disabled");
189		break;
190	case IRQ_EXTINT:
191		printf("ExtINT");
192		break;
193	case IRQ_NMI:
194		printf("NMI");
195		break;
196	case IRQ_SMI:
197		printf("SMI");
198		break;
199	default:
200		printf("%s IRQ %u", ioapic_bus_string(intpin->io_bus),
201		    intpin->io_irq);
202	}
203}
204
205static void
206ioapic_enable_source(struct intsrc *isrc)
207{
208	struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
209	struct ioapic *io = (struct ioapic *)isrc->is_pic;
210	uint32_t flags;
211
212	mtx_lock_spin(&icu_lock);
213	if (intpin->io_masked) {
214		flags = intpin->io_lowreg & ~IOART_INTMASK;
215		ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin),
216		    flags);
217		intpin->io_masked = 0;
218	}
219	mtx_unlock_spin(&icu_lock);
220}
221
222static void
223ioapic_disable_source(struct intsrc *isrc, int eoi)
224{
225	struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
226	struct ioapic *io = (struct ioapic *)isrc->is_pic;
227	uint32_t flags;
228
229	mtx_lock_spin(&icu_lock);
230	if (!intpin->io_masked && !intpin->io_edgetrigger) {
231		flags = intpin->io_lowreg | IOART_INTMSET;
232		ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin),
233		    flags);
234		intpin->io_masked = 1;
235	}
236
237	if (eoi == PIC_EOI)
238		_ioapic_eoi_source(isrc);
239
240	mtx_unlock_spin(&icu_lock);
241}
242
243static void
244ioapic_eoi_source(struct intsrc *isrc)
245{
246
247	_ioapic_eoi_source(isrc);
248}
249
250/*
251 * Completely program an intpin based on the data in its interrupt source
252 * structure.
253 */
254static void
255ioapic_program_intpin(struct ioapic_intsrc *intpin)
256{
257	struct ioapic *io = (struct ioapic *)intpin->io_intsrc.is_pic;
258	uint32_t low, high, value;
259
260	/*
261	 * If a pin is completely invalid or if it is valid but hasn't
262	 * been enabled yet, just ensure that the pin is masked.
263	 */
264	if (intpin->io_irq == IRQ_DISABLED || (intpin->io_irq < NUM_IO_INTS &&
265	    intpin->io_vector == 0)) {
266		mtx_lock_spin(&icu_lock);
267		low = ioapic_read(io->io_addr,
268		    IOAPIC_REDTBL_LO(intpin->io_intpin));
269		if ((low & IOART_INTMASK) == IOART_INTMCLR)
270			ioapic_write(io->io_addr,
271			    IOAPIC_REDTBL_LO(intpin->io_intpin),
272			    low | IOART_INTMSET);
273		mtx_unlock_spin(&icu_lock);
274		return;
275	}
276
277	/* Set the destination. */
278	low = IOART_DESTPHY;
279	high = intpin->io_cpu << APIC_ID_SHIFT;
280
281	/* Program the rest of the low word. */
282	if (intpin->io_edgetrigger)
283		low |= IOART_TRGREDG;
284	else
285		low |= IOART_TRGRLVL;
286	if (intpin->io_activehi)
287		low |= IOART_INTAHI;
288	else
289		low |= IOART_INTALO;
290	if (intpin->io_masked)
291		low |= IOART_INTMSET;
292	switch (intpin->io_irq) {
293	case IRQ_EXTINT:
294		KASSERT(intpin->io_edgetrigger,
295		    ("ExtINT not edge triggered"));
296		low |= IOART_DELEXINT;
297		break;
298	case IRQ_NMI:
299		KASSERT(intpin->io_edgetrigger,
300		    ("NMI not edge triggered"));
301		low |= IOART_DELNMI;
302		break;
303	case IRQ_SMI:
304		KASSERT(intpin->io_edgetrigger,
305		    ("SMI not edge triggered"));
306		low |= IOART_DELSMI;
307		break;
308	default:
309		KASSERT(intpin->io_vector != 0, ("No vector for IRQ %u",
310		    intpin->io_irq));
311		low |= IOART_DELFIXED | intpin->io_vector;
312	}
313
314	/* Write the values to the APIC. */
315	mtx_lock_spin(&icu_lock);
316	intpin->io_lowreg = low;
317	ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin), low);
318	value = ioapic_read(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin));
319	value &= ~IOART_DEST;
320	value |= high;
321	ioapic_write(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin), value);
322	mtx_unlock_spin(&icu_lock);
323}
324
325static void
326ioapic_assign_cpu(struct intsrc *isrc, u_int apic_id)
327{
328	struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
329	struct ioapic *io = (struct ioapic *)isrc->is_pic;
330	u_int old_vector;
331	u_int old_id;
332
333	/*
334	 * keep 1st core as the destination for NMI
335	 */
336	if (intpin->io_irq == IRQ_NMI)
337		apic_id = 0;
338
339	/*
340	 * Set us up to free the old irq.
341	 */
342	old_vector = intpin->io_vector;
343	old_id = intpin->io_cpu;
344	if (old_vector && apic_id == old_id)
345		return;
346
347	/*
348	 * Allocate an APIC vector for this interrupt pin.  Once
349	 * we have a vector we program the interrupt pin.
350	 */
351	intpin->io_cpu = apic_id;
352	intpin->io_vector = apic_alloc_vector(apic_id, intpin->io_irq);
353	if (bootverbose) {
354		printf("ioapic%u: routing intpin %u (", io->io_id,
355		    intpin->io_intpin);
356		ioapic_print_irq(intpin);
357		printf(") to lapic %u vector %u\n", intpin->io_cpu,
358		    intpin->io_vector);
359	}
360	ioapic_program_intpin(intpin);
361	/*
362	 * Free the old vector after the new one is established.  This is done
363	 * to prevent races where we could miss an interrupt.
364	 */
365	if (old_vector)
366		apic_free_vector(old_id, old_vector, intpin->io_irq);
367}
368
369static void
370ioapic_enable_intr(struct intsrc *isrc)
371{
372	struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
373
374	if (intpin->io_vector == 0)
375		ioapic_assign_cpu(isrc, pcpu_find(0)->pc_apic_id);
376	apic_enable_vector(intpin->io_cpu, intpin->io_vector);
377}
378
379
380static void
381ioapic_disable_intr(struct intsrc *isrc)
382{
383	struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
384	u_int vector;
385
386	if (intpin->io_vector != 0) {
387		/* Mask this interrupt pin and free its APIC vector. */
388		vector = intpin->io_vector;
389		apic_disable_vector(intpin->io_cpu, vector);
390		intpin->io_masked = 1;
391		intpin->io_vector = 0;
392		ioapic_program_intpin(intpin);
393		apic_free_vector(intpin->io_cpu, vector, intpin->io_irq);
394	}
395}
396
397static int
398ioapic_vector(struct intsrc *isrc)
399{
400	struct ioapic_intsrc *pin;
401
402	pin = (struct ioapic_intsrc *)isrc;
403	return (pin->io_irq);
404}
405
406static int
407ioapic_source_pending(struct intsrc *isrc)
408{
409	struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
410
411	if (intpin->io_vector == 0)
412		return 0;
413	return (lapic_intr_pending(intpin->io_vector));
414}
415
416static int
417ioapic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
418    enum intr_polarity pol)
419{
420	struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
421	struct ioapic *io = (struct ioapic *)isrc->is_pic;
422	int changed;
423
424	KASSERT(!(trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM),
425	    ("%s: Conforming trigger or polarity\n", __func__));
426
427	/*
428	 * EISA interrupts always use active high polarity, so don't allow
429	 * them to be set to active low.
430	 *
431	 * XXX: Should we write to the ELCR if the trigger mode changes for
432	 * an EISA IRQ or an ISA IRQ with the ELCR present?
433	 */
434	if (intpin->io_bus == APIC_BUS_EISA)
435		pol = INTR_POLARITY_HIGH;
436	changed = 0;
437	if (intpin->io_edgetrigger != (trig == INTR_TRIGGER_EDGE)) {
438		if (bootverbose)
439			printf("ioapic%u: Changing trigger for pin %u to %s\n",
440			    io->io_id, intpin->io_intpin,
441			    trig == INTR_TRIGGER_EDGE ? "edge" : "level");
442		intpin->io_edgetrigger = (trig == INTR_TRIGGER_EDGE);
443		changed++;
444	}
445	if (intpin->io_activehi != (pol == INTR_POLARITY_HIGH)) {
446		if (bootverbose)
447			printf("ioapic%u: Changing polarity for pin %u to %s\n",
448			    io->io_id, intpin->io_intpin,
449			    pol == INTR_POLARITY_HIGH ? "high" : "low");
450		intpin->io_activehi = (pol == INTR_POLARITY_HIGH);
451		changed++;
452	}
453	if (changed)
454		ioapic_program_intpin(intpin);
455	return (0);
456}
457
458static void
459ioapic_resume(struct pic *pic)
460{
461	struct ioapic *io = (struct ioapic *)pic;
462	int i;
463
464	for (i = 0; i < io->io_numintr; i++)
465		ioapic_program_intpin(&io->io_pins[i]);
466}
467
468/*
469 * Create a plain I/O APIC object.
470 */
471void *
472ioapic_create(vm_paddr_t addr, int32_t apic_id, int intbase)
473{
474	struct ioapic *io;
475	struct ioapic_intsrc *intpin;
476	volatile ioapic_t *apic;
477	u_int numintr, i;
478	uint32_t value;
479
480	/* Map the register window so we can access the device. */
481	apic = pmap_mapdev(addr, IOAPIC_MEM_REGION);
482	mtx_lock_spin(&icu_lock);
483	value = ioapic_read(apic, IOAPIC_VER);
484	mtx_unlock_spin(&icu_lock);
485
486	/* If it's version register doesn't seem to work, punt. */
487	if (value == 0xffffffff) {
488		pmap_unmapdev((vm_offset_t)apic, IOAPIC_MEM_REGION);
489		return (NULL);
490	}
491
492	/* Determine the number of vectors and set the APIC ID. */
493	numintr = ((value & IOART_VER_MAXREDIR) >> MAXREDIRSHIFT) + 1;
494	io = malloc(sizeof(struct ioapic) +
495	    numintr * sizeof(struct ioapic_intsrc), M_IOAPIC, M_WAITOK);
496	io->io_pic = ioapic_template;
497	mtx_lock_spin(&icu_lock);
498	io->io_id = next_id++;
499	io->io_apic_id = ioapic_read(apic, IOAPIC_ID) >> APIC_ID_SHIFT;
500	if (apic_id != -1 && io->io_apic_id != apic_id) {
501		ioapic_write(apic, IOAPIC_ID, apic_id << APIC_ID_SHIFT);
502		mtx_unlock_spin(&icu_lock);
503		io->io_apic_id = apic_id;
504		printf("ioapic%u: Changing APIC ID to %d\n", io->io_id,
505		    apic_id);
506	} else
507		mtx_unlock_spin(&icu_lock);
508	if (intbase == -1) {
509		intbase = next_ioapic_base;
510		printf("ioapic%u: Assuming intbase of %d\n", io->io_id,
511		    intbase);
512	} else if (intbase != next_ioapic_base && bootverbose)
513		printf("ioapic%u: WARNING: intbase %d != expected base %d\n",
514		    io->io_id, intbase, next_ioapic_base);
515	io->io_intbase = intbase;
516	next_ioapic_base = intbase + numintr;
517	io->io_numintr = numintr;
518	io->io_addr = apic;
519	io->io_paddr = addr;
520
521	/*
522	 * Initialize pins.  Start off with interrupts disabled.  Default
523	 * to active-hi and edge-triggered for ISA interrupts and active-lo
524	 * and level-triggered for all others.
525	 */
526	bzero(io->io_pins, sizeof(struct ioapic_intsrc) * numintr);
527	mtx_lock_spin(&icu_lock);
528	for (i = 0, intpin = io->io_pins; i < numintr; i++, intpin++) {
529		intpin->io_intsrc.is_pic = (struct pic *)io;
530		intpin->io_intpin = i;
531		intpin->io_irq = intbase + i;
532
533		/*
534		 * Assume that pin 0 on the first I/O APIC is an ExtINT pin.
535		 * Assume that pins 1-15 are ISA interrupts and that all
536		 * other pins are PCI interrupts.
537		 */
538		if (intpin->io_irq == 0)
539			ioapic_set_extint(io, i);
540		else if (intpin->io_irq < IOAPIC_ISA_INTS) {
541			intpin->io_bus = APIC_BUS_ISA;
542			intpin->io_activehi = 1;
543			intpin->io_edgetrigger = 1;
544			intpin->io_masked = 1;
545		} else {
546			intpin->io_bus = APIC_BUS_PCI;
547			intpin->io_activehi = 0;
548			intpin->io_edgetrigger = 0;
549			intpin->io_masked = 1;
550		}
551
552		/*
553		 * Route interrupts to the BSP by default.  Interrupts may
554		 * be routed to other CPUs later after they are enabled.
555		 */
556		intpin->io_cpu = PCPU_GET(apic_id);
557		value = ioapic_read(apic, IOAPIC_REDTBL_LO(i));
558		ioapic_write(apic, IOAPIC_REDTBL_LO(i), value | IOART_INTMSET);
559	}
560	mtx_unlock_spin(&icu_lock);
561
562	return (io);
563}
564
565int
566ioapic_get_vector(void *cookie, u_int pin)
567{
568	struct ioapic *io;
569
570	io = (struct ioapic *)cookie;
571	if (pin >= io->io_numintr)
572		return (-1);
573	return (io->io_pins[pin].io_irq);
574}
575
576int
577ioapic_disable_pin(void *cookie, u_int pin)
578{
579	struct ioapic *io;
580
581	io = (struct ioapic *)cookie;
582	if (pin >= io->io_numintr)
583		return (EINVAL);
584	if (io->io_pins[pin].io_irq == IRQ_DISABLED)
585		return (EINVAL);
586	io->io_pins[pin].io_irq = IRQ_DISABLED;
587	if (bootverbose)
588		printf("ioapic%u: intpin %d disabled\n", io->io_id, pin);
589	return (0);
590}
591
592int
593ioapic_remap_vector(void *cookie, u_int pin, int vector)
594{
595	struct ioapic *io;
596
597	io = (struct ioapic *)cookie;
598	if (pin >= io->io_numintr || vector < 0)
599		return (EINVAL);
600	if (io->io_pins[pin].io_irq >= NUM_IO_INTS)
601		return (EINVAL);
602	io->io_pins[pin].io_irq = vector;
603	if (bootverbose)
604		printf("ioapic%u: Routing IRQ %d -> intpin %d\n", io->io_id,
605		    vector, pin);
606	return (0);
607}
608
609int
610ioapic_set_bus(void *cookie, u_int pin, int bus_type)
611{
612	struct ioapic *io;
613
614	if (bus_type < 0 || bus_type > APIC_BUS_MAX)
615		return (EINVAL);
616	io = (struct ioapic *)cookie;
617	if (pin >= io->io_numintr)
618		return (EINVAL);
619	if (io->io_pins[pin].io_irq >= NUM_IO_INTS)
620		return (EINVAL);
621	if (io->io_pins[pin].io_bus == bus_type)
622		return (0);
623	io->io_pins[pin].io_bus = bus_type;
624	if (bootverbose)
625		printf("ioapic%u: intpin %d bus %s\n", io->io_id, pin,
626		    ioapic_bus_string(bus_type));
627	return (0);
628}
629
630int
631ioapic_set_nmi(void *cookie, u_int pin)
632{
633	struct ioapic *io;
634
635	io = (struct ioapic *)cookie;
636	if (pin >= io->io_numintr)
637		return (EINVAL);
638	if (io->io_pins[pin].io_irq == IRQ_NMI)
639		return (0);
640	if (io->io_pins[pin].io_irq >= NUM_IO_INTS)
641		return (EINVAL);
642	io->io_pins[pin].io_bus = APIC_BUS_UNKNOWN;
643	io->io_pins[pin].io_irq = IRQ_NMI;
644	io->io_pins[pin].io_masked = 0;
645	io->io_pins[pin].io_edgetrigger = 1;
646	io->io_pins[pin].io_activehi = 1;
647	if (bootverbose)
648		printf("ioapic%u: Routing NMI -> intpin %d\n",
649		    io->io_id, pin);
650	return (0);
651}
652
653int
654ioapic_set_smi(void *cookie, u_int pin)
655{
656	struct ioapic *io;
657
658	io = (struct ioapic *)cookie;
659	if (pin >= io->io_numintr)
660		return (EINVAL);
661	if (io->io_pins[pin].io_irq == IRQ_SMI)
662		return (0);
663	if (io->io_pins[pin].io_irq >= NUM_IO_INTS)
664		return (EINVAL);
665	io->io_pins[pin].io_bus = APIC_BUS_UNKNOWN;
666	io->io_pins[pin].io_irq = IRQ_SMI;
667	io->io_pins[pin].io_masked = 0;
668	io->io_pins[pin].io_edgetrigger = 1;
669	io->io_pins[pin].io_activehi = 1;
670	if (bootverbose)
671		printf("ioapic%u: Routing SMI -> intpin %d\n",
672		    io->io_id, pin);
673	return (0);
674}
675
676int
677ioapic_set_extint(void *cookie, u_int pin)
678{
679	struct ioapic *io;
680
681	io = (struct ioapic *)cookie;
682	if (pin >= io->io_numintr)
683		return (EINVAL);
684	if (io->io_pins[pin].io_irq == IRQ_EXTINT)
685		return (0);
686	if (io->io_pins[pin].io_irq >= NUM_IO_INTS)
687		return (EINVAL);
688	io->io_pins[pin].io_bus = APIC_BUS_UNKNOWN;
689	io->io_pins[pin].io_irq = IRQ_EXTINT;
690	if (enable_extint)
691		io->io_pins[pin].io_masked = 0;
692	else
693		io->io_pins[pin].io_masked = 1;
694	io->io_pins[pin].io_edgetrigger = 1;
695	io->io_pins[pin].io_activehi = 1;
696	if (bootverbose)
697		printf("ioapic%u: Routing external 8259A's -> intpin %d\n",
698		    io->io_id, pin);
699	return (0);
700}
701
702int
703ioapic_set_polarity(void *cookie, u_int pin, enum intr_polarity pol)
704{
705	struct ioapic *io;
706	int activehi;
707
708	io = (struct ioapic *)cookie;
709	if (pin >= io->io_numintr || pol == INTR_POLARITY_CONFORM)
710		return (EINVAL);
711	if (io->io_pins[pin].io_irq >= NUM_IO_INTS)
712		return (EINVAL);
713	activehi = (pol == INTR_POLARITY_HIGH);
714	if (io->io_pins[pin].io_activehi == activehi)
715		return (0);
716	io->io_pins[pin].io_activehi = activehi;
717	if (bootverbose)
718		printf("ioapic%u: intpin %d polarity: %s\n", io->io_id, pin,
719		    pol == INTR_POLARITY_HIGH ? "high" : "low");
720	return (0);
721}
722
723int
724ioapic_set_triggermode(void *cookie, u_int pin, enum intr_trigger trigger)
725{
726	struct ioapic *io;
727	int edgetrigger;
728
729	io = (struct ioapic *)cookie;
730	if (pin >= io->io_numintr || trigger == INTR_TRIGGER_CONFORM)
731		return (EINVAL);
732	if (io->io_pins[pin].io_irq >= NUM_IO_INTS)
733		return (EINVAL);
734	edgetrigger = (trigger == INTR_TRIGGER_EDGE);
735	if (io->io_pins[pin].io_edgetrigger == edgetrigger)
736		return (0);
737	io->io_pins[pin].io_edgetrigger = edgetrigger;
738	if (bootverbose)
739		printf("ioapic%u: intpin %d trigger: %s\n", io->io_id, pin,
740		    trigger == INTR_TRIGGER_EDGE ? "edge" : "level");
741	return (0);
742}
743
744/*
745 * Register a complete I/O APIC object with the interrupt subsystem.
746 */
747void
748ioapic_register(void *cookie)
749{
750	struct ioapic_intsrc *pin;
751	struct ioapic *io;
752	volatile ioapic_t *apic;
753	uint32_t flags;
754	int i;
755
756	io = (struct ioapic *)cookie;
757	apic = io->io_addr;
758	mtx_lock_spin(&icu_lock);
759	flags = ioapic_read(apic, IOAPIC_VER) & IOART_VER_VERSION;
760	STAILQ_INSERT_TAIL(&ioapic_list, io, io_next);
761	mtx_unlock_spin(&icu_lock);
762	printf("ioapic%u <Version %u.%u> irqs %u-%u on motherboard\n",
763	    io->io_id, flags >> 4, flags & 0xf, io->io_intbase,
764	    io->io_intbase + io->io_numintr - 1);
765
766	/* Register valid pins as interrupt sources. */
767	intr_register_pic(&io->io_pic);
768	for (i = 0, pin = io->io_pins; i < io->io_numintr; i++, pin++)
769		if (pin->io_irq < NUM_IO_INTS)
770			intr_register_source(&pin->io_intsrc);
771}
772
773/* A simple new-bus driver to consume PCI I/O APIC devices. */
774static int
775ioapic_pci_probe(device_t dev)
776{
777
778	if (pci_get_class(dev) == PCIC_BASEPERIPH &&
779	    pci_get_subclass(dev) == PCIS_BASEPERIPH_PIC) {
780		switch (pci_get_progif(dev)) {
781		case PCIP_BASEPERIPH_PIC_IO_APIC:
782			device_set_desc(dev, "IO APIC");
783			break;
784		case PCIP_BASEPERIPH_PIC_IOX_APIC:
785			device_set_desc(dev, "IO(x) APIC");
786			break;
787		default:
788			return (ENXIO);
789		}
790		device_quiet(dev);
791		return (-10000);
792	}
793	return (ENXIO);
794}
795
796static int
797ioapic_pci_attach(device_t dev)
798{
799
800	return (0);
801}
802
803static device_method_t ioapic_pci_methods[] = {
804	/* Device interface */
805	DEVMETHOD(device_probe,		ioapic_pci_probe),
806	DEVMETHOD(device_attach,	ioapic_pci_attach),
807
808	{ 0, 0 }
809};
810
811DEFINE_CLASS_0(ioapic, ioapic_pci_driver, ioapic_pci_methods, 0);
812
813static devclass_t ioapic_devclass;
814DRIVER_MODULE(ioapic, pci, ioapic_pci_driver, ioapic_devclass, 0, 0);
815
816/*
817 * A new-bus driver to consume the memory resources associated with
818 * the APICs in the system.  On some systems ACPI or PnPBIOS system
819 * resource devices may already claim these resources.  To keep from
820 * breaking those devices, we attach ourself to the nexus device after
821 * legacy0 and acpi0 and ignore any allocation failures.
822 */
823static void
824apic_identify(driver_t *driver, device_t parent)
825{
826
827	/*
828	 * Add at order 12.  acpi0 is probed at order 10 and legacy0
829	 * is probed at order 11.
830	 */
831	if (lapic_paddr != 0)
832		BUS_ADD_CHILD(parent, 12, "apic", 0);
833}
834
835static int
836apic_probe(device_t dev)
837{
838
839	device_set_desc(dev, "APIC resources");
840	device_quiet(dev);
841	return (0);
842}
843
844static void
845apic_add_resource(device_t dev, int rid, vm_paddr_t base, size_t length)
846{
847	int error;
848
849#ifdef PAE
850	/*
851	 * Resources use long's to track resources, so we can't
852	 * include memory regions above 4GB.
853	 */
854	if (base >= ~0ul)
855		return;
856#endif
857	error = bus_set_resource(dev, SYS_RES_MEMORY, rid, base, length);
858	if (error)
859		panic("apic_add_resource: resource %d failed set with %d", rid,
860		    error);
861	bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 0);
862}
863
864static int
865apic_attach(device_t dev)
866{
867	struct ioapic *io;
868	int i;
869
870	/* Reserve the local APIC. */
871	apic_add_resource(dev, 0, lapic_paddr, sizeof(lapic_t));
872	i = 1;
873	STAILQ_FOREACH(io, &ioapic_list, io_next) {
874		apic_add_resource(dev, i, io->io_paddr, IOAPIC_MEM_REGION);
875		i++;
876	}
877	return (0);
878}
879
880static device_method_t apic_methods[] = {
881	/* Device interface */
882	DEVMETHOD(device_identify,	apic_identify),
883	DEVMETHOD(device_probe,		apic_probe),
884	DEVMETHOD(device_attach,	apic_attach),
885
886	{ 0, 0 }
887};
888
889DEFINE_CLASS_0(apic, apic_driver, apic_methods, 0);
890
891static devclass_t apic_devclass;
892DRIVER_MODULE(apic, nexus, apic_driver, apic_devclass, 0, 0);
893