io_apic.c revision 145057
11590Srgrimes/*-
21590Srgrimes * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
31590Srgrimes * All rights reserved.
41590Srgrimes *
51590Srgrimes * Redistribution and use in source and binary forms, with or without
61590Srgrimes * modification, are permitted provided that the following conditions
71590Srgrimes * are met:
81590Srgrimes * 1. Redistributions of source code must retain the above copyright
91590Srgrimes *    notice, this list of conditions and the following disclaimer.
101590Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111590Srgrimes *    notice, this list of conditions and the following disclaimer in the
121590Srgrimes *    documentation and/or other materials provided with the distribution.
131590Srgrimes * 3. Neither the name of the author nor the names of any co-contributors
141590Srgrimes *    may be used to endorse or promote products derived from this software
151590Srgrimes *    without specific prior written permission.
161590Srgrimes *
171590Srgrimes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
181590Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191590Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201590Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
211590Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221590Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231590Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241590Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251590Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261590Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271590Srgrimes * SUCH DAMAGE.
281590Srgrimes */
291590Srgrimes
301590Srgrimes#include <sys/cdefs.h>
311590Srgrimes__FBSDID("$FreeBSD: head/sys/i386/i386/io_apic.c 145057 2005-04-14 06:33:23Z jhb $");
321590Srgrimes
331590Srgrimes#include "opt_isa.h"
3423246Swosch#include "opt_no_mixed_mode.h"
351590Srgrimes
361590Srgrimes#include <sys/param.h>
371590Srgrimes#include <sys/systm.h>
3823246Swosch#include <sys/bus.h>
391590Srgrimes#include <sys/kernel.h>
401590Srgrimes#include <sys/malloc.h>
4127605Scharnier#include <sys/lock.h>
421590Srgrimes#include <sys/mutex.h>
4327605Scharnier
4427605Scharnier#include <vm/vm.h>
4550477Speter#include <vm/pmap.h>
461590Srgrimes
471590Srgrimes#include <machine/apicreg.h>
481590Srgrimes#include <machine/frame.h>
491590Srgrimes#include <machine/intr_machdep.h>
501590Srgrimes#include <machine/apicvar.h>
511590Srgrimes#include <machine/segments.h>
521590Srgrimes
531590Srgrimes#define IOAPIC_ISA_INTS		16
5423246Swosch#define	IOAPIC_MEM_REGION	32
551590Srgrimes#define	IOAPIC_REDTBL_LO(i)	(IOAPIC_REDTBL + (i) * 2)
561590Srgrimes#define	IOAPIC_REDTBL_HI(i)	(IOAPIC_REDTBL_LO(i) + 1)
5757339Sshin
581590Srgrimes#define	VECTOR_EXTINT		252
591590Srgrimes#define	VECTOR_NMI		253
601590Srgrimes#define	VECTOR_SMI		254
6116423Sache#define	VECTOR_DISABLED		255
6216423Sache
631590Srgrimes#define	DEST_NONE		-1
641590Srgrimes#define	DEST_EXTINT		-2
651590Srgrimes
661590Srgrimes#define	TODO		printf("%s: not implemented!\n", __func__)
6740102Smarkm
6841079Sjdpstatic MALLOC_DEFINE(M_IOAPIC, "I/O APIC", "I/O APIC structures");
6916423Sache
701590Srgrimes/*
711590Srgrimes * New interrupt support code..
721590Srgrimes *
731590Srgrimes * XXX: we really should have the interrupt cookie passed up from new-bus
741590Srgrimes * just be a int pin, and not map 1:1 to interrupt vector number but should
751590Srgrimes * use INTR_TYPE_FOO to set priority bands for device classes and do all the
761590Srgrimes * magic remapping of intpin to vector in here.  For now we just cheat as on
771590Srgrimes * ia64 and map intpin X to vector NRSVIDT + X.  Note that we assume that the
781590Srgrimes * first IO APIC has ISA interrupts on pins 1-15.  Not sure how you are
791590Srgrimes * really supposed to figure out which IO APIC in a system with multiple IO
801590Srgrimes * APIC's actually has the ISA interrupts routed to it.  As far as interrupt
8174874Smarkm * pin numbers, we use the ACPI System Interrupt number model where each
8241279Sjdp * IO APIC has a contiguous chunk of the System Interrupt address space.
8341279Sjdp */
8474874Smarkm
8574874Smarkm/*
863702Spst * Direct the ExtINT pin on the first I/O APIC to a logical cluster of
871590Srgrimes * CPUs rather than a physical destination of just the BSP.
881590Srgrimes *
8957339Sshin * Note: This is disabled by default as test systems seem to croak with it
9057339Sshin * enabled.
9157339Sshin#define ENABLE_EXTINT_LOGICAL_DESTINATION
9257339Sshin */
9357339Sshin
941590Srgrimesstruct ioapic_intsrc {
951590Srgrimes	struct intsrc io_intsrc;
961590Srgrimes	u_int io_intpin:8;
971590Srgrimes	u_int io_vector:8;
9821528Sdavidn	u_int io_activehi:1;
991590Srgrimes	u_int io_edgetrigger:1;
1001590Srgrimes	u_int io_masked:1;
1011590Srgrimes	int io_dest:5;
10223985Sdavidn	int io_bus:4;
1031590Srgrimes};
1041590Srgrimes
10529922Smarkmstruct ioapic {
1062224Sguido	struct pic io_pic;
1071590Srgrimes	u_int io_id:8;			/* logical ID */
10874874Smarkm	u_int io_apic_id:4;
10941279Sjdp	u_int io_intbase:8;		/* System Interrupt base */
11072215Snectar	u_int io_numintr:8;
11172215Snectar	volatile ioapic_t *io_addr;	/* XXX: should use bus_space */
11274874Smarkm	STAILQ_ENTRY(ioapic) io_next;
11374874Smarkm	struct ioapic_intsrc io_pins[0];
11474874Smarkm};
11574874Smarkm
11674874Smarkmstatic u_int	ioapic_read(volatile ioapic_t *apic, int reg);
11774874Smarkmstatic void	ioapic_write(volatile ioapic_t *apic, int reg, u_int val);
11874874Smarkmstatic const char *ioapic_bus_string(int bus_type);
11974874Smarkmstatic void	ioapic_print_vector(struct ioapic_intsrc *intpin);
12074874Smarkmstatic void	ioapic_enable_source(struct intsrc *isrc);
12174874Smarkmstatic void	ioapic_disable_source(struct intsrc *isrc, int eoi);
12274874Smarkmstatic void	ioapic_eoi_source(struct intsrc *isrc);
12374874Smarkmstatic void	ioapic_enable_intr(struct intsrc *isrc);
12474874Smarkmstatic int	ioapic_vector(struct intsrc *isrc);
12541279Sjdpstatic int	ioapic_source_pending(struct intsrc *isrc);
1261590Srgrimesstatic int	ioapic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
12727605Scharnier		    enum intr_polarity pol);
1281590Srgrimesstatic void	ioapic_suspend(struct intsrc *isrc);
1291590Srgrimesstatic void	ioapic_resume(struct intsrc *isrc);
13023985Sdavidnstatic void	ioapic_program_destination(struct ioapic_intsrc *intpin);
13123985Sdavidnstatic void	ioapic_program_intpin(struct ioapic_intsrc *intpin);
13276786Sobrienstatic void	ioapic_setup_mixed_mode(struct ioapic_intsrc *intpin);
13376786Sobrien
1341590Srgrimesstatic STAILQ_HEAD(,ioapic) ioapic_list = STAILQ_HEAD_INITIALIZER(ioapic_list);
1351590Srgrimesstruct pic ioapic_template = { ioapic_enable_source, ioapic_disable_source,
1361590Srgrimes			       ioapic_eoi_source, ioapic_enable_intr,
1371590Srgrimes			       ioapic_vector, ioapic_source_pending,
1381590Srgrimes			       ioapic_suspend, ioapic_resume,
1391590Srgrimes			       ioapic_config_intr };
1401590Srgrimes
14142272Seivindstatic int bsp_id, current_cluster, logical_clusters, next_ioapic_base;
14242272Seivindstatic u_int mixed_mode_enabled, next_id, program_logical_dest;
14342272Seivind#ifdef NO_MIXED_MODE
1441590Srgrimesstatic int mixed_mode_active = 0;
1451590Srgrimes#else
14676786Sobrienstatic int mixed_mode_active = 1;
14716423Sache#endif
1481590SrgrimesTUNABLE_INT("hw.apic.mixed_mode", &mixed_mode_active);
1491590Srgrimes
1501590Srgrimesstatic __inline void
1511590Srgrimes_ioapic_eoi_source(struct intsrc *isrc)
1521590Srgrimes{
1531590Srgrimes	lapic_eoi();
1541590Srgrimes}
1551590Srgrimes
1561590Srgrimesstatic u_int
1571590Srgrimesioapic_read(volatile ioapic_t *apic, int reg)
1581590Srgrimes{
15923985Sdavidn
1601590Srgrimes	mtx_assert(&icu_lock, MA_OWNED);
1614878Sugen	apic->ioregsel = reg;
16223985Sdavidn	return (apic->iowin);
16335559Speter}
16446007Sache
16545431Sbrianstatic void
16642272Seivindioapic_write(volatile ioapic_t *apic, int reg, u_int val)
16742272Seivind{
16823985Sdavidn
16921528Sdavidn	mtx_assert(&icu_lock, MA_OWNED);
17074874Smarkm	apic->ioregsel = reg;
17174874Smarkm	apic->iowin = val;
17274874Smarkm}
17374874Smarkm
1741590Srgrimesstatic const char *
17542272Seivindioapic_bus_string(int bus_type)
17642272Seivind{
17742272Seivind
17842272Seivind	switch (bus_type) {
17942272Seivind	case APIC_BUS_ISA:
18042272Seivind		return ("ISA");
18142272Seivind	case APIC_BUS_EISA:
18242272Seivind		return ("EISA");
18342272Seivind	case APIC_BUS_PCI:
1841590Srgrimes		return ("PCI");
1851590Srgrimes	default:
1861590Srgrimes		return ("unknown");
1871590Srgrimes	}
1881590Srgrimes}
1891590Srgrimes
1901590Srgrimesstatic void
1911590Srgrimesioapic_print_vector(struct ioapic_intsrc *intpin)
1921590Srgrimes{
1931590Srgrimes
1941590Srgrimes	switch (intpin->io_vector) {
1951590Srgrimes	case VECTOR_DISABLED:
1963205Spst		printf("disabled");
19723985Sdavidn		break;
1981590Srgrimes	case VECTOR_EXTINT:
1991590Srgrimes		printf("ExtINT");
2001590Srgrimes		break;
20135557Speter	case VECTOR_NMI:
20246007Sache		printf("NMI");
20324360Simp		break;
2041590Srgrimes	case VECTOR_SMI:
2051590Srgrimes		printf("SMI");
2061590Srgrimes		break;
2071590Srgrimes	default:
2081590Srgrimes		printf("%s IRQ %u", ioapic_bus_string(intpin->io_bus),
2091590Srgrimes		    intpin->io_vector);
2101590Srgrimes	}
2111590Srgrimes}
2123205Spst
21336559Samuraistatic void
21445431Sbrianioapic_enable_source(struct intsrc *isrc)
21536559Samurai{
21616423Sache	struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
21757339Sshin	struct ioapic *io = (struct ioapic *)isrc->is_pic;
21857339Sshin	uint32_t flags;
21957339Sshin
22057339Sshin	mtx_lock_spin(&icu_lock);
22157339Sshin	if (intpin->io_masked) {
22257339Sshin		flags = ioapic_read(io->io_addr,
22357339Sshin		    IOAPIC_REDTBL_LO(intpin->io_intpin));
22457339Sshin		flags &= ~(IOART_INTMASK);
22557339Sshin		ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin),
22616423Sache		    flags);
22757339Sshin		intpin->io_masked = 0;
22857339Sshin	}
22957339Sshin	mtx_unlock_spin(&icu_lock);
23057339Sshin}
23157339Sshin
23257339Sshinstatic void
23357339Sshinioapic_disable_source(struct intsrc *isrc, int eoi)
23416423Sache{
23516423Sache	struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
23657339Sshin	struct ioapic *io = (struct ioapic *)isrc->is_pic;
23757339Sshin	uint32_t flags;
23816423Sache
2391590Srgrimes	mtx_lock_spin(&icu_lock);
2401590Srgrimes	if (!intpin->io_masked && !intpin->io_edgetrigger) {
2411590Srgrimes		flags = ioapic_read(io->io_addr,
2421590Srgrimes		    IOAPIC_REDTBL_LO(intpin->io_intpin));
2431590Srgrimes		flags |= IOART_INTMSET;
2441590Srgrimes		ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin),
2451590Srgrimes		    flags);
2461590Srgrimes		intpin->io_masked = 1;
2471590Srgrimes	}
24827605Scharnier
2491590Srgrimes	if (eoi == PIC_EOI)
2501590Srgrimes		_ioapic_eoi_source(isrc);
2511590Srgrimes
2521590Srgrimes	mtx_unlock_spin(&icu_lock);
2531590Srgrimes}
2541590Srgrimes
2551590Srgrimesstatic void
2561590Srgrimesioapic_eoi_source(struct intsrc *isrc)
2571590Srgrimes{
2581590Srgrimes
2591590Srgrimes	_ioapic_eoi_source(isrc);
2601590Srgrimes}
2611590Srgrimes
2621590Srgrimes/*
2631590Srgrimes * Completely program an intpin based on the data in its interrupt source
2641590Srgrimes * structure.
2651590Srgrimes */
2661590Srgrimesstatic void
26723985Sdavidnioapic_program_intpin(struct ioapic_intsrc *intpin)
2681590Srgrimes{
2691590Srgrimes	struct ioapic *io = (struct ioapic *)intpin->io_intsrc.is_pic;
2701590Srgrimes	uint32_t low, high, value;
2711590Srgrimes
27223985Sdavidn	/*
27323985Sdavidn	 * For pins routed via mixed mode or disabled, just ensure that
27423985Sdavidn	 * they are masked.
27523985Sdavidn	 */
27676786Sobrien	if (intpin->io_dest == DEST_EXTINT ||
27776786Sobrien	    intpin->io_vector == VECTOR_DISABLED) {
27876786Sobrien		low = ioapic_read(io->io_addr,
27923985Sdavidn		    IOAPIC_REDTBL_LO(intpin->io_intpin));
28023985Sdavidn		if ((low & IOART_INTMASK) == IOART_INTMCLR)
28123985Sdavidn			ioapic_write(io->io_addr,
28223985Sdavidn			    IOAPIC_REDTBL_LO(intpin->io_intpin),
28321528Sdavidn			    low | IOART_INTMSET);
2841590Srgrimes		return;
2851590Srgrimes	}
2861590Srgrimes
2871590Srgrimes	/* Set the destination. */
2881590Srgrimes	if (intpin->io_dest == DEST_NONE) {
2891590Srgrimes		low = IOART_DESTPHY;
29021528Sdavidn		high = bsp_id << APIC_ID_SHIFT;
29121528Sdavidn	} else {
2921590Srgrimes		low = IOART_DESTLOG;
2931590Srgrimes		high = (intpin->io_dest << APIC_ID_CLUSTER_SHIFT |
2941590Srgrimes		    APIC_ID_CLUSTER_ID) << APIC_ID_SHIFT;
2951590Srgrimes	}
2961590Srgrimes
2971590Srgrimes	/* Program the rest of the low word. */
2981590Srgrimes	if (intpin->io_edgetrigger)
2991590Srgrimes		low |= IOART_TRGREDG;
3001590Srgrimes	else
3011590Srgrimes		low |= IOART_TRGRLVL;
3021590Srgrimes	if (intpin->io_activehi)
3031590Srgrimes		low |= IOART_INTAHI;
30427605Scharnier	else
30527605Scharnier		low |= IOART_INTALO;
3061590Srgrimes	if (intpin->io_masked)
30741279Sjdp		low |= IOART_INTMSET;
30823985Sdavidn	switch (intpin->io_vector) {
30923985Sdavidn	case VECTOR_EXTINT:
3101590Srgrimes		KASSERT(intpin->io_edgetrigger,
3111590Srgrimes		    ("EXTINT not edge triggered"));
3121590Srgrimes		low |= IOART_DELEXINT;
3131590Srgrimes		break;
3141590Srgrimes	case VECTOR_NMI:
31521528Sdavidn		KASSERT(intpin->io_edgetrigger,
3163205Spst		    ("NMI not edge triggered"));
3173205Spst		low |= IOART_DELNMI;
3183205Spst		break;
31923985Sdavidn	case VECTOR_SMI:
32023985Sdavidn		KASSERT(intpin->io_edgetrigger,
3213205Spst		    ("SMI not edge triggered"));
3223205Spst		low |= IOART_DELSMI;
3233205Spst		break;
32424321Sdavidn	default:
32524251Sdavidn		low |= IOART_DELLOPRI | apic_irq_to_idt(intpin->io_vector);
32624251Sdavidn	}
32724251Sdavidn
32824251Sdavidn	/* Write the values to the APIC. */
3293205Spst	mtx_lock_spin(&icu_lock);
3303205Spst	ioapic_write(io->io_addr, IOAPIC_REDTBL_LO(intpin->io_intpin), low);
3313205Spst	value = ioapic_read(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin));
3321590Srgrimes	value &= ~IOART_DEST;
3331590Srgrimes	value |= high;
3341590Srgrimes	ioapic_write(io->io_addr, IOAPIC_REDTBL_HI(intpin->io_intpin), value);
3351590Srgrimes	mtx_unlock_spin(&icu_lock);
33674874Smarkm}
33741279Sjdp
33841279Sjdp/*
33941279Sjdp * Program an individual intpin's logical destination.
34041279Sjdp */
34141279Sjdpstatic void
34241279Sjdpioapic_program_destination(struct ioapic_intsrc *intpin)
34374874Smarkm{
34441279Sjdp	struct ioapic *io = (struct ioapic *)intpin->io_intsrc.is_pic;
34523985Sdavidn
34641279Sjdp	KASSERT(intpin->io_dest != DEST_NONE,
34723985Sdavidn	    ("intpin not assigned to a cluster"));
34874874Smarkm	KASSERT(intpin->io_dest != DEST_EXTINT,
34941279Sjdp	    ("intpin routed via ExtINT"));
35041279Sjdp	if (bootverbose) {
35141279Sjdp		printf("ioapic%u: routing intpin %u (", io->io_id,
35241279Sjdp		    intpin->io_intpin);
35341279Sjdp		ioapic_print_vector(intpin);
35441279Sjdp		printf(") to cluster %u\n", intpin->io_dest);
35541279Sjdp	}
35674874Smarkm	ioapic_program_intpin(intpin);
3571590Srgrimes}
3583205Spst
3591590Srgrimesstatic void
3601590Srgrimesioapic_assign_cluster(struct ioapic_intsrc *intpin)
3611590Srgrimes{
3621590Srgrimes
36324222Sdavidn	/*
36424222Sdavidn	 * Assign this intpin to a logical APIC cluster in a
36524222Sdavidn	 * round-robin fashion.  We don't actually use the logical
36624222Sdavidn	 * destination for this intpin until after all the CPU's
36724222Sdavidn	 * have been started so that we don't end up with interrupts
3681590Srgrimes	 * that don't go anywhere.  Another alternative might be to
3691590Srgrimes	 * start up the CPU's earlier so that they can handle interrupts
3701590Srgrimes	 * sooner.
3711590Srgrimes	 */
37223985Sdavidn	intpin->io_dest = current_cluster;
37323985Sdavidn	current_cluster++;
37423985Sdavidn	if (current_cluster >= logical_clusters)
37523985Sdavidn		current_cluster = 0;
37623985Sdavidn	if (program_logical_dest)
37723985Sdavidn		ioapic_program_destination(intpin);
37823985Sdavidn}
3791590Srgrimes
3801590Srgrimesstatic void
3811590Srgrimesioapic_enable_intr(struct intsrc *isrc)
38238374Sjkoshy{
3831590Srgrimes	struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
3841590Srgrimes
3851590Srgrimes	KASSERT(intpin->io_dest != DEST_EXTINT,
3861590Srgrimes	    ("ExtINT pin trying to use ioapic enable_intr method"));
3871590Srgrimes	if (intpin->io_dest == DEST_NONE) {
3881590Srgrimes		ioapic_assign_cluster(intpin);
3891590Srgrimes		lapic_enable_intr(intpin->io_vector);
3901590Srgrimes	}
39141279Sjdp}
39241279Sjdp
39341279Sjdpstatic int
39441279Sjdpioapic_vector(struct intsrc *isrc)
39541279Sjdp{
3961590Srgrimes	struct ioapic_intsrc *pin;
3971590Srgrimes
39821528Sdavidn	pin = (struct ioapic_intsrc *)isrc;
3991590Srgrimes	return (pin->io_vector);
40021528Sdavidn}
40146007Sache
40246007Sachestatic int
40335557Speterioapic_source_pending(struct intsrc *isrc)
40421528Sdavidn{
40526021Spst	struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
40623985Sdavidn
40724485Sdavidn	return (lapic_intr_pending(intpin->io_vector));
40823985Sdavidn}
40923985Sdavidn
41023985Sdavidnstatic int
41157546Sacheioapic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
4121590Srgrimes    enum intr_polarity pol)
41335557Speter{
41446007Sache	struct ioapic_intsrc *intpin = (struct ioapic_intsrc *)isrc;
41521528Sdavidn	struct ioapic *io = (struct ioapic *)isrc->is_pic;
41621528Sdavidn	int changed;
4171590Srgrimes
4181590Srgrimes	KASSERT(!(trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM),
4191590Srgrimes	    ("%s: Conforming trigger or polarity\n", __func__));
4202532Sjkh
42130564Sjoerg	/*
42221528Sdavidn	 * EISA interrupts always use active high polarity, so don't allow
42376710Seric	 * them to be set to active low.
42476710Seric	 *
42576710Seric	 * XXX: Should we write to the ELCR if the trigger mode changes for
42676710Seric	 * an EISA IRQ or an ISA IRQ with the ELCR present?
42776710Seric	 */
42876710Seric	if (intpin->io_bus == APIC_BUS_EISA)
42976710Seric		pol = INTR_POLARITY_HIGH;
43076710Seric	changed = 0;
43176710Seric	if (intpin->io_edgetrigger != (trig == INTR_TRIGGER_EDGE)) {
43276710Seric		if (bootverbose)
43376710Seric			printf("ioapic%u: Changing trigger for pin %u to %s\n",
43476710Seric			    io->io_id, intpin->io_intpin,
43576710Seric			    trig == INTR_TRIGGER_EDGE ? "edge" : "level");
43623985Sdavidn		intpin->io_edgetrigger = (trig == INTR_TRIGGER_EDGE);
43723985Sdavidn		changed++;
43823985Sdavidn	}
4394878Sugen	if (intpin->io_activehi != (pol == INTR_POLARITY_HIGH)) {
44021528Sdavidn		if (bootverbose)
4411590Srgrimes			printf("ioapic%u: Changing polarity for pin %u to %s\n",
4421590Srgrimes			    io->io_id, intpin->io_intpin,
4434878Sugen			    pol == INTR_POLARITY_HIGH ? "high" : "low");
44423985Sdavidn		intpin->io_activehi = (pol == INTR_POLARITY_HIGH);
44523985Sdavidn		changed++;
44623985Sdavidn	}
44723985Sdavidn	if (changed)
44823985Sdavidn		ioapic_program_intpin(intpin);
44923985Sdavidn	return (0);
45023985Sdavidn}
45123985Sdavidn
45221528Sdavidnstatic void
45321528Sdavidnioapic_suspend(struct intsrc *isrc)
45457339Sshin{
45557339Sshin
45621528Sdavidn	TODO;
45757339Sshin}
45857339Sshin
45957339Sshinstatic void
46057339Sshinioapic_resume(struct intsrc *isrc)
46157339Sshin{
46257339Sshin
46357339Sshin	ioapic_program_intpin((struct ioapic_intsrc *)isrc);
46457339Sshin}
46557339Sshin
46657339Sshin/*
46757339Sshin * APIC enumerators call this function to indicate that the 8259A AT PICs
46857339Sshin * are available and that mixed mode can be used.
46921528Sdavidn */
47057339Sshinvoid
47157339Sshinioapic_enable_mixed_mode(void)
47223985Sdavidn{
47323985Sdavidn
47421528Sdavidn	mixed_mode_enabled = 1;
47521528Sdavidn}
47623985Sdavidn
47723985Sdavidn/*
47821528Sdavidn * Allocate and return a logical cluster ID.  Note that the first time
47923985Sdavidn * this is called, it returns cluster 0.  ioapic_enable_intr() treats
48023985Sdavidn * the two cases of logical_clusters == 0 and logical_clusters == 1 the
48121528Sdavidn * same: one cluster of ID 0 exists.  The logical_clusters == 0 case is
48223985Sdavidn * for UP kernels, which should never call this function.
48323985Sdavidn */
48423985Sdavidnint
48523985Sdavidnioapic_next_logical_cluster(void)
48623985Sdavidn{
48723985Sdavidn
48823985Sdavidn	if (logical_clusters >= APIC_MAX_CLUSTER)
48923985Sdavidn		panic("WARNING: Local APIC cluster IDs exhausted!");
49023985Sdavidn	return (logical_clusters++);
49121528Sdavidn}
49221528Sdavidn
49323985Sdavidn/*
49423985Sdavidn * Create a plain I/O APIC object.
49521528Sdavidn */
49621528Sdavidnvoid *
4971590Srgrimesioapic_create(uintptr_t addr, int32_t apic_id, int intbase)
4981590Srgrimes{
4991590Srgrimes	struct ioapic *io;
5001590Srgrimes	struct ioapic_intsrc *intpin;
5011590Srgrimes	volatile ioapic_t *apic;
5021590Srgrimes	u_int numintr, i;
5031590Srgrimes	uint32_t value;
5041590Srgrimes
5051590Srgrimes	/* Map the register window so we can access the device. */
5061590Srgrimes	apic = (ioapic_t *)pmap_mapdev(addr, IOAPIC_MEM_REGION);
5071590Srgrimes	mtx_lock_spin(&icu_lock);
5082224Sguido	value = ioapic_read(apic, IOAPIC_VER);
5092224Sguido	mtx_unlock_spin(&icu_lock);
5102224Sguido
5112224Sguido	/* If it's version register doesn't seem to work, punt. */
5122224Sguido	if (value == 0xffffff) {
5132224Sguido		pmap_unmapdev((vm_offset_t)apic, IOAPIC_MEM_REGION);
5142224Sguido		return (NULL);
51550124Simp	}
51650124Simp
51750124Simp	/* Determine the number of vectors and set the APIC ID. */
51850124Simp	numintr = ((value & IOART_VER_MAXREDIR) >> MAXREDIRSHIFT) + 1;
51950124Simp	io = malloc(sizeof(struct ioapic) +
52050124Simp	    numintr * sizeof(struct ioapic_intsrc), M_IOAPIC, M_WAITOK);
52159621Ssheldonh	io->io_pic = ioapic_template;
52250124Simp	mtx_lock_spin(&icu_lock);
52350124Simp	io->io_id = next_id++;
52450124Simp	io->io_apic_id = ioapic_read(apic, IOAPIC_ID) >> APIC_ID_SHIFT;
52550124Simp	if (apic_id != -1 && io->io_apic_id != apic_id) {
5261590Srgrimes		ioapic_write(apic, IOAPIC_ID, apic_id << APIC_ID_SHIFT);
52750124Simp		mtx_unlock_spin(&icu_lock);
52823985Sdavidn		io->io_apic_id = apic_id;
52923985Sdavidn		printf("ioapic%u: Changing APIC ID to %d\n", io->io_id,
53023985Sdavidn		    apic_id);
53124222Sdavidn	} else
53224222Sdavidn		mtx_unlock_spin(&icu_lock);
5331590Srgrimes	if (intbase == -1) {
53423985Sdavidn		intbase = next_ioapic_base;
53523985Sdavidn		printf("ioapic%u: Assuming intbase of %d\n", io->io_id,
53623985Sdavidn		    intbase);
53723985Sdavidn	} else if (intbase != next_ioapic_base)
53823985Sdavidn		printf("ioapic%u: WARNING: intbase %d != expected base %d\n",
53924894Sdavidn		    io->io_id, intbase, next_ioapic_base);
5401590Srgrimes	io->io_intbase = intbase;
5411590Srgrimes	next_ioapic_base = intbase + numintr;
5423205Spst	io->io_numintr = numintr;
5433205Spst	io->io_addr = apic;
5443205Spst
5453205Spst	/*
5463205Spst	 * Initialize pins.  Start off with interrupts disabled.  Default
54723985Sdavidn	 * to active-hi and edge-triggered for ISA interrupts and active-lo
54823985Sdavidn	 * and level-triggered for all others.
54923985Sdavidn	 */
55023985Sdavidn	bzero(io->io_pins, sizeof(struct ioapic_intsrc) * numintr);
55123985Sdavidn	mtx_lock_spin(&icu_lock);
55223985Sdavidn	for (i = 0, intpin = io->io_pins; i < numintr; i++, intpin++) {
55321528Sdavidn		intpin->io_intsrc.is_pic = (struct pic *)io;
55421528Sdavidn		intpin->io_intpin = i;
55523985Sdavidn		intpin->io_vector = intbase + i;
55623985Sdavidn
55723985Sdavidn		/*
55823985Sdavidn		 * Assume that pin 0 on the first I/O APIC is an ExtINT pin
55923985Sdavidn		 * if mixed mode is enabled and an ISA interrupt if not.
56023985Sdavidn		 * Assume that pins 1-15 are ISA interrupts and that all
56123985Sdavidn		 * other pins are PCI interrupts.
56223985Sdavidn		 */
56323985Sdavidn		if (intpin->io_vector == 0 && mixed_mode_enabled)
56423985Sdavidn			ioapic_set_extint(io, i);
56523985Sdavidn		else if (intpin->io_vector < IOAPIC_ISA_INTS) {
56623985Sdavidn			intpin->io_bus = APIC_BUS_ISA;
56723985Sdavidn			intpin->io_activehi = 1;
56823985Sdavidn			intpin->io_edgetrigger = 1;
56923985Sdavidn			intpin->io_masked = 1;
57023985Sdavidn		} else {
57123985Sdavidn			intpin->io_bus = APIC_BUS_PCI;
57223985Sdavidn			intpin->io_activehi = 0;
57321528Sdavidn			intpin->io_edgetrigger = 0;
57421528Sdavidn			intpin->io_masked = 1;
57521528Sdavidn		}
57674874Smarkm
57723985Sdavidn		/*
57872215Snectar		 * Route interrupts to the BSP by default using physical
57972215Snectar		 * addressing.  Vectored interrupts get readdressed using
58072215Snectar		 * logical IDs to CPU clusters when they are enabled.
58174874Smarkm		 */
58274874Smarkm		intpin->io_dest = DEST_NONE;
58374874Smarkm		if (bootverbose && intpin->io_vector != VECTOR_DISABLED) {
58474874Smarkm			printf("ioapic%u: intpin %d -> ",  io->io_id, i);
58574874Smarkm			ioapic_print_vector(intpin);
58674874Smarkm			printf(" (%s, %s)\n", intpin->io_edgetrigger ?
58772215Snectar			    "edge" : "level", intpin->io_activehi ? "high" :
58872215Snectar			    "low");
58974874Smarkm		}
59074874Smarkm		value = ioapic_read(apic, IOAPIC_REDTBL_LO(i));
59174874Smarkm		ioapic_write(apic, IOAPIC_REDTBL_LO(i), value | IOART_INTMSET);
59274874Smarkm	}
59374874Smarkm	mtx_unlock_spin(&icu_lock);
59474874Smarkm
59574874Smarkm	return (io);
59674874Smarkm}
59774874Smarkm
59874874Smarkmint
59974874Smarkmioapic_get_vector(void *cookie, u_int pin)
60074874Smarkm{
60174874Smarkm	struct ioapic *io;
60274874Smarkm
60374874Smarkm	io = (struct ioapic *)cookie;
60474874Smarkm	if (pin >= io->io_numintr)
60574874Smarkm		return (-1);
60674874Smarkm	return (io->io_pins[pin].io_vector);
60774874Smarkm}
60874874Smarkm
60974874Smarkmint
61074874Smarkmioapic_disable_pin(void *cookie, u_int pin)
61174874Smarkm{
61274874Smarkm	struct ioapic *io;
61374874Smarkm
61474874Smarkm	io = (struct ioapic *)cookie;
61574874Smarkm	if (pin >= io->io_numintr)
61674874Smarkm		return (EINVAL);
61774874Smarkm	if (io->io_pins[pin].io_vector == VECTOR_DISABLED)
61874874Smarkm		return (EINVAL);
61974874Smarkm	io->io_pins[pin].io_vector = VECTOR_DISABLED;
62074874Smarkm	if (bootverbose)
62174874Smarkm		printf("ioapic%u: intpin %d disabled\n", io->io_id, pin);
62274874Smarkm	return (0);
62374874Smarkm}
62474874Smarkm
62574874Smarkmint
62623985Sdavidnioapic_remap_vector(void *cookie, u_int pin, int vector)
62721528Sdavidn{
62821528Sdavidn	struct ioapic *io;
62941279Sjdp
63041279Sjdp	io = (struct ioapic *)cookie;
63141279Sjdp	if (pin >= io->io_numintr || vector < 0)
63241279Sjdp		return (EINVAL);
63341279Sjdp	if (io->io_pins[pin].io_vector >= NUM_IO_INTS)
63474874Smarkm		return (EINVAL);
63521528Sdavidn	io->io_pins[pin].io_vector = vector;
63621528Sdavidn	if (bootverbose)
6373205Spst		printf("ioapic%u: Routing IRQ %d -> intpin %d\n", io->io_id,
63821528Sdavidn		    vector, pin);
63923148Sache	return (0);
64021528Sdavidn}
64123985Sdavidn
64221528Sdavidnint
64321528Sdavidnioapic_set_bus(void *cookie, u_int pin, int bus_type)
64423985Sdavidn{
64521528Sdavidn	struct ioapic *io;
64641279Sjdp
64741279Sjdp	if (bus_type < 0 || bus_type > APIC_BUS_MAX)
64821528Sdavidn		return (EINVAL);
64921528Sdavidn	io = (struct ioapic *)cookie;
6501590Srgrimes	if (pin >= io->io_numintr)
65123985Sdavidn		return (EINVAL);
65223985Sdavidn	if (io->io_pins[pin].io_vector >= NUM_IO_INTS)
65323985Sdavidn		return (EINVAL);
65421528Sdavidn	io->io_pins[pin].io_bus = bus_type;
65521528Sdavidn	if (bootverbose)
65621528Sdavidn		printf("ioapic%u: intpin %d bus %s\n", io->io_id, pin,
65723985Sdavidn		    ioapic_bus_string(bus_type));
65823985Sdavidn	return (0);
65923985Sdavidn}
66023985Sdavidn
66123985Sdavidnint
66223985Sdavidnioapic_set_nmi(void *cookie, u_int pin)
66323985Sdavidn{
66421528Sdavidn	struct ioapic *io;
66521528Sdavidn
66621528Sdavidn	io = (struct ioapic *)cookie;
66721528Sdavidn	if (pin >= io->io_numintr)
66823985Sdavidn		return (EINVAL);
66921528Sdavidn	if (io->io_pins[pin].io_vector == VECTOR_NMI)
67021528Sdavidn		return (0);
67121528Sdavidn	if (io->io_pins[pin].io_vector >= NUM_IO_INTS)
67221528Sdavidn		return (EINVAL);
67321528Sdavidn	io->io_pins[pin].io_bus = APIC_BUS_UNKNOWN;
67423985Sdavidn	io->io_pins[pin].io_vector = VECTOR_NMI;
67523985Sdavidn	io->io_pins[pin].io_masked = 0;
6761590Srgrimes	io->io_pins[pin].io_edgetrigger = 1;
67723985Sdavidn	io->io_pins[pin].io_activehi = 1;
67823985Sdavidn	if (bootverbose)
6791590Srgrimes		printf("ioapic%u: Routing NMI -> intpin %d\n",
6801590Srgrimes		    io->io_id, pin);
68121528Sdavidn	return (0);
6823205Spst}
6831590Srgrimes
6841590Srgrimesint
6851590Srgrimesioapic_set_smi(void *cookie, u_int pin)
6861590Srgrimes{
6871590Srgrimes	struct ioapic *io;
6884878Sugen
68921528Sdavidn	io = (struct ioapic *)cookie;
6904878Sugen	if (pin >= io->io_numintr)
6914878Sugen		return (EINVAL);
6924878Sugen	if (io->io_pins[pin].io_vector == VECTOR_SMI)
69323985Sdavidn		return (0);
69423985Sdavidn	if (io->io_pins[pin].io_vector >= NUM_IO_INTS)
69523985Sdavidn		return (EINVAL);
69623985Sdavidn	io->io_pins[pin].io_bus = APIC_BUS_UNKNOWN;
69723985Sdavidn	io->io_pins[pin].io_vector = VECTOR_SMI;
69823985Sdavidn	io->io_pins[pin].io_masked = 0;
69921528Sdavidn	io->io_pins[pin].io_edgetrigger = 1;
70021528Sdavidn	io->io_pins[pin].io_activehi = 1;
7011590Srgrimes	if (bootverbose)
7021590Srgrimes		printf("ioapic%u: Routing SMI -> intpin %d\n",
70341279Sjdp		    io->io_id, pin);
70441279Sjdp	return (0);
70541279Sjdp}
70641279Sjdp
70741279Sjdpint
70841279Sjdpioapic_set_extint(void *cookie, u_int pin)
70941279Sjdp{
71041279Sjdp	struct ioapic *io;
71141279Sjdp
71241279Sjdp	io = (struct ioapic *)cookie;
71341279Sjdp	if (pin >= io->io_numintr)
71476786Sobrien		return (EINVAL);
71541279Sjdp	if (io->io_pins[pin].io_vector == VECTOR_EXTINT)
71641279Sjdp		return (0);
71741279Sjdp	if (io->io_pins[pin].io_vector >= NUM_IO_INTS)
71841279Sjdp		return (EINVAL);
71941279Sjdp	io->io_pins[pin].io_bus = APIC_BUS_UNKNOWN;
72041279Sjdp	io->io_pins[pin].io_vector = VECTOR_EXTINT;
72141279Sjdp
72241279Sjdp	/* Enable this pin if mixed mode is available and active. */
72341279Sjdp	if (mixed_mode_enabled && mixed_mode_active)
72441279Sjdp		io->io_pins[pin].io_masked = 0;
72541279Sjdp	else
72641279Sjdp		io->io_pins[pin].io_masked = 1;
72741279Sjdp	io->io_pins[pin].io_edgetrigger = 1;
72841279Sjdp	io->io_pins[pin].io_activehi = 1;
72974874Smarkm	if (bootverbose)
73041279Sjdp		printf("ioapic%u: Routing external 8259A's -> intpin %d\n",
73141279Sjdp		    io->io_id, pin);
73241279Sjdp	return (0);
73341279Sjdp}
73441279Sjdp
73541279Sjdpint
73641279Sjdpioapic_set_polarity(void *cookie, u_int pin, enum intr_polarity pol)
73741279Sjdp{
73841279Sjdp	struct ioapic *io;
73941279Sjdp
74041279Sjdp	io = (struct ioapic *)cookie;
74141279Sjdp	if (pin >= io->io_numintr || pol == INTR_POLARITY_CONFORM)
74241279Sjdp		return (EINVAL);
74341279Sjdp	if (io->io_pins[pin].io_vector >= NUM_IO_INTS)
74441279Sjdp		return (EINVAL);
74541279Sjdp	io->io_pins[pin].io_activehi = (pol == INTR_POLARITY_HIGH);
74641279Sjdp	if (bootverbose)
74741279Sjdp		printf("ioapic%u: intpin %d polarity: %s\n", io->io_id, pin,
74841279Sjdp		    pol == INTR_POLARITY_HIGH ? "high" : "low");
74941279Sjdp	return (0);
75041279Sjdp}
75141279Sjdp
75241279Sjdpint
75341279Sjdpioapic_set_triggermode(void *cookie, u_int pin, enum intr_trigger trigger)
75441279Sjdp{
75541279Sjdp	struct ioapic *io;
75641279Sjdp
75741279Sjdp	io = (struct ioapic *)cookie;
75841279Sjdp	if (pin >= io->io_numintr || trigger == INTR_TRIGGER_CONFORM)
75941279Sjdp		return (EINVAL);
76041279Sjdp	if (io->io_pins[pin].io_vector >= NUM_IO_INTS)
76141279Sjdp		return (EINVAL);
76241279Sjdp	io->io_pins[pin].io_edgetrigger = (trigger == INTR_TRIGGER_EDGE);
76341279Sjdp	if (bootverbose)
76441279Sjdp		printf("ioapic%u: intpin %d trigger: %s\n", io->io_id, pin,
76541279Sjdp		    trigger == INTR_TRIGGER_EDGE ? "edge" : "level");
76641279Sjdp	return (0);
76741279Sjdp}
76841279Sjdp
76941279Sjdp/*
77041279Sjdp * Register a complete I/O APIC object with the interrupt subsystem.
77141279Sjdp */
77241279Sjdpvoid
77341279Sjdpioapic_register(void *cookie)
77441279Sjdp{
77541279Sjdp	struct ioapic_intsrc *pin;
77641279Sjdp	struct ioapic *io;
77741279Sjdp	volatile ioapic_t *apic;
77841279Sjdp	uint32_t flags;
77941279Sjdp	int i;
78041279Sjdp
78141279Sjdp	io = (struct ioapic *)cookie;
78241279Sjdp	apic = io->io_addr;
78341279Sjdp	mtx_lock_spin(&icu_lock);
78441279Sjdp	flags = ioapic_read(apic, IOAPIC_VER) & IOART_VER_VERSION;
78541279Sjdp	STAILQ_INSERT_TAIL(&ioapic_list, io, io_next);
78641279Sjdp	mtx_unlock_spin(&icu_lock);
78741279Sjdp	printf("ioapic%u <Version %u.%u> irqs %u-%u on motherboard\n",
78841279Sjdp	    io->io_id, flags >> 4, flags & 0xf, io->io_intbase,
78941279Sjdp	    io->io_intbase + io->io_numintr - 1);
79041279Sjdp	bsp_id = PCPU_GET(apic_id);
79141279Sjdp	for (i = 0, pin = io->io_pins; i < io->io_numintr; i++, pin++) {
79241279Sjdp		/*
79341279Sjdp		 * Finish initializing the pins by programming the vectors
79441279Sjdp		 * and delivery mode.
79541279Sjdp		 */
79641279Sjdp		if (pin->io_vector == VECTOR_DISABLED)
79741279Sjdp			continue;
79841279Sjdp		ioapic_program_intpin(pin);
79941279Sjdp		if (pin->io_vector >= NUM_IO_INTS)
80074874Smarkm			continue;
80141279Sjdp		/*
80241279Sjdp		 * Route IRQ0 via the 8259A using mixed mode if mixed mode
80341279Sjdp		 * is available and turned on.
80474874Smarkm		 */
80574874Smarkm		if (pin->io_vector == 0 && mixed_mode_active &&
80674874Smarkm		    mixed_mode_enabled)
80774874Smarkm			ioapic_setup_mixed_mode(pin);
80874874Smarkm		else
80974874Smarkm			intr_register_source(&pin->io_intsrc);
81074874Smarkm	}
81174874Smarkm}
81274874Smarkm
81374874Smarkm/*
81474874Smarkm * Program all the intpins to use logical destinations once the AP's
81574874Smarkm * have been launched.
81641279Sjdp */
81774874Smarkmstatic void
81874874Smarkmioapic_set_logical_destinations(void *arg __unused)
81974874Smarkm{
82074874Smarkm	struct ioapic *io;
82174874Smarkm	int i;
82274874Smarkm
82374874Smarkm	program_logical_dest = 1;
82441279Sjdp	STAILQ_FOREACH(io, &ioapic_list, io_next)
82541279Sjdp	    for (i = 0; i < io->io_numintr; i++)
82672215Snectar		    if (io->io_pins[i].io_dest != DEST_NONE &&
82772215Snectar			io->io_pins[i].io_dest != DEST_EXTINT)
82872215Snectar			    ioapic_program_destination(&io->io_pins[i]);
82972215Snectar}
83072215SnectarSYSINIT(ioapic_destinations, SI_SUB_SMP, SI_ORDER_SECOND,
83172215Snectar    ioapic_set_logical_destinations, NULL)
83272215Snectar
83372215Snectar/*
83472215Snectar * Support for mixed-mode interrupt sources.  These sources route an ISA
83572215Snectar * IRQ through the 8259A's via the ExtINT on pin 0 of the I/O APIC that
83672215Snectar * routes the ISA interrupts.  We just ignore the intpins that use this
83772215Snectar * mode and allow the atpic driver to register its interrupt source for
83872215Snectar * that IRQ instead.
83972215Snectar */
84072215Snectar
84172215Snectarstatic void
84272215Snectarioapic_setup_mixed_mode(struct ioapic_intsrc *intpin)
84372215Snectar{
84472215Snectar	struct ioapic_intsrc *extint;
84572215Snectar	struct ioapic *io;
84672215Snectar
84772215Snectar	/*
84872215Snectar	 * Mark the associated I/O APIC intpin as being delivered via
84972215Snectar	 * ExtINT and enable the ExtINT pin on the I/O APIC if needed.
85072215Snectar	 */
85172215Snectar	intpin->io_dest = DEST_EXTINT;
85272215Snectar	io = (struct ioapic *)intpin->io_intsrc.is_pic;
85372215Snectar	extint = &io->io_pins[0];
85472215Snectar	if (extint->io_vector != VECTOR_EXTINT)
85572215Snectar		panic("Can't find ExtINT pin to route through!");
85672215Snectar#ifdef ENABLE_EXTINT_LOGICAL_DESTINATION
85772215Snectar	if (extint->io_dest == DEST_NONE)
85872215Snectar		ioapic_assign_cluster(extint);
85972215Snectar#endif
86072215Snectar}
86172215Snectar