xref: /freebsd/sys/x86/isa/atpic.c (revision 80d2b3de16bf9d5df774555382ce8e3f4f887b6f)
132580301SAttilio Rao /*-
2ebf5747bSPedro F. Giffuni  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3ebf5747bSPedro F. Giffuni  *
432580301SAttilio Rao  * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
532580301SAttilio Rao  *
632580301SAttilio Rao  * Redistribution and use in source and binary forms, with or without
732580301SAttilio Rao  * modification, are permitted provided that the following conditions
832580301SAttilio Rao  * are met:
932580301SAttilio Rao  * 1. Redistributions of source code must retain the above copyright
1032580301SAttilio Rao  *    notice, this list of conditions and the following disclaimer.
1132580301SAttilio Rao  * 2. Redistributions in binary form must reproduce the above copyright
1232580301SAttilio Rao  *    notice, this list of conditions and the following disclaimer in the
1332580301SAttilio Rao  *    documentation and/or other materials provided with the distribution.
1432580301SAttilio Rao  *
1532580301SAttilio Rao  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1632580301SAttilio Rao  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1732580301SAttilio Rao  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1832580301SAttilio Rao  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1932580301SAttilio Rao  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2032580301SAttilio Rao  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2132580301SAttilio Rao  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2232580301SAttilio Rao  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2332580301SAttilio Rao  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2432580301SAttilio Rao  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2532580301SAttilio Rao  * SUCH DAMAGE.
2632580301SAttilio Rao  */
2732580301SAttilio Rao 
2832580301SAttilio Rao /*
2932580301SAttilio Rao  * PIC driver for the 8259A Master and Slave PICs in PC/AT machines.
3032580301SAttilio Rao  */
3132580301SAttilio Rao 
3232580301SAttilio Rao #include <sys/cdefs.h>
3332580301SAttilio Rao __FBSDID("$FreeBSD$");
3432580301SAttilio Rao 
3532580301SAttilio Rao #include "opt_auto_eoi.h"
3632580301SAttilio Rao #include "opt_isa.h"
3732580301SAttilio Rao 
3832580301SAttilio Rao #include <sys/param.h>
3932580301SAttilio Rao #include <sys/systm.h>
40f115c061SMark Johnston #include <sys/asan.h>
4132580301SAttilio Rao #include <sys/bus.h>
4232580301SAttilio Rao #include <sys/interrupt.h>
4332580301SAttilio Rao #include <sys/kernel.h>
4432580301SAttilio Rao #include <sys/lock.h>
4532580301SAttilio Rao #include <sys/module.h>
46b0f71f1bSMark Johnston #include <sys/msan.h>
4732580301SAttilio Rao 
4832580301SAttilio Rao #include <machine/cpufunc.h>
4932580301SAttilio Rao #include <machine/frame.h>
5032580301SAttilio Rao #include <machine/intr_machdep.h>
5132580301SAttilio Rao #include <machine/md_var.h>
5232580301SAttilio Rao #include <machine/resource.h>
5332580301SAttilio Rao #include <machine/segments.h>
5432580301SAttilio Rao 
5532580301SAttilio Rao #include <dev/ic/i8259.h>
5632580301SAttilio Rao #include <x86/isa/icu.h>
57f79309d2SWarner Losh #include <isa/isareg.h>
5832580301SAttilio Rao #include <isa/isavar.h>
5932580301SAttilio Rao 
6032580301SAttilio Rao #ifdef __amd64__
6132580301SAttilio Rao #define	SDT_ATPIC	SDT_SYSIGT
6232580301SAttilio Rao #define	GSEL_ATPIC	0
6332580301SAttilio Rao #else
6432580301SAttilio Rao #define	SDT_ATPIC	SDT_SYS386IGT
6532580301SAttilio Rao #define	GSEL_ATPIC	GSEL(GCODE_SEL, SEL_KPL)
6632580301SAttilio Rao #endif
6732580301SAttilio Rao 
6832580301SAttilio Rao #define	MASTER	0
6932580301SAttilio Rao #define	SLAVE	1
7032580301SAttilio Rao 
7162a08214SJohn Baldwin #define	IMEN_MASK(ai)		(IRQ_MASK((ai)->at_irq))
7262a08214SJohn Baldwin 
7332580301SAttilio Rao #define	NUM_ISA_IRQS		16
7432580301SAttilio Rao 
7532580301SAttilio Rao static void	atpic_init(void *dummy);
7632580301SAttilio Rao 
7732580301SAttilio Rao inthand_t
7832580301SAttilio Rao 	IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2),
7932580301SAttilio Rao 	IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5),
8032580301SAttilio Rao 	IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8),
8132580301SAttilio Rao 	IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11),
8232580301SAttilio Rao 	IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14),
8332580301SAttilio Rao 	IDTVEC(atpic_intr15);
84bd50262fSKonstantin Belousov /* XXXKIB i386 uses stubs until pti comes */
85bd50262fSKonstantin Belousov inthand_t
86bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr0_pti), IDTVEC(atpic_intr1_pti),
87bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr2_pti), IDTVEC(atpic_intr3_pti),
88bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr4_pti), IDTVEC(atpic_intr5_pti),
89bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr6_pti), IDTVEC(atpic_intr7_pti),
90bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr8_pti), IDTVEC(atpic_intr9_pti),
91bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr10_pti), IDTVEC(atpic_intr11_pti),
92bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr12_pti), IDTVEC(atpic_intr13_pti),
93bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr14_pti), IDTVEC(atpic_intr15_pti);
9432580301SAttilio Rao 
9532580301SAttilio Rao #define	IRQ(ap, ai)	((ap)->at_irqbase + (ai)->at_irq)
9632580301SAttilio Rao 
9762a08214SJohn Baldwin #define	ATPIC(io, base, eoi) {						\
9862a08214SJohn Baldwin 		.at_pic = {						\
99fd036deaSJohn Baldwin 			.pic_register_sources = atpic_register_sources,	\
10062a08214SJohn Baldwin 			.pic_enable_source = atpic_enable_source,	\
10162a08214SJohn Baldwin 			.pic_disable_source = atpic_disable_source,	\
10262a08214SJohn Baldwin 			.pic_eoi_source = (eoi),			\
10362a08214SJohn Baldwin 			.pic_enable_intr = atpic_enable_intr,		\
10462a08214SJohn Baldwin 			.pic_disable_intr = atpic_disable_intr,		\
10562a08214SJohn Baldwin 			.pic_vector = atpic_vector,			\
10662a08214SJohn Baldwin 			.pic_source_pending = atpic_source_pending,	\
10762a08214SJohn Baldwin 			.pic_resume = atpic_resume,			\
10862a08214SJohn Baldwin 			.pic_config_intr = atpic_config_intr,		\
10962a08214SJohn Baldwin 			.pic_assign_cpu = atpic_assign_cpu		\
11062a08214SJohn Baldwin 		},							\
11162a08214SJohn Baldwin 		.at_ioaddr = (io),					\
11262a08214SJohn Baldwin 		.at_irqbase = (base),					\
11362a08214SJohn Baldwin 		.at_intbase = IDT_IO_INTS + (base),			\
11462a08214SJohn Baldwin 		.at_imen = 0xff,					\
11562a08214SJohn Baldwin 	}
11632580301SAttilio Rao 
11732580301SAttilio Rao #define	INTSRC(irq)							\
11832580301SAttilio Rao 	{ { &atpics[(irq) / 8].at_pic }, IDTVEC(atpic_intr ## irq ),	\
119bd50262fSKonstantin Belousov 	    IDTVEC(atpic_intr ## irq ## _pti), (irq) % 8 }
12032580301SAttilio Rao 
12132580301SAttilio Rao struct atpic {
12232580301SAttilio Rao 	struct pic at_pic;
12332580301SAttilio Rao 	int	at_ioaddr;
12432580301SAttilio Rao 	int	at_irqbase;
12532580301SAttilio Rao 	uint8_t	at_intbase;
12662a08214SJohn Baldwin 	uint8_t	at_imen;
12732580301SAttilio Rao };
12832580301SAttilio Rao 
12932580301SAttilio Rao struct atpic_intsrc {
13032580301SAttilio Rao 	struct intsrc at_intsrc;
131bd50262fSKonstantin Belousov 	inthand_t *at_intr, *at_intr_pti;
13232580301SAttilio Rao 	int	at_irq;			/* Relative to PIC base. */
13332580301SAttilio Rao 	enum intr_trigger at_trigger;
13432580301SAttilio Rao 	u_long	at_count;
13532580301SAttilio Rao 	u_long	at_straycount;
13632580301SAttilio Rao };
13732580301SAttilio Rao 
138fd036deaSJohn Baldwin static void atpic_register_sources(struct pic *pic);
13932580301SAttilio Rao static void atpic_enable_source(struct intsrc *isrc);
14032580301SAttilio Rao static void atpic_disable_source(struct intsrc *isrc, int eoi);
14132580301SAttilio Rao static void atpic_eoi_master(struct intsrc *isrc);
14232580301SAttilio Rao static void atpic_eoi_slave(struct intsrc *isrc);
14332580301SAttilio Rao static void atpic_enable_intr(struct intsrc *isrc);
14432580301SAttilio Rao static void atpic_disable_intr(struct intsrc *isrc);
14532580301SAttilio Rao static int atpic_vector(struct intsrc *isrc);
146428b7ca2SJustin T. Gibbs static void atpic_resume(struct pic *pic, bool suspend_cancelled);
14732580301SAttilio Rao static int atpic_source_pending(struct intsrc *isrc);
14832580301SAttilio Rao static int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
14932580301SAttilio Rao     enum intr_polarity pol);
15032580301SAttilio Rao static int atpic_assign_cpu(struct intsrc *isrc, u_int apic_id);
15132580301SAttilio Rao static void i8259_init(struct atpic *pic, int slave);
15232580301SAttilio Rao 
15332580301SAttilio Rao static struct atpic atpics[] = {
15462a08214SJohn Baldwin 	ATPIC(IO_ICU1, 0, atpic_eoi_master),
15562a08214SJohn Baldwin 	ATPIC(IO_ICU2, 8, atpic_eoi_slave)
15632580301SAttilio Rao };
15732580301SAttilio Rao 
15832580301SAttilio Rao static struct atpic_intsrc atintrs[] = {
15932580301SAttilio Rao 	INTSRC(0),
16032580301SAttilio Rao 	INTSRC(1),
16132580301SAttilio Rao 	INTSRC(2),
16232580301SAttilio Rao 	INTSRC(3),
16332580301SAttilio Rao 	INTSRC(4),
16432580301SAttilio Rao 	INTSRC(5),
16532580301SAttilio Rao 	INTSRC(6),
16632580301SAttilio Rao 	INTSRC(7),
16732580301SAttilio Rao 	INTSRC(8),
16832580301SAttilio Rao 	INTSRC(9),
16932580301SAttilio Rao 	INTSRC(10),
17032580301SAttilio Rao 	INTSRC(11),
17132580301SAttilio Rao 	INTSRC(12),
17232580301SAttilio Rao 	INTSRC(13),
17332580301SAttilio Rao 	INTSRC(14),
17432580301SAttilio Rao 	INTSRC(15),
17532580301SAttilio Rao };
17632580301SAttilio Rao 
177ea24b056SPedro F. Giffuni CTASSERT(nitems(atintrs) == NUM_ISA_IRQS);
17832580301SAttilio Rao 
17932580301SAttilio Rao static __inline void
18032580301SAttilio Rao _atpic_eoi_master(struct intsrc *isrc)
18132580301SAttilio Rao {
18232580301SAttilio Rao 
18332580301SAttilio Rao 	KASSERT(isrc->is_pic == &atpics[MASTER].at_pic,
18432580301SAttilio Rao 	    ("%s: mismatched pic", __func__));
18532580301SAttilio Rao #ifndef AUTO_EOI_1
18632580301SAttilio Rao 	outb(atpics[MASTER].at_ioaddr, OCW2_EOI);
18732580301SAttilio Rao #endif
18832580301SAttilio Rao }
18932580301SAttilio Rao 
19032580301SAttilio Rao /*
19132580301SAttilio Rao  * The data sheet says no auto-EOI on slave, but it sometimes works.
19232580301SAttilio Rao  * So, if AUTO_EOI_2 is enabled, we use it.
19332580301SAttilio Rao  */
19432580301SAttilio Rao static __inline void
19532580301SAttilio Rao _atpic_eoi_slave(struct intsrc *isrc)
19632580301SAttilio Rao {
19732580301SAttilio Rao 
19832580301SAttilio Rao 	KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic,
19932580301SAttilio Rao 	    ("%s: mismatched pic", __func__));
20032580301SAttilio Rao #ifndef AUTO_EOI_2
20132580301SAttilio Rao 	outb(atpics[SLAVE].at_ioaddr, OCW2_EOI);
20232580301SAttilio Rao #ifndef AUTO_EOI_1
20332580301SAttilio Rao 	outb(atpics[MASTER].at_ioaddr, OCW2_EOI);
20432580301SAttilio Rao #endif
20532580301SAttilio Rao #endif
20632580301SAttilio Rao }
20732580301SAttilio Rao 
20832580301SAttilio Rao static void
209fd036deaSJohn Baldwin atpic_register_sources(struct pic *pic)
210fd036deaSJohn Baldwin {
211fd036deaSJohn Baldwin 	struct atpic *ap = (struct atpic *)pic;
212fd036deaSJohn Baldwin 	struct atpic_intsrc *ai;
213fd036deaSJohn Baldwin 	int i;
214fd036deaSJohn Baldwin 
215fd036deaSJohn Baldwin 	/*
216fd036deaSJohn Baldwin 	 * If any of the ISA IRQs have an interrupt source already, then
217fd036deaSJohn Baldwin 	 * assume that the I/O APICs are being used and don't register any
218fd036deaSJohn Baldwin 	 * of our interrupt sources.  This makes sure we don't accidentally
219fd036deaSJohn Baldwin 	 * use mixed mode.  The "accidental" use could otherwise occur on
220fd036deaSJohn Baldwin 	 * machines that route the ACPI SCI interrupt to a different ISA
221fd036deaSJohn Baldwin 	 * IRQ (at least one machine routes it to IRQ 13) thus disabling
222fd036deaSJohn Baldwin 	 * that APIC ISA routing and allowing the ATPIC source for that IRQ
223fd036deaSJohn Baldwin 	 * to leak through.  We used to depend on this feature for routing
224fd036deaSJohn Baldwin 	 * IRQ0 via mixed mode, but now we don't use mixed mode at all.
22587bdca82SJohn Baldwin 	 *
22687bdca82SJohn Baldwin 	 * To avoid the slave not register sources after the master
22787bdca82SJohn Baldwin 	 * registers its sources, register all IRQs when this function is
22887bdca82SJohn Baldwin 	 * called on the master.
229fd036deaSJohn Baldwin 	 */
23087bdca82SJohn Baldwin 	if (ap != &atpics[MASTER])
23187bdca82SJohn Baldwin 		return;
232fd036deaSJohn Baldwin 	for (i = 0; i < NUM_ISA_IRQS; i++)
233fd036deaSJohn Baldwin 		if (intr_lookup_source(i) != NULL)
234fd036deaSJohn Baldwin 			return;
235fd036deaSJohn Baldwin 
236fd036deaSJohn Baldwin 	/* Loop through all interrupt sources and add them. */
23787bdca82SJohn Baldwin 	for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
23887bdca82SJohn Baldwin 		if (i == ICU_SLAVEID)
239fd036deaSJohn Baldwin 			continue;
240fd036deaSJohn Baldwin 		intr_register_source(&ai->at_intsrc);
241fd036deaSJohn Baldwin 	}
242fd036deaSJohn Baldwin }
243fd036deaSJohn Baldwin 
244fd036deaSJohn Baldwin static void
24532580301SAttilio Rao atpic_enable_source(struct intsrc *isrc)
24632580301SAttilio Rao {
24732580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
24832580301SAttilio Rao 	struct atpic *ap = (struct atpic *)isrc->is_pic;
24932580301SAttilio Rao 
25032580301SAttilio Rao 	spinlock_enter();
25162a08214SJohn Baldwin 	if (ap->at_imen & IMEN_MASK(ai)) {
25262a08214SJohn Baldwin 		ap->at_imen &= ~IMEN_MASK(ai);
25362a08214SJohn Baldwin 		outb(ap->at_ioaddr + ICU_IMR_OFFSET, ap->at_imen);
25432580301SAttilio Rao 	}
25532580301SAttilio Rao 	spinlock_exit();
25632580301SAttilio Rao }
25732580301SAttilio Rao 
25832580301SAttilio Rao static void
25932580301SAttilio Rao atpic_disable_source(struct intsrc *isrc, int eoi)
26032580301SAttilio Rao {
26132580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
26232580301SAttilio Rao 	struct atpic *ap = (struct atpic *)isrc->is_pic;
26332580301SAttilio Rao 
26432580301SAttilio Rao 	spinlock_enter();
26532580301SAttilio Rao 	if (ai->at_trigger != INTR_TRIGGER_EDGE) {
26662a08214SJohn Baldwin 		ap->at_imen |= IMEN_MASK(ai);
26762a08214SJohn Baldwin 		outb(ap->at_ioaddr + ICU_IMR_OFFSET, ap->at_imen);
26832580301SAttilio Rao 	}
26932580301SAttilio Rao 
27032580301SAttilio Rao 	/*
27132580301SAttilio Rao 	 * Take care to call these functions directly instead of through
27232580301SAttilio Rao 	 * a function pointer.  All of the referenced variables should
27332580301SAttilio Rao 	 * still be hot in the cache.
27432580301SAttilio Rao 	 */
27532580301SAttilio Rao 	if (eoi == PIC_EOI) {
27632580301SAttilio Rao 		if (isrc->is_pic == &atpics[MASTER].at_pic)
27732580301SAttilio Rao 			_atpic_eoi_master(isrc);
27832580301SAttilio Rao 		else
27932580301SAttilio Rao 			_atpic_eoi_slave(isrc);
28032580301SAttilio Rao 	}
28132580301SAttilio Rao 
28232580301SAttilio Rao 	spinlock_exit();
28332580301SAttilio Rao }
28432580301SAttilio Rao 
28532580301SAttilio Rao static void
28632580301SAttilio Rao atpic_eoi_master(struct intsrc *isrc)
28732580301SAttilio Rao {
28832580301SAttilio Rao #ifndef AUTO_EOI_1
28932580301SAttilio Rao 	spinlock_enter();
29032580301SAttilio Rao 	_atpic_eoi_master(isrc);
29132580301SAttilio Rao 	spinlock_exit();
29232580301SAttilio Rao #endif
29332580301SAttilio Rao }
29432580301SAttilio Rao 
29532580301SAttilio Rao static void
29632580301SAttilio Rao atpic_eoi_slave(struct intsrc *isrc)
29732580301SAttilio Rao {
29832580301SAttilio Rao #ifndef AUTO_EOI_2
29932580301SAttilio Rao 	spinlock_enter();
30032580301SAttilio Rao 	_atpic_eoi_slave(isrc);
30132580301SAttilio Rao 	spinlock_exit();
30232580301SAttilio Rao #endif
30332580301SAttilio Rao }
30432580301SAttilio Rao 
30532580301SAttilio Rao static void
30632580301SAttilio Rao atpic_enable_intr(struct intsrc *isrc)
30732580301SAttilio Rao {
30832580301SAttilio Rao }
30932580301SAttilio Rao 
31032580301SAttilio Rao static void
31132580301SAttilio Rao atpic_disable_intr(struct intsrc *isrc)
31232580301SAttilio Rao {
31332580301SAttilio Rao }
31432580301SAttilio Rao 
31532580301SAttilio Rao static int
31632580301SAttilio Rao atpic_vector(struct intsrc *isrc)
31732580301SAttilio Rao {
31832580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
31932580301SAttilio Rao 	struct atpic *ap = (struct atpic *)isrc->is_pic;
32032580301SAttilio Rao 
32132580301SAttilio Rao 	return (IRQ(ap, ai));
32232580301SAttilio Rao }
32332580301SAttilio Rao 
32432580301SAttilio Rao static int
32532580301SAttilio Rao atpic_source_pending(struct intsrc *isrc)
32632580301SAttilio Rao {
32732580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
32832580301SAttilio Rao 	struct atpic *ap = (struct atpic *)isrc->is_pic;
32932580301SAttilio Rao 
33032580301SAttilio Rao 	return (inb(ap->at_ioaddr) & IMEN_MASK(ai));
33132580301SAttilio Rao }
33232580301SAttilio Rao 
33332580301SAttilio Rao static void
334428b7ca2SJustin T. Gibbs atpic_resume(struct pic *pic, bool suspend_cancelled)
33532580301SAttilio Rao {
33632580301SAttilio Rao 	struct atpic *ap = (struct atpic *)pic;
33732580301SAttilio Rao 
33832580301SAttilio Rao 	i8259_init(ap, ap == &atpics[SLAVE]);
33932580301SAttilio Rao 	if (ap == &atpics[SLAVE] && elcr_found)
34032580301SAttilio Rao 		elcr_resume();
34132580301SAttilio Rao }
34232580301SAttilio Rao 
34332580301SAttilio Rao static int
34432580301SAttilio Rao atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
34532580301SAttilio Rao     enum intr_polarity pol)
34632580301SAttilio Rao {
34732580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
34832580301SAttilio Rao 	u_int vector;
34932580301SAttilio Rao 
35032580301SAttilio Rao 	/* Map conforming values to edge/hi and sanity check the values. */
35132580301SAttilio Rao 	if (trig == INTR_TRIGGER_CONFORM)
35232580301SAttilio Rao 		trig = INTR_TRIGGER_EDGE;
35332580301SAttilio Rao 	if (pol == INTR_POLARITY_CONFORM)
35432580301SAttilio Rao 		pol = INTR_POLARITY_HIGH;
35532580301SAttilio Rao 	vector = atpic_vector(isrc);
35632580301SAttilio Rao 	if ((trig == INTR_TRIGGER_EDGE && pol == INTR_POLARITY_LOW) ||
35732580301SAttilio Rao 	    (trig == INTR_TRIGGER_LEVEL && pol == INTR_POLARITY_HIGH)) {
35832580301SAttilio Rao 		printf(
35932580301SAttilio Rao 		"atpic: Mismatched config for IRQ%u: trigger %s, polarity %s\n",
36032580301SAttilio Rao 		    vector, trig == INTR_TRIGGER_EDGE ? "edge" : "level",
36132580301SAttilio Rao 		    pol == INTR_POLARITY_HIGH ? "high" : "low");
36232580301SAttilio Rao 		return (EINVAL);
36332580301SAttilio Rao 	}
36432580301SAttilio Rao 
36532580301SAttilio Rao 	/* If there is no change, just return. */
36632580301SAttilio Rao 	if (ai->at_trigger == trig)
36732580301SAttilio Rao 		return (0);
36832580301SAttilio Rao 
36932580301SAttilio Rao 	/*
37032580301SAttilio Rao 	 * Certain IRQs can never be level/lo, so don't try to set them
37132580301SAttilio Rao 	 * that way if asked.  At least some ELCR registers ignore setting
37232580301SAttilio Rao 	 * these bits as well.
37332580301SAttilio Rao 	 */
37432580301SAttilio Rao 	if ((vector == 0 || vector == 1 || vector == 2 || vector == 13) &&
37532580301SAttilio Rao 	    trig == INTR_TRIGGER_LEVEL) {
37632580301SAttilio Rao 		if (bootverbose)
37732580301SAttilio Rao 			printf(
37832580301SAttilio Rao 		"atpic: Ignoring invalid level/low configuration for IRQ%u\n",
37932580301SAttilio Rao 			    vector);
38032580301SAttilio Rao 		return (EINVAL);
38132580301SAttilio Rao 	}
38232580301SAttilio Rao 	if (!elcr_found) {
38332580301SAttilio Rao 		if (bootverbose)
38432580301SAttilio Rao 			printf("atpic: No ELCR to configure IRQ%u as %s\n",
38532580301SAttilio Rao 			    vector, trig == INTR_TRIGGER_EDGE ? "edge/high" :
38632580301SAttilio Rao 			    "level/low");
38732580301SAttilio Rao 		return (ENXIO);
38832580301SAttilio Rao 	}
38932580301SAttilio Rao 	if (bootverbose)
39032580301SAttilio Rao 		printf("atpic: Programming IRQ%u as %s\n", vector,
39132580301SAttilio Rao 		    trig == INTR_TRIGGER_EDGE ? "edge/high" : "level/low");
39232580301SAttilio Rao 	spinlock_enter();
39332580301SAttilio Rao 	elcr_write_trigger(atpic_vector(isrc), trig);
39432580301SAttilio Rao 	ai->at_trigger = trig;
39532580301SAttilio Rao 	spinlock_exit();
39632580301SAttilio Rao 	return (0);
39732580301SAttilio Rao }
39832580301SAttilio Rao 
39932580301SAttilio Rao static int
40032580301SAttilio Rao atpic_assign_cpu(struct intsrc *isrc, u_int apic_id)
40132580301SAttilio Rao {
40232580301SAttilio Rao 
40332580301SAttilio Rao 	/*
40432580301SAttilio Rao 	 * 8259A's are only used in UP in which case all interrupts always
40532580301SAttilio Rao 	 * go to the sole CPU and this function shouldn't even be called.
40632580301SAttilio Rao 	 */
40732580301SAttilio Rao 	panic("%s: bad cookie", __func__);
40832580301SAttilio Rao }
40932580301SAttilio Rao 
41032580301SAttilio Rao static void
41132580301SAttilio Rao i8259_init(struct atpic *pic, int slave)
41232580301SAttilio Rao {
41332580301SAttilio Rao 	int imr_addr;
41432580301SAttilio Rao 
41532580301SAttilio Rao 	/* Reset the PIC and program with next four bytes. */
41632580301SAttilio Rao 	spinlock_enter();
41732580301SAttilio Rao 	outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
41832580301SAttilio Rao 	imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
41932580301SAttilio Rao 
42032580301SAttilio Rao 	/* Start vector. */
42132580301SAttilio Rao 	outb(imr_addr, pic->at_intbase);
42232580301SAttilio Rao 
42332580301SAttilio Rao 	/*
42432580301SAttilio Rao 	 * Setup slave links.  For the master pic, indicate what line
42532580301SAttilio Rao 	 * the slave is configured on.  For the slave indicate
42632580301SAttilio Rao 	 * which line on the master we are connected to.
42732580301SAttilio Rao 	 */
42832580301SAttilio Rao 	if (slave)
42932580301SAttilio Rao 		outb(imr_addr, ICU_SLAVEID);
43032580301SAttilio Rao 	else
43132580301SAttilio Rao 		outb(imr_addr, IRQ_MASK(ICU_SLAVEID));
43232580301SAttilio Rao 
43332580301SAttilio Rao 	/* Set mode. */
43432580301SAttilio Rao 	if (slave)
43532580301SAttilio Rao 		outb(imr_addr, SLAVE_MODE);
43632580301SAttilio Rao 	else
43732580301SAttilio Rao 		outb(imr_addr, MASTER_MODE);
43832580301SAttilio Rao 
43932580301SAttilio Rao 	/* Set interrupt enable mask. */
44062a08214SJohn Baldwin 	outb(imr_addr, pic->at_imen);
44132580301SAttilio Rao 
44232580301SAttilio Rao 	/* Reset is finished, default to IRR on read. */
44332580301SAttilio Rao 	outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
44432580301SAttilio Rao 
44532580301SAttilio Rao 	/* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
44632580301SAttilio Rao 	if (!slave)
44732580301SAttilio Rao 		outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
4482b375b4eSYoshihiro Takahashi 
44932580301SAttilio Rao 	spinlock_exit();
45032580301SAttilio Rao }
45132580301SAttilio Rao 
45232580301SAttilio Rao void
45332580301SAttilio Rao atpic_startup(void)
45432580301SAttilio Rao {
45532580301SAttilio Rao 	struct atpic_intsrc *ai;
45632580301SAttilio Rao 	int i;
45732580301SAttilio Rao 
45832580301SAttilio Rao 	/* Start off with all interrupts disabled. */
45932580301SAttilio Rao 	i8259_init(&atpics[MASTER], 0);
46032580301SAttilio Rao 	i8259_init(&atpics[SLAVE], 1);
46132580301SAttilio Rao 	atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
46232580301SAttilio Rao 
46332580301SAttilio Rao 	/* Install low-level interrupt handlers for all of our IRQs. */
46432580301SAttilio Rao 	for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
46532580301SAttilio Rao 		if (i == ICU_SLAVEID)
46632580301SAttilio Rao 			continue;
46732580301SAttilio Rao 		ai->at_intsrc.is_count = &ai->at_count;
46832580301SAttilio Rao 		ai->at_intsrc.is_straycount = &ai->at_straycount;
46932580301SAttilio Rao 		setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
470bd50262fSKonstantin Belousov 		    ai->at_irq, pti ? ai->at_intr_pti : ai->at_intr, SDT_ATPIC,
471bd50262fSKonstantin Belousov 		    SEL_KPL, GSEL_ATPIC);
47232580301SAttilio Rao 	}
47332580301SAttilio Rao 
47432580301SAttilio Rao 	/*
47532580301SAttilio Rao 	 * Look for an ELCR.  If we find one, update the trigger modes.
47632580301SAttilio Rao 	 * If we don't find one, assume that IRQs 0, 1, 2, and 13 are
47732580301SAttilio Rao 	 * edge triggered and that everything else is level triggered.
47832580301SAttilio Rao 	 * We only use the trigger information to reprogram the ELCR if
47932580301SAttilio Rao 	 * we have one and as an optimization to avoid masking edge
48032580301SAttilio Rao 	 * triggered interrupts.  For the case that we don't have an ELCR,
48132580301SAttilio Rao 	 * it doesn't hurt to mask an edge triggered interrupt, so we
48232580301SAttilio Rao 	 * assume level trigger for any interrupt that we aren't sure is
48332580301SAttilio Rao 	 * edge triggered.
48432580301SAttilio Rao 	 */
48532580301SAttilio Rao 	if (elcr_found) {
48632580301SAttilio Rao 		for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
48732580301SAttilio Rao 			ai->at_trigger = elcr_read_trigger(i);
48832580301SAttilio Rao 	} else {
48932580301SAttilio Rao 		for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
49032580301SAttilio Rao 			switch (i) {
49132580301SAttilio Rao 			case 0:
49232580301SAttilio Rao 			case 1:
49332580301SAttilio Rao 			case 2:
49432580301SAttilio Rao 			case 8:
49532580301SAttilio Rao 			case 13:
49632580301SAttilio Rao 				ai->at_trigger = INTR_TRIGGER_EDGE;
49732580301SAttilio Rao 				break;
49832580301SAttilio Rao 			default:
49932580301SAttilio Rao 				ai->at_trigger = INTR_TRIGGER_LEVEL;
50032580301SAttilio Rao 				break;
50132580301SAttilio Rao 			}
50232580301SAttilio Rao 	}
50332580301SAttilio Rao }
50432580301SAttilio Rao 
50532580301SAttilio Rao static void
50632580301SAttilio Rao atpic_init(void *dummy __unused)
50732580301SAttilio Rao {
50832580301SAttilio Rao 
50932580301SAttilio Rao 	/*
51032580301SAttilio Rao 	 * Register our PICs, even if we aren't going to use any of their
51132580301SAttilio Rao 	 * pins so that they are suspended and resumed.
51232580301SAttilio Rao 	 */
51332580301SAttilio Rao 	if (intr_register_pic(&atpics[0].at_pic) != 0 ||
51432580301SAttilio Rao 	    intr_register_pic(&atpics[1].at_pic) != 0)
51532580301SAttilio Rao 		panic("Unable to register ATPICs");
51632580301SAttilio Rao 
517fd036deaSJohn Baldwin 	if (num_io_irqs == 0)
518fd036deaSJohn Baldwin 		num_io_irqs = NUM_ISA_IRQS;
51932580301SAttilio Rao }
520e68c8d7fSRoger Pau Monné SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_FOURTH, atpic_init, NULL);
52132580301SAttilio Rao 
52232580301SAttilio Rao void
52332580301SAttilio Rao atpic_handle_intr(u_int vector, struct trapframe *frame)
52432580301SAttilio Rao {
52532580301SAttilio Rao 	struct intsrc *isrc;
52632580301SAttilio Rao 
527f115c061SMark Johnston 	kasan_mark(frame, sizeof(*frame), sizeof(*frame), 0);
528b0f71f1bSMark Johnston 	kmsan_mark(frame, sizeof(*frame), KMSAN_STATE_INITED);
529f115c061SMark Johnston 
53032580301SAttilio Rao 	KASSERT(vector < NUM_ISA_IRQS, ("unknown int %u\n", vector));
53132580301SAttilio Rao 	isrc = &atintrs[vector].at_intsrc;
53232580301SAttilio Rao 
53332580301SAttilio Rao 	/*
53432580301SAttilio Rao 	 * If we don't have an event, see if this is a spurious
53532580301SAttilio Rao 	 * interrupt.
53632580301SAttilio Rao 	 */
53732580301SAttilio Rao 	if (isrc->is_event == NULL && (vector == 7 || vector == 15)) {
53832580301SAttilio Rao 		int port, isr;
53932580301SAttilio Rao 
54032580301SAttilio Rao 		/*
54132580301SAttilio Rao 		 * Read the ISR register to see if IRQ 7/15 is really
54232580301SAttilio Rao 		 * pending.  Reset read register back to IRR when done.
54332580301SAttilio Rao 		 */
54432580301SAttilio Rao 		port = ((struct atpic *)isrc->is_pic)->at_ioaddr;
54532580301SAttilio Rao 		spinlock_enter();
54632580301SAttilio Rao 		outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS);
54732580301SAttilio Rao 		isr = inb(port);
54832580301SAttilio Rao 		outb(port, OCW3_SEL | OCW3_RR);
54932580301SAttilio Rao 		spinlock_exit();
55032580301SAttilio Rao 		if ((isr & IRQ_MASK(7)) == 0)
55132580301SAttilio Rao 			return;
55232580301SAttilio Rao 	}
55332580301SAttilio Rao 	intr_execute_handlers(isrc, frame);
55432580301SAttilio Rao }
55532580301SAttilio Rao 
55632580301SAttilio Rao #ifdef DEV_ISA
55732580301SAttilio Rao /*
55832580301SAttilio Rao  * Bus attachment for the ISA PIC.
55932580301SAttilio Rao  */
56032580301SAttilio Rao static struct isa_pnp_id atpic_ids[] = {
56132580301SAttilio Rao 	{ 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
56232580301SAttilio Rao 	{ 0 }
56332580301SAttilio Rao };
56432580301SAttilio Rao 
56532580301SAttilio Rao static int
56632580301SAttilio Rao atpic_probe(device_t dev)
56732580301SAttilio Rao {
56832580301SAttilio Rao 	int result;
56932580301SAttilio Rao 
57032580301SAttilio Rao 	result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
57132580301SAttilio Rao 	if (result <= 0)
57232580301SAttilio Rao 		device_quiet(dev);
57332580301SAttilio Rao 	return (result);
57432580301SAttilio Rao }
57532580301SAttilio Rao 
57632580301SAttilio Rao /*
57732580301SAttilio Rao  * We might be granted IRQ 2, as this is typically consumed by chaining
57832580301SAttilio Rao  * between the two PIC components.  If we're using the APIC, however,
57932580301SAttilio Rao  * this may not be the case, and as such we should free the resource.
58032580301SAttilio Rao  * (XXX untested)
58132580301SAttilio Rao  *
58232580301SAttilio Rao  * The generic ISA attachment code will handle allocating any other resources
58332580301SAttilio Rao  * that we don't explicitly claim here.
58432580301SAttilio Rao  */
58532580301SAttilio Rao static int
58632580301SAttilio Rao atpic_attach(device_t dev)
58732580301SAttilio Rao {
58832580301SAttilio Rao 	struct resource *res;
58932580301SAttilio Rao 	int rid;
59032580301SAttilio Rao 
59132580301SAttilio Rao 	/* Try to allocate our IRQ and then free it. */
59232580301SAttilio Rao 	rid = 0;
59332580301SAttilio Rao 	res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 0);
59432580301SAttilio Rao 	if (res != NULL)
59532580301SAttilio Rao 		bus_release_resource(dev, SYS_RES_IRQ, rid, res);
59632580301SAttilio Rao 	return (0);
59732580301SAttilio Rao }
59832580301SAttilio Rao 
59932580301SAttilio Rao static device_method_t atpic_methods[] = {
60032580301SAttilio Rao 	/* Device interface */
60132580301SAttilio Rao 	DEVMETHOD(device_probe,		atpic_probe),
60232580301SAttilio Rao 	DEVMETHOD(device_attach,	atpic_attach),
60332580301SAttilio Rao 	DEVMETHOD(device_detach,	bus_generic_detach),
60432580301SAttilio Rao 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
60532580301SAttilio Rao 	DEVMETHOD(device_suspend,	bus_generic_suspend),
60632580301SAttilio Rao 	DEVMETHOD(device_resume,	bus_generic_resume),
60732580301SAttilio Rao 	{ 0, 0 }
60832580301SAttilio Rao };
60932580301SAttilio Rao 
61032580301SAttilio Rao static driver_t atpic_driver = {
61132580301SAttilio Rao 	"atpic",
61232580301SAttilio Rao 	atpic_methods,
61332580301SAttilio Rao 	1,		/* no softc */
61432580301SAttilio Rao };
61532580301SAttilio Rao 
616*80d2b3deSJohn Baldwin DRIVER_MODULE(atpic, isa, atpic_driver, 0, 0);
617*80d2b3deSJohn Baldwin DRIVER_MODULE(atpic, acpi, atpic_driver, 0, 0);
618d6b66397SWarner Losh ISA_PNP_INFO(atpic_ids);
61932580301SAttilio Rao #endif /* DEV_ISA */
620