xref: /freebsd/sys/x86/isa/atpic.c (revision 638b29fe651e9a2454d6d73f0af0aec0d208c5d0)
132580301SAttilio Rao /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
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 #include "opt_auto_eoi.h"
3432580301SAttilio Rao #include "opt_isa.h"
3532580301SAttilio Rao 
3632580301SAttilio Rao #include <sys/param.h>
3732580301SAttilio Rao #include <sys/systm.h>
38f115c061SMark Johnston #include <sys/asan.h>
3932580301SAttilio Rao #include <sys/bus.h>
4032580301SAttilio Rao #include <sys/interrupt.h>
4132580301SAttilio Rao #include <sys/kernel.h>
4232580301SAttilio Rao #include <sys/lock.h>
4332580301SAttilio Rao #include <sys/module.h>
44b0f71f1bSMark Johnston #include <sys/msan.h>
4532580301SAttilio Rao 
4632580301SAttilio Rao #include <machine/cpufunc.h>
4732580301SAttilio Rao #include <machine/frame.h>
4832580301SAttilio Rao #include <machine/intr_machdep.h>
4932580301SAttilio Rao #include <machine/md_var.h>
5032580301SAttilio Rao #include <machine/resource.h>
5132580301SAttilio Rao #include <machine/segments.h>
5232580301SAttilio Rao 
5332580301SAttilio Rao #include <dev/ic/i8259.h>
5432580301SAttilio Rao #include <x86/isa/icu.h>
55f79309d2SWarner Losh #include <isa/isareg.h>
5632580301SAttilio Rao #include <isa/isavar.h>
5732580301SAttilio Rao 
5832580301SAttilio Rao #ifdef __amd64__
5932580301SAttilio Rao #define	SDT_ATPIC	SDT_SYSIGT
6032580301SAttilio Rao #define	GSEL_ATPIC	0
6132580301SAttilio Rao #else
6232580301SAttilio Rao #define	SDT_ATPIC	SDT_SYS386IGT
6332580301SAttilio Rao #define	GSEL_ATPIC	GSEL(GCODE_SEL, SEL_KPL)
6432580301SAttilio Rao #endif
6532580301SAttilio Rao 
6632580301SAttilio Rao #define	MASTER	0
6732580301SAttilio Rao #define	SLAVE	1
6832580301SAttilio Rao 
6962a08214SJohn Baldwin #define	IMEN_MASK(ai)		(IRQ_MASK((ai)->at_irq))
7062a08214SJohn Baldwin 
7132580301SAttilio Rao #define	NUM_ISA_IRQS		16
7232580301SAttilio Rao 
7332580301SAttilio Rao static void	atpic_init(void *dummy);
7432580301SAttilio Rao 
7532580301SAttilio Rao inthand_t
7632580301SAttilio Rao 	IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2),
7732580301SAttilio Rao 	IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5),
7832580301SAttilio Rao 	IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8),
7932580301SAttilio Rao 	IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11),
8032580301SAttilio Rao 	IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14),
8132580301SAttilio Rao 	IDTVEC(atpic_intr15);
82bd50262fSKonstantin Belousov /* XXXKIB i386 uses stubs until pti comes */
83bd50262fSKonstantin Belousov inthand_t
84bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr0_pti), IDTVEC(atpic_intr1_pti),
85bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr2_pti), IDTVEC(atpic_intr3_pti),
86bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr4_pti), IDTVEC(atpic_intr5_pti),
87bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr6_pti), IDTVEC(atpic_intr7_pti),
88bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr8_pti), IDTVEC(atpic_intr9_pti),
89bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr10_pti), IDTVEC(atpic_intr11_pti),
90bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr12_pti), IDTVEC(atpic_intr13_pti),
91bd50262fSKonstantin Belousov 	IDTVEC(atpic_intr14_pti), IDTVEC(atpic_intr15_pti);
9232580301SAttilio Rao 
9332580301SAttilio Rao #define	IRQ(ap, ai)	((ap)->at_irqbase + (ai)->at_irq)
9432580301SAttilio Rao 
9562a08214SJohn Baldwin #define	ATPIC(io, base, eoi) {						\
9662a08214SJohn Baldwin 		.at_pic = {						\
97fd036deaSJohn Baldwin 			.pic_register_sources = atpic_register_sources,	\
9862a08214SJohn Baldwin 			.pic_enable_source = atpic_enable_source,	\
9962a08214SJohn Baldwin 			.pic_disable_source = atpic_disable_source,	\
10062a08214SJohn Baldwin 			.pic_eoi_source = (eoi),			\
10162a08214SJohn Baldwin 			.pic_enable_intr = atpic_enable_intr,		\
10262a08214SJohn Baldwin 			.pic_disable_intr = atpic_disable_intr,		\
10362a08214SJohn Baldwin 			.pic_vector = atpic_vector,			\
10462a08214SJohn Baldwin 			.pic_source_pending = atpic_source_pending,	\
10562a08214SJohn Baldwin 			.pic_resume = atpic_resume,			\
10662a08214SJohn Baldwin 			.pic_config_intr = atpic_config_intr,		\
10762a08214SJohn Baldwin 			.pic_assign_cpu = atpic_assign_cpu		\
10862a08214SJohn Baldwin 		},							\
10962a08214SJohn Baldwin 		.at_ioaddr = (io),					\
11062a08214SJohn Baldwin 		.at_irqbase = (base),					\
11162a08214SJohn Baldwin 		.at_intbase = IDT_IO_INTS + (base),			\
11262a08214SJohn Baldwin 		.at_imen = 0xff,					\
11362a08214SJohn Baldwin 	}
11432580301SAttilio Rao 
11532580301SAttilio Rao #define	INTSRC(irq)							\
116*638b29feSElliott Mitchell 	{								\
117*638b29feSElliott Mitchell 		.at_intsrc = { &atpics[(irq) / 8].at_pic },		\
118*638b29feSElliott Mitchell 		.at_intr = IDTVEC(atpic_intr ## irq ),			\
119*638b29feSElliott Mitchell 		.at_intr_pti = IDTVEC(atpic_intr ## irq ## _pti),	\
120*638b29feSElliott Mitchell 		.at_irq = (irq) % 8,					\
121*638b29feSElliott Mitchell 	}
12232580301SAttilio Rao 
12332580301SAttilio Rao struct atpic {
12432580301SAttilio Rao 	struct pic at_pic;
12532580301SAttilio Rao 	int	at_ioaddr;
12632580301SAttilio Rao 	int	at_irqbase;
12732580301SAttilio Rao 	uint8_t	at_intbase;
12862a08214SJohn Baldwin 	uint8_t	at_imen;
12932580301SAttilio Rao };
13032580301SAttilio Rao 
13132580301SAttilio Rao struct atpic_intsrc {
13232580301SAttilio Rao 	struct intsrc at_intsrc;
133bd50262fSKonstantin Belousov 	inthand_t *at_intr, *at_intr_pti;
13432580301SAttilio Rao 	int	at_irq;			/* Relative to PIC base. */
13532580301SAttilio Rao 	enum intr_trigger at_trigger;
13632580301SAttilio Rao 	u_long	at_count;
13732580301SAttilio Rao 	u_long	at_straycount;
13832580301SAttilio Rao };
13932580301SAttilio Rao 
140fd036deaSJohn Baldwin static void atpic_register_sources(struct pic *pic);
14132580301SAttilio Rao static void atpic_enable_source(struct intsrc *isrc);
14232580301SAttilio Rao static void atpic_disable_source(struct intsrc *isrc, int eoi);
14332580301SAttilio Rao static void atpic_eoi_master(struct intsrc *isrc);
14432580301SAttilio Rao static void atpic_eoi_slave(struct intsrc *isrc);
14532580301SAttilio Rao static void atpic_enable_intr(struct intsrc *isrc);
14632580301SAttilio Rao static void atpic_disable_intr(struct intsrc *isrc);
14732580301SAttilio Rao static int atpic_vector(struct intsrc *isrc);
148428b7ca2SJustin T. Gibbs static void atpic_resume(struct pic *pic, bool suspend_cancelled);
14932580301SAttilio Rao static int atpic_source_pending(struct intsrc *isrc);
15032580301SAttilio Rao static int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
15132580301SAttilio Rao     enum intr_polarity pol);
15232580301SAttilio Rao static int atpic_assign_cpu(struct intsrc *isrc, u_int apic_id);
15332580301SAttilio Rao static void i8259_init(struct atpic *pic, int slave);
15432580301SAttilio Rao 
15532580301SAttilio Rao static struct atpic atpics[] = {
15662a08214SJohn Baldwin 	ATPIC(IO_ICU1, 0, atpic_eoi_master),
15762a08214SJohn Baldwin 	ATPIC(IO_ICU2, 8, atpic_eoi_slave)
15832580301SAttilio Rao };
15932580301SAttilio Rao 
16032580301SAttilio Rao static struct atpic_intsrc atintrs[] = {
16132580301SAttilio Rao 	INTSRC(0),
16232580301SAttilio Rao 	INTSRC(1),
16332580301SAttilio Rao 	INTSRC(2),
16432580301SAttilio Rao 	INTSRC(3),
16532580301SAttilio Rao 	INTSRC(4),
16632580301SAttilio Rao 	INTSRC(5),
16732580301SAttilio Rao 	INTSRC(6),
16832580301SAttilio Rao 	INTSRC(7),
16932580301SAttilio Rao 	INTSRC(8),
17032580301SAttilio Rao 	INTSRC(9),
17132580301SAttilio Rao 	INTSRC(10),
17232580301SAttilio Rao 	INTSRC(11),
17332580301SAttilio Rao 	INTSRC(12),
17432580301SAttilio Rao 	INTSRC(13),
17532580301SAttilio Rao 	INTSRC(14),
17632580301SAttilio Rao 	INTSRC(15),
17732580301SAttilio Rao };
17832580301SAttilio Rao 
179ea24b056SPedro F. Giffuni CTASSERT(nitems(atintrs) == NUM_ISA_IRQS);
18032580301SAttilio Rao 
18132580301SAttilio Rao static __inline void
_atpic_eoi_master(struct intsrc * isrc)18232580301SAttilio Rao _atpic_eoi_master(struct intsrc *isrc)
18332580301SAttilio Rao {
18432580301SAttilio Rao 
18532580301SAttilio Rao 	KASSERT(isrc->is_pic == &atpics[MASTER].at_pic,
18632580301SAttilio Rao 	    ("%s: mismatched pic", __func__));
18732580301SAttilio Rao #ifndef AUTO_EOI_1
18832580301SAttilio Rao 	outb(atpics[MASTER].at_ioaddr, OCW2_EOI);
18932580301SAttilio Rao #endif
19032580301SAttilio Rao }
19132580301SAttilio Rao 
19232580301SAttilio Rao /*
19332580301SAttilio Rao  * The data sheet says no auto-EOI on slave, but it sometimes works.
19432580301SAttilio Rao  * So, if AUTO_EOI_2 is enabled, we use it.
19532580301SAttilio Rao  */
19632580301SAttilio Rao static __inline void
_atpic_eoi_slave(struct intsrc * isrc)19732580301SAttilio Rao _atpic_eoi_slave(struct intsrc *isrc)
19832580301SAttilio Rao {
19932580301SAttilio Rao 
20032580301SAttilio Rao 	KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic,
20132580301SAttilio Rao 	    ("%s: mismatched pic", __func__));
20232580301SAttilio Rao #ifndef AUTO_EOI_2
20332580301SAttilio Rao 	outb(atpics[SLAVE].at_ioaddr, OCW2_EOI);
20432580301SAttilio Rao #ifndef AUTO_EOI_1
20532580301SAttilio Rao 	outb(atpics[MASTER].at_ioaddr, OCW2_EOI);
20632580301SAttilio Rao #endif
20732580301SAttilio Rao #endif
20832580301SAttilio Rao }
20932580301SAttilio Rao 
21032580301SAttilio Rao static void
atpic_register_sources(struct pic * pic)211fd036deaSJohn Baldwin atpic_register_sources(struct pic *pic)
212fd036deaSJohn Baldwin {
213fd036deaSJohn Baldwin 	struct atpic *ap = (struct atpic *)pic;
214fd036deaSJohn Baldwin 	struct atpic_intsrc *ai;
215fd036deaSJohn Baldwin 	int i;
216fd036deaSJohn Baldwin 
217fd036deaSJohn Baldwin 	/*
218fd036deaSJohn Baldwin 	 * If any of the ISA IRQs have an interrupt source already, then
219fd036deaSJohn Baldwin 	 * assume that the I/O APICs are being used and don't register any
220fd036deaSJohn Baldwin 	 * of our interrupt sources.  This makes sure we don't accidentally
221fd036deaSJohn Baldwin 	 * use mixed mode.  The "accidental" use could otherwise occur on
222fd036deaSJohn Baldwin 	 * machines that route the ACPI SCI interrupt to a different ISA
223fd036deaSJohn Baldwin 	 * IRQ (at least one machine routes it to IRQ 13) thus disabling
224fd036deaSJohn Baldwin 	 * that APIC ISA routing and allowing the ATPIC source for that IRQ
225fd036deaSJohn Baldwin 	 * to leak through.  We used to depend on this feature for routing
226fd036deaSJohn Baldwin 	 * IRQ0 via mixed mode, but now we don't use mixed mode at all.
22787bdca82SJohn Baldwin 	 *
22887bdca82SJohn Baldwin 	 * To avoid the slave not register sources after the master
22987bdca82SJohn Baldwin 	 * registers its sources, register all IRQs when this function is
23087bdca82SJohn Baldwin 	 * called on the master.
231fd036deaSJohn Baldwin 	 */
23287bdca82SJohn Baldwin 	if (ap != &atpics[MASTER])
23387bdca82SJohn Baldwin 		return;
234fd036deaSJohn Baldwin 	for (i = 0; i < NUM_ISA_IRQS; i++)
235fd036deaSJohn Baldwin 		if (intr_lookup_source(i) != NULL)
236fd036deaSJohn Baldwin 			return;
237fd036deaSJohn Baldwin 
238fd036deaSJohn Baldwin 	/* Loop through all interrupt sources and add them. */
23987bdca82SJohn Baldwin 	for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
24087bdca82SJohn Baldwin 		if (i == ICU_SLAVEID)
241fd036deaSJohn Baldwin 			continue;
242fd036deaSJohn Baldwin 		intr_register_source(&ai->at_intsrc);
243fd036deaSJohn Baldwin 	}
244fd036deaSJohn Baldwin }
245fd036deaSJohn Baldwin 
246fd036deaSJohn Baldwin static void
atpic_enable_source(struct intsrc * isrc)24732580301SAttilio Rao atpic_enable_source(struct intsrc *isrc)
24832580301SAttilio Rao {
24932580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
25032580301SAttilio Rao 	struct atpic *ap = (struct atpic *)isrc->is_pic;
25132580301SAttilio Rao 
25232580301SAttilio Rao 	spinlock_enter();
25362a08214SJohn Baldwin 	if (ap->at_imen & IMEN_MASK(ai)) {
25462a08214SJohn Baldwin 		ap->at_imen &= ~IMEN_MASK(ai);
25562a08214SJohn Baldwin 		outb(ap->at_ioaddr + ICU_IMR_OFFSET, ap->at_imen);
25632580301SAttilio Rao 	}
25732580301SAttilio Rao 	spinlock_exit();
25832580301SAttilio Rao }
25932580301SAttilio Rao 
26032580301SAttilio Rao static void
atpic_disable_source(struct intsrc * isrc,int eoi)26132580301SAttilio Rao atpic_disable_source(struct intsrc *isrc, int eoi)
26232580301SAttilio Rao {
26332580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
26432580301SAttilio Rao 	struct atpic *ap = (struct atpic *)isrc->is_pic;
26532580301SAttilio Rao 
26632580301SAttilio Rao 	spinlock_enter();
26732580301SAttilio Rao 	if (ai->at_trigger != INTR_TRIGGER_EDGE) {
26862a08214SJohn Baldwin 		ap->at_imen |= IMEN_MASK(ai);
26962a08214SJohn Baldwin 		outb(ap->at_ioaddr + ICU_IMR_OFFSET, ap->at_imen);
27032580301SAttilio Rao 	}
27132580301SAttilio Rao 
27232580301SAttilio Rao 	/*
27332580301SAttilio Rao 	 * Take care to call these functions directly instead of through
27432580301SAttilio Rao 	 * a function pointer.  All of the referenced variables should
27532580301SAttilio Rao 	 * still be hot in the cache.
27632580301SAttilio Rao 	 */
27732580301SAttilio Rao 	if (eoi == PIC_EOI) {
27832580301SAttilio Rao 		if (isrc->is_pic == &atpics[MASTER].at_pic)
27932580301SAttilio Rao 			_atpic_eoi_master(isrc);
28032580301SAttilio Rao 		else
28132580301SAttilio Rao 			_atpic_eoi_slave(isrc);
28232580301SAttilio Rao 	}
28332580301SAttilio Rao 
28432580301SAttilio Rao 	spinlock_exit();
28532580301SAttilio Rao }
28632580301SAttilio Rao 
28732580301SAttilio Rao static void
atpic_eoi_master(struct intsrc * isrc)28832580301SAttilio Rao atpic_eoi_master(struct intsrc *isrc)
28932580301SAttilio Rao {
29032580301SAttilio Rao #ifndef AUTO_EOI_1
29132580301SAttilio Rao 	spinlock_enter();
29232580301SAttilio Rao 	_atpic_eoi_master(isrc);
29332580301SAttilio Rao 	spinlock_exit();
29432580301SAttilio Rao #endif
29532580301SAttilio Rao }
29632580301SAttilio Rao 
29732580301SAttilio Rao static void
atpic_eoi_slave(struct intsrc * isrc)29832580301SAttilio Rao atpic_eoi_slave(struct intsrc *isrc)
29932580301SAttilio Rao {
30032580301SAttilio Rao #ifndef AUTO_EOI_2
30132580301SAttilio Rao 	spinlock_enter();
30232580301SAttilio Rao 	_atpic_eoi_slave(isrc);
30332580301SAttilio Rao 	spinlock_exit();
30432580301SAttilio Rao #endif
30532580301SAttilio Rao }
30632580301SAttilio Rao 
30732580301SAttilio Rao static void
atpic_enable_intr(struct intsrc * isrc)30832580301SAttilio Rao atpic_enable_intr(struct intsrc *isrc)
30932580301SAttilio Rao {
31032580301SAttilio Rao }
31132580301SAttilio Rao 
31232580301SAttilio Rao static void
atpic_disable_intr(struct intsrc * isrc)31332580301SAttilio Rao atpic_disable_intr(struct intsrc *isrc)
31432580301SAttilio Rao {
31532580301SAttilio Rao }
31632580301SAttilio Rao 
31732580301SAttilio Rao static int
atpic_vector(struct intsrc * isrc)31832580301SAttilio Rao atpic_vector(struct intsrc *isrc)
31932580301SAttilio Rao {
32032580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
32132580301SAttilio Rao 	struct atpic *ap = (struct atpic *)isrc->is_pic;
32232580301SAttilio Rao 
32332580301SAttilio Rao 	return (IRQ(ap, ai));
32432580301SAttilio Rao }
32532580301SAttilio Rao 
32632580301SAttilio Rao static int
atpic_source_pending(struct intsrc * isrc)32732580301SAttilio Rao atpic_source_pending(struct intsrc *isrc)
32832580301SAttilio Rao {
32932580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
33032580301SAttilio Rao 	struct atpic *ap = (struct atpic *)isrc->is_pic;
33132580301SAttilio Rao 
33232580301SAttilio Rao 	return (inb(ap->at_ioaddr) & IMEN_MASK(ai));
33332580301SAttilio Rao }
33432580301SAttilio Rao 
33532580301SAttilio Rao static void
atpic_resume(struct pic * pic,bool suspend_cancelled)336428b7ca2SJustin T. Gibbs atpic_resume(struct pic *pic, bool suspend_cancelled)
33732580301SAttilio Rao {
33832580301SAttilio Rao 	struct atpic *ap = (struct atpic *)pic;
33932580301SAttilio Rao 
34032580301SAttilio Rao 	i8259_init(ap, ap == &atpics[SLAVE]);
34132580301SAttilio Rao 	if (ap == &atpics[SLAVE] && elcr_found)
34232580301SAttilio Rao 		elcr_resume();
34332580301SAttilio Rao }
34432580301SAttilio Rao 
34532580301SAttilio Rao static int
atpic_config_intr(struct intsrc * isrc,enum intr_trigger trig,enum intr_polarity pol)34632580301SAttilio Rao atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
34732580301SAttilio Rao     enum intr_polarity pol)
34832580301SAttilio Rao {
34932580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
35032580301SAttilio Rao 	u_int vector;
35132580301SAttilio Rao 
35232580301SAttilio Rao 	/* Map conforming values to edge/hi and sanity check the values. */
35332580301SAttilio Rao 	if (trig == INTR_TRIGGER_CONFORM)
35432580301SAttilio Rao 		trig = INTR_TRIGGER_EDGE;
35532580301SAttilio Rao 	if (pol == INTR_POLARITY_CONFORM)
35632580301SAttilio Rao 		pol = INTR_POLARITY_HIGH;
35732580301SAttilio Rao 	vector = atpic_vector(isrc);
35832580301SAttilio Rao 	if ((trig == INTR_TRIGGER_EDGE && pol == INTR_POLARITY_LOW) ||
35932580301SAttilio Rao 	    (trig == INTR_TRIGGER_LEVEL && pol == INTR_POLARITY_HIGH)) {
36032580301SAttilio Rao 		printf(
36132580301SAttilio Rao 		"atpic: Mismatched config for IRQ%u: trigger %s, polarity %s\n",
36232580301SAttilio Rao 		    vector, trig == INTR_TRIGGER_EDGE ? "edge" : "level",
36332580301SAttilio Rao 		    pol == INTR_POLARITY_HIGH ? "high" : "low");
36432580301SAttilio Rao 		return (EINVAL);
36532580301SAttilio Rao 	}
36632580301SAttilio Rao 
36732580301SAttilio Rao 	/* If there is no change, just return. */
36832580301SAttilio Rao 	if (ai->at_trigger == trig)
36932580301SAttilio Rao 		return (0);
37032580301SAttilio Rao 
37132580301SAttilio Rao 	/*
37232580301SAttilio Rao 	 * Certain IRQs can never be level/lo, so don't try to set them
37332580301SAttilio Rao 	 * that way if asked.  At least some ELCR registers ignore setting
37432580301SAttilio Rao 	 * these bits as well.
37532580301SAttilio Rao 	 */
37632580301SAttilio Rao 	if ((vector == 0 || vector == 1 || vector == 2 || vector == 13) &&
37732580301SAttilio Rao 	    trig == INTR_TRIGGER_LEVEL) {
37832580301SAttilio Rao 		if (bootverbose)
37932580301SAttilio Rao 			printf(
38032580301SAttilio Rao 		"atpic: Ignoring invalid level/low configuration for IRQ%u\n",
38132580301SAttilio Rao 			    vector);
38232580301SAttilio Rao 		return (EINVAL);
38332580301SAttilio Rao 	}
38432580301SAttilio Rao 	if (!elcr_found) {
38532580301SAttilio Rao 		if (bootverbose)
38632580301SAttilio Rao 			printf("atpic: No ELCR to configure IRQ%u as %s\n",
38732580301SAttilio Rao 			    vector, trig == INTR_TRIGGER_EDGE ? "edge/high" :
38832580301SAttilio Rao 			    "level/low");
38932580301SAttilio Rao 		return (ENXIO);
39032580301SAttilio Rao 	}
39132580301SAttilio Rao 	if (bootverbose)
39232580301SAttilio Rao 		printf("atpic: Programming IRQ%u as %s\n", vector,
39332580301SAttilio Rao 		    trig == INTR_TRIGGER_EDGE ? "edge/high" : "level/low");
39432580301SAttilio Rao 	spinlock_enter();
39532580301SAttilio Rao 	elcr_write_trigger(atpic_vector(isrc), trig);
39632580301SAttilio Rao 	ai->at_trigger = trig;
39732580301SAttilio Rao 	spinlock_exit();
39832580301SAttilio Rao 	return (0);
39932580301SAttilio Rao }
40032580301SAttilio Rao 
40132580301SAttilio Rao static int
atpic_assign_cpu(struct intsrc * isrc,u_int apic_id)40232580301SAttilio Rao atpic_assign_cpu(struct intsrc *isrc, u_int apic_id)
40332580301SAttilio Rao {
40432580301SAttilio Rao 
40532580301SAttilio Rao 	/*
40632580301SAttilio Rao 	 * 8259A's are only used in UP in which case all interrupts always
40732580301SAttilio Rao 	 * go to the sole CPU and this function shouldn't even be called.
40832580301SAttilio Rao 	 */
40932580301SAttilio Rao 	panic("%s: bad cookie", __func__);
41032580301SAttilio Rao }
41132580301SAttilio Rao 
41232580301SAttilio Rao static void
i8259_init(struct atpic * pic,int slave)41332580301SAttilio Rao i8259_init(struct atpic *pic, int slave)
41432580301SAttilio Rao {
41532580301SAttilio Rao 	int imr_addr;
41632580301SAttilio Rao 
41732580301SAttilio Rao 	/* Reset the PIC and program with next four bytes. */
41832580301SAttilio Rao 	spinlock_enter();
41932580301SAttilio Rao 	outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
42032580301SAttilio Rao 	imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
42132580301SAttilio Rao 
42232580301SAttilio Rao 	/* Start vector. */
42332580301SAttilio Rao 	outb(imr_addr, pic->at_intbase);
42432580301SAttilio Rao 
42532580301SAttilio Rao 	/*
42632580301SAttilio Rao 	 * Setup slave links.  For the master pic, indicate what line
42732580301SAttilio Rao 	 * the slave is configured on.  For the slave indicate
42832580301SAttilio Rao 	 * which line on the master we are connected to.
42932580301SAttilio Rao 	 */
43032580301SAttilio Rao 	if (slave)
43132580301SAttilio Rao 		outb(imr_addr, ICU_SLAVEID);
43232580301SAttilio Rao 	else
43332580301SAttilio Rao 		outb(imr_addr, IRQ_MASK(ICU_SLAVEID));
43432580301SAttilio Rao 
43532580301SAttilio Rao 	/* Set mode. */
43632580301SAttilio Rao 	if (slave)
43732580301SAttilio Rao 		outb(imr_addr, SLAVE_MODE);
43832580301SAttilio Rao 	else
43932580301SAttilio Rao 		outb(imr_addr, MASTER_MODE);
44032580301SAttilio Rao 
44132580301SAttilio Rao 	/* Set interrupt enable mask. */
44262a08214SJohn Baldwin 	outb(imr_addr, pic->at_imen);
44332580301SAttilio Rao 
44432580301SAttilio Rao 	/* Reset is finished, default to IRR on read. */
44532580301SAttilio Rao 	outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
44632580301SAttilio Rao 
44732580301SAttilio Rao 	/* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
44832580301SAttilio Rao 	if (!slave)
44932580301SAttilio Rao 		outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
4502b375b4eSYoshihiro Takahashi 
45132580301SAttilio Rao 	spinlock_exit();
45232580301SAttilio Rao }
45332580301SAttilio Rao 
45432580301SAttilio Rao void
atpic_startup(void)45532580301SAttilio Rao atpic_startup(void)
45632580301SAttilio Rao {
45732580301SAttilio Rao 	struct atpic_intsrc *ai;
45832580301SAttilio Rao 	int i;
45932580301SAttilio Rao 
46032580301SAttilio Rao 	/* Start off with all interrupts disabled. */
46132580301SAttilio Rao 	i8259_init(&atpics[MASTER], 0);
46232580301SAttilio Rao 	i8259_init(&atpics[SLAVE], 1);
46332580301SAttilio Rao 	atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
46432580301SAttilio Rao 
46532580301SAttilio Rao 	/* Install low-level interrupt handlers for all of our IRQs. */
46632580301SAttilio Rao 	for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
46732580301SAttilio Rao 		if (i == ICU_SLAVEID)
46832580301SAttilio Rao 			continue;
46932580301SAttilio Rao 		ai->at_intsrc.is_count = &ai->at_count;
47032580301SAttilio Rao 		ai->at_intsrc.is_straycount = &ai->at_straycount;
47132580301SAttilio Rao 		setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
472bd50262fSKonstantin Belousov 		    ai->at_irq, pti ? ai->at_intr_pti : ai->at_intr, SDT_ATPIC,
473bd50262fSKonstantin Belousov 		    SEL_KPL, GSEL_ATPIC);
47432580301SAttilio Rao 	}
47532580301SAttilio Rao 
47632580301SAttilio Rao 	/*
47732580301SAttilio Rao 	 * Look for an ELCR.  If we find one, update the trigger modes.
47832580301SAttilio Rao 	 * If we don't find one, assume that IRQs 0, 1, 2, and 13 are
47932580301SAttilio Rao 	 * edge triggered and that everything else is level triggered.
48032580301SAttilio Rao 	 * We only use the trigger information to reprogram the ELCR if
48132580301SAttilio Rao 	 * we have one and as an optimization to avoid masking edge
48232580301SAttilio Rao 	 * triggered interrupts.  For the case that we don't have an ELCR,
48332580301SAttilio Rao 	 * it doesn't hurt to mask an edge triggered interrupt, so we
48432580301SAttilio Rao 	 * assume level trigger for any interrupt that we aren't sure is
48532580301SAttilio Rao 	 * edge triggered.
48632580301SAttilio Rao 	 */
48732580301SAttilio Rao 	if (elcr_found) {
48832580301SAttilio Rao 		for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
48932580301SAttilio Rao 			ai->at_trigger = elcr_read_trigger(i);
49032580301SAttilio Rao 	} else {
49132580301SAttilio Rao 		for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
49232580301SAttilio Rao 			switch (i) {
49332580301SAttilio Rao 			case 0:
49432580301SAttilio Rao 			case 1:
49532580301SAttilio Rao 			case 2:
49632580301SAttilio Rao 			case 8:
49732580301SAttilio Rao 			case 13:
49832580301SAttilio Rao 				ai->at_trigger = INTR_TRIGGER_EDGE;
49932580301SAttilio Rao 				break;
50032580301SAttilio Rao 			default:
50132580301SAttilio Rao 				ai->at_trigger = INTR_TRIGGER_LEVEL;
50232580301SAttilio Rao 				break;
50332580301SAttilio Rao 			}
50432580301SAttilio Rao 	}
50532580301SAttilio Rao }
50632580301SAttilio Rao 
50732580301SAttilio Rao static void
atpic_init(void * dummy __unused)50832580301SAttilio Rao atpic_init(void *dummy __unused)
50932580301SAttilio Rao {
51032580301SAttilio Rao 
51132580301SAttilio Rao 	/*
51232580301SAttilio Rao 	 * Register our PICs, even if we aren't going to use any of their
51332580301SAttilio Rao 	 * pins so that they are suspended and resumed.
51432580301SAttilio Rao 	 */
51532580301SAttilio Rao 	if (intr_register_pic(&atpics[0].at_pic) != 0 ||
51632580301SAttilio Rao 	    intr_register_pic(&atpics[1].at_pic) != 0)
51732580301SAttilio Rao 		panic("Unable to register ATPICs");
51832580301SAttilio Rao 
519fd036deaSJohn Baldwin 	if (num_io_irqs == 0)
520fd036deaSJohn Baldwin 		num_io_irqs = NUM_ISA_IRQS;
52132580301SAttilio Rao }
522e68c8d7fSRoger Pau Monné SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_FOURTH, atpic_init, NULL);
52332580301SAttilio Rao 
52432580301SAttilio Rao void
atpic_handle_intr(u_int vector,struct trapframe * frame)52532580301SAttilio Rao atpic_handle_intr(u_int vector, struct trapframe *frame)
52632580301SAttilio Rao {
52732580301SAttilio Rao 	struct intsrc *isrc;
52832580301SAttilio Rao 
529f115c061SMark Johnston 	kasan_mark(frame, sizeof(*frame), sizeof(*frame), 0);
530b0f71f1bSMark Johnston 	kmsan_mark(frame, sizeof(*frame), KMSAN_STATE_INITED);
531fd25c622SKonstantin Belousov 	trap_check_kstack();
532f115c061SMark Johnston 
53332580301SAttilio Rao 	KASSERT(vector < NUM_ISA_IRQS, ("unknown int %u\n", vector));
53432580301SAttilio Rao 	isrc = &atintrs[vector].at_intsrc;
53532580301SAttilio Rao 
53632580301SAttilio Rao 	/*
53732580301SAttilio Rao 	 * If we don't have an event, see if this is a spurious
53832580301SAttilio Rao 	 * interrupt.
53932580301SAttilio Rao 	 */
54032580301SAttilio Rao 	if (isrc->is_event == NULL && (vector == 7 || vector == 15)) {
54132580301SAttilio Rao 		int port, isr;
54232580301SAttilio Rao 
54332580301SAttilio Rao 		/*
54432580301SAttilio Rao 		 * Read the ISR register to see if IRQ 7/15 is really
54532580301SAttilio Rao 		 * pending.  Reset read register back to IRR when done.
54632580301SAttilio Rao 		 */
54732580301SAttilio Rao 		port = ((struct atpic *)isrc->is_pic)->at_ioaddr;
54832580301SAttilio Rao 		spinlock_enter();
54932580301SAttilio Rao 		outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS);
55032580301SAttilio Rao 		isr = inb(port);
55132580301SAttilio Rao 		outb(port, OCW3_SEL | OCW3_RR);
55232580301SAttilio Rao 		spinlock_exit();
55332580301SAttilio Rao 		if ((isr & IRQ_MASK(7)) == 0)
55432580301SAttilio Rao 			return;
55532580301SAttilio Rao 	}
55632580301SAttilio Rao 	intr_execute_handlers(isrc, frame);
55732580301SAttilio Rao }
55832580301SAttilio Rao 
55932580301SAttilio Rao #ifdef DEV_ISA
56032580301SAttilio Rao /*
56132580301SAttilio Rao  * Bus attachment for the ISA PIC.
56232580301SAttilio Rao  */
56332580301SAttilio Rao static struct isa_pnp_id atpic_ids[] = {
56432580301SAttilio Rao 	{ 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
56532580301SAttilio Rao 	{ 0 }
56632580301SAttilio Rao };
56732580301SAttilio Rao 
56832580301SAttilio Rao static int
atpic_probe(device_t dev)56932580301SAttilio Rao atpic_probe(device_t dev)
57032580301SAttilio Rao {
57132580301SAttilio Rao 	int result;
57232580301SAttilio Rao 
57332580301SAttilio Rao 	result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
57432580301SAttilio Rao 	if (result <= 0)
57532580301SAttilio Rao 		device_quiet(dev);
57632580301SAttilio Rao 	return (result);
57732580301SAttilio Rao }
57832580301SAttilio Rao 
57932580301SAttilio Rao /*
58032580301SAttilio Rao  * We might be granted IRQ 2, as this is typically consumed by chaining
58132580301SAttilio Rao  * between the two PIC components.  If we're using the APIC, however,
58232580301SAttilio Rao  * this may not be the case, and as such we should free the resource.
58332580301SAttilio Rao  * (XXX untested)
58432580301SAttilio Rao  *
58532580301SAttilio Rao  * The generic ISA attachment code will handle allocating any other resources
58632580301SAttilio Rao  * that we don't explicitly claim here.
58732580301SAttilio Rao  */
58832580301SAttilio Rao static int
atpic_attach(device_t dev)58932580301SAttilio Rao atpic_attach(device_t dev)
59032580301SAttilio Rao {
59132580301SAttilio Rao 	struct resource *res;
59232580301SAttilio Rao 	int rid;
59332580301SAttilio Rao 
59432580301SAttilio Rao 	/* Try to allocate our IRQ and then free it. */
59532580301SAttilio Rao 	rid = 0;
59632580301SAttilio Rao 	res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 0);
59732580301SAttilio Rao 	if (res != NULL)
59832580301SAttilio Rao 		bus_release_resource(dev, SYS_RES_IRQ, rid, res);
59932580301SAttilio Rao 	return (0);
60032580301SAttilio Rao }
60132580301SAttilio Rao 
60232580301SAttilio Rao static device_method_t atpic_methods[] = {
60332580301SAttilio Rao 	/* Device interface */
60432580301SAttilio Rao 	DEVMETHOD(device_probe,		atpic_probe),
60532580301SAttilio Rao 	DEVMETHOD(device_attach,	atpic_attach),
60632580301SAttilio Rao 	{ 0, 0 }
60732580301SAttilio Rao };
60832580301SAttilio Rao 
60932580301SAttilio Rao static driver_t atpic_driver = {
61032580301SAttilio Rao 	"atpic",
61132580301SAttilio Rao 	atpic_methods,
61232580301SAttilio Rao 	1,		/* no softc */
61332580301SAttilio Rao };
61432580301SAttilio Rao 
61580d2b3deSJohn Baldwin DRIVER_MODULE(atpic, isa, atpic_driver, 0, 0);
61680d2b3deSJohn Baldwin DRIVER_MODULE(atpic, acpi, atpic_driver, 0, 0);
617d6b66397SWarner Losh ISA_PNP_INFO(atpic_ids);
61832580301SAttilio Rao #endif /* DEV_ISA */
619