xref: /freebsd/sys/x86/isa/atpic.c (revision ea24b0561f961d0a1bccb37b85a43f3c9a3e851a)
132580301SAttilio Rao /*-
232580301SAttilio Rao  * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
332580301SAttilio Rao  * All rights reserved.
432580301SAttilio Rao  *
532580301SAttilio Rao  * Redistribution and use in source and binary forms, with or without
632580301SAttilio Rao  * modification, are permitted provided that the following conditions
732580301SAttilio Rao  * are met:
832580301SAttilio Rao  * 1. Redistributions of source code must retain the above copyright
932580301SAttilio Rao  *    notice, this list of conditions and the following disclaimer.
1032580301SAttilio Rao  * 2. Redistributions in binary form must reproduce the above copyright
1132580301SAttilio Rao  *    notice, this list of conditions and the following disclaimer in the
1232580301SAttilio Rao  *    documentation and/or other materials provided with the distribution.
1332580301SAttilio Rao  *
1432580301SAttilio Rao  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1532580301SAttilio Rao  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1632580301SAttilio Rao  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1732580301SAttilio Rao  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1832580301SAttilio Rao  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1932580301SAttilio Rao  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2032580301SAttilio Rao  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2132580301SAttilio Rao  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2232580301SAttilio Rao  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2332580301SAttilio Rao  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2432580301SAttilio Rao  * SUCH DAMAGE.
2532580301SAttilio Rao  */
2632580301SAttilio Rao 
2732580301SAttilio Rao /*
2832580301SAttilio Rao  * PIC driver for the 8259A Master and Slave PICs in PC/AT machines.
2932580301SAttilio Rao  */
3032580301SAttilio Rao 
3132580301SAttilio Rao #include <sys/cdefs.h>
3232580301SAttilio Rao __FBSDID("$FreeBSD$");
3332580301SAttilio Rao 
3432580301SAttilio Rao #include "opt_auto_eoi.h"
3532580301SAttilio Rao #include "opt_isa.h"
368e9b1703SWarner Losh #include "opt_mca.h"
3732580301SAttilio Rao 
3832580301SAttilio Rao #include <sys/param.h>
3932580301SAttilio Rao #include <sys/systm.h>
4032580301SAttilio Rao #include <sys/bus.h>
4132580301SAttilio Rao #include <sys/interrupt.h>
4232580301SAttilio Rao #include <sys/kernel.h>
4332580301SAttilio Rao #include <sys/lock.h>
4432580301SAttilio Rao #include <sys/module.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>
5532580301SAttilio Rao #ifdef PC98
5632580301SAttilio Rao #include <pc98/cbus/cbus.h>
5732580301SAttilio Rao #else
58f79309d2SWarner Losh #include <isa/isareg.h>
5932580301SAttilio Rao #endif
6032580301SAttilio Rao #include <isa/isavar.h>
61a66412fcSWarner Losh #ifdef DEV_MCA
62a66412fcSWarner Losh #include <i386/bios/mca_machdep.h>
63a66412fcSWarner Losh #endif
6432580301SAttilio Rao 
6532580301SAttilio Rao #ifdef __amd64__
6632580301SAttilio Rao #define	SDT_ATPIC	SDT_SYSIGT
6732580301SAttilio Rao #define	GSEL_ATPIC	0
6832580301SAttilio Rao #else
6932580301SAttilio Rao #define	SDT_ATPIC	SDT_SYS386IGT
7032580301SAttilio Rao #define	GSEL_ATPIC	GSEL(GCODE_SEL, SEL_KPL)
7132580301SAttilio Rao #endif
7232580301SAttilio Rao 
7332580301SAttilio Rao #define	MASTER	0
7432580301SAttilio Rao #define	SLAVE	1
7532580301SAttilio Rao 
7632580301SAttilio Rao #define	NUM_ISA_IRQS		16
7732580301SAttilio Rao 
7832580301SAttilio Rao static void	atpic_init(void *dummy);
7932580301SAttilio Rao 
8032580301SAttilio Rao unsigned int imen;	/* XXX */
8132580301SAttilio Rao 
8232580301SAttilio Rao inthand_t
8332580301SAttilio Rao 	IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2),
8432580301SAttilio Rao 	IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5),
8532580301SAttilio Rao 	IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8),
8632580301SAttilio Rao 	IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11),
8732580301SAttilio Rao 	IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14),
8832580301SAttilio Rao 	IDTVEC(atpic_intr15);
8932580301SAttilio Rao 
9032580301SAttilio Rao #define	IRQ(ap, ai)	((ap)->at_irqbase + (ai)->at_irq)
9132580301SAttilio Rao 
9232580301SAttilio Rao #define	ATPIC(io, base, eoi, imenptr)					\
9332580301SAttilio Rao      	{ { atpic_enable_source, atpic_disable_source, (eoi),		\
9432580301SAttilio Rao 	    atpic_enable_intr, atpic_disable_intr, atpic_vector,	\
9532580301SAttilio Rao 	    atpic_source_pending, NULL,	atpic_resume, atpic_config_intr,\
9632580301SAttilio Rao 	    atpic_assign_cpu }, (io), (base), IDT_IO_INTS + (base),	\
9732580301SAttilio Rao 	    (imenptr) }
9832580301SAttilio Rao 
9932580301SAttilio Rao #define	INTSRC(irq)							\
10032580301SAttilio Rao 	{ { &atpics[(irq) / 8].at_pic }, IDTVEC(atpic_intr ## irq ),	\
10132580301SAttilio Rao 	    (irq) % 8 }
10232580301SAttilio Rao 
10332580301SAttilio Rao struct atpic {
10432580301SAttilio Rao 	struct pic at_pic;
10532580301SAttilio Rao 	int	at_ioaddr;
10632580301SAttilio Rao 	int	at_irqbase;
10732580301SAttilio Rao 	uint8_t	at_intbase;
10832580301SAttilio Rao 	uint8_t	*at_imen;
10932580301SAttilio Rao };
11032580301SAttilio Rao 
11132580301SAttilio Rao struct atpic_intsrc {
11232580301SAttilio Rao 	struct intsrc at_intsrc;
11332580301SAttilio Rao 	inthand_t *at_intr;
11432580301SAttilio Rao 	int	at_irq;			/* Relative to PIC base. */
11532580301SAttilio Rao 	enum intr_trigger at_trigger;
11632580301SAttilio Rao 	u_long	at_count;
11732580301SAttilio Rao 	u_long	at_straycount;
11832580301SAttilio Rao };
11932580301SAttilio Rao 
12032580301SAttilio Rao static void atpic_enable_source(struct intsrc *isrc);
12132580301SAttilio Rao static void atpic_disable_source(struct intsrc *isrc, int eoi);
12232580301SAttilio Rao static void atpic_eoi_master(struct intsrc *isrc);
12332580301SAttilio Rao static void atpic_eoi_slave(struct intsrc *isrc);
12432580301SAttilio Rao static void atpic_enable_intr(struct intsrc *isrc);
12532580301SAttilio Rao static void atpic_disable_intr(struct intsrc *isrc);
12632580301SAttilio Rao static int atpic_vector(struct intsrc *isrc);
127428b7ca2SJustin T. Gibbs static void atpic_resume(struct pic *pic, bool suspend_cancelled);
12832580301SAttilio Rao static int atpic_source_pending(struct intsrc *isrc);
12932580301SAttilio Rao static int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
13032580301SAttilio Rao     enum intr_polarity pol);
13132580301SAttilio Rao static int atpic_assign_cpu(struct intsrc *isrc, u_int apic_id);
13232580301SAttilio Rao static void i8259_init(struct atpic *pic, int slave);
13332580301SAttilio Rao 
13432580301SAttilio Rao static struct atpic atpics[] = {
13532580301SAttilio Rao 	ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen),
13632580301SAttilio Rao 	ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1)
13732580301SAttilio Rao };
13832580301SAttilio Rao 
13932580301SAttilio Rao static struct atpic_intsrc atintrs[] = {
14032580301SAttilio Rao 	INTSRC(0),
14132580301SAttilio Rao 	INTSRC(1),
14232580301SAttilio Rao 	INTSRC(2),
14332580301SAttilio Rao 	INTSRC(3),
14432580301SAttilio Rao 	INTSRC(4),
14532580301SAttilio Rao 	INTSRC(5),
14632580301SAttilio Rao 	INTSRC(6),
14732580301SAttilio Rao 	INTSRC(7),
14832580301SAttilio Rao 	INTSRC(8),
14932580301SAttilio Rao 	INTSRC(9),
15032580301SAttilio Rao 	INTSRC(10),
15132580301SAttilio Rao 	INTSRC(11),
15232580301SAttilio Rao 	INTSRC(12),
15332580301SAttilio Rao 	INTSRC(13),
15432580301SAttilio Rao 	INTSRC(14),
15532580301SAttilio Rao 	INTSRC(15),
15632580301SAttilio Rao };
15732580301SAttilio Rao 
158*ea24b056SPedro F. Giffuni CTASSERT(nitems(atintrs) == NUM_ISA_IRQS);
15932580301SAttilio Rao 
16032580301SAttilio Rao static __inline void
16132580301SAttilio Rao _atpic_eoi_master(struct intsrc *isrc)
16232580301SAttilio Rao {
16332580301SAttilio Rao 
16432580301SAttilio Rao 	KASSERT(isrc->is_pic == &atpics[MASTER].at_pic,
16532580301SAttilio Rao 	    ("%s: mismatched pic", __func__));
16632580301SAttilio Rao #ifndef AUTO_EOI_1
16732580301SAttilio Rao 	outb(atpics[MASTER].at_ioaddr, OCW2_EOI);
16832580301SAttilio Rao #endif
16932580301SAttilio Rao }
17032580301SAttilio Rao 
17132580301SAttilio Rao /*
17232580301SAttilio Rao  * The data sheet says no auto-EOI on slave, but it sometimes works.
17332580301SAttilio Rao  * So, if AUTO_EOI_2 is enabled, we use it.
17432580301SAttilio Rao  */
17532580301SAttilio Rao static __inline void
17632580301SAttilio Rao _atpic_eoi_slave(struct intsrc *isrc)
17732580301SAttilio Rao {
17832580301SAttilio Rao 
17932580301SAttilio Rao 	KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic,
18032580301SAttilio Rao 	    ("%s: mismatched pic", __func__));
18132580301SAttilio Rao #ifndef AUTO_EOI_2
18232580301SAttilio Rao 	outb(atpics[SLAVE].at_ioaddr, OCW2_EOI);
18332580301SAttilio Rao #ifndef AUTO_EOI_1
18432580301SAttilio Rao 	outb(atpics[MASTER].at_ioaddr, OCW2_EOI);
18532580301SAttilio Rao #endif
18632580301SAttilio Rao #endif
18732580301SAttilio Rao }
18832580301SAttilio Rao 
18932580301SAttilio Rao static void
19032580301SAttilio Rao atpic_enable_source(struct intsrc *isrc)
19132580301SAttilio Rao {
19232580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
19332580301SAttilio Rao 	struct atpic *ap = (struct atpic *)isrc->is_pic;
19432580301SAttilio Rao 
19532580301SAttilio Rao 	spinlock_enter();
19632580301SAttilio Rao 	if (*ap->at_imen & IMEN_MASK(ai)) {
19732580301SAttilio Rao 		*ap->at_imen &= ~IMEN_MASK(ai);
19832580301SAttilio Rao 		outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
19932580301SAttilio Rao 	}
20032580301SAttilio Rao 	spinlock_exit();
20132580301SAttilio Rao }
20232580301SAttilio Rao 
20332580301SAttilio Rao static void
20432580301SAttilio Rao atpic_disable_source(struct intsrc *isrc, int eoi)
20532580301SAttilio Rao {
20632580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
20732580301SAttilio Rao 	struct atpic *ap = (struct atpic *)isrc->is_pic;
20832580301SAttilio Rao 
20932580301SAttilio Rao 	spinlock_enter();
21032580301SAttilio Rao 	if (ai->at_trigger != INTR_TRIGGER_EDGE) {
21132580301SAttilio Rao 		*ap->at_imen |= IMEN_MASK(ai);
21232580301SAttilio Rao 		outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
21332580301SAttilio Rao 	}
21432580301SAttilio Rao 
21532580301SAttilio Rao 	/*
21632580301SAttilio Rao 	 * Take care to call these functions directly instead of through
21732580301SAttilio Rao 	 * a function pointer.  All of the referenced variables should
21832580301SAttilio Rao 	 * still be hot in the cache.
21932580301SAttilio Rao 	 */
22032580301SAttilio Rao 	if (eoi == PIC_EOI) {
22132580301SAttilio Rao 		if (isrc->is_pic == &atpics[MASTER].at_pic)
22232580301SAttilio Rao 			_atpic_eoi_master(isrc);
22332580301SAttilio Rao 		else
22432580301SAttilio Rao 			_atpic_eoi_slave(isrc);
22532580301SAttilio Rao 	}
22632580301SAttilio Rao 
22732580301SAttilio Rao 	spinlock_exit();
22832580301SAttilio Rao }
22932580301SAttilio Rao 
23032580301SAttilio Rao static void
23132580301SAttilio Rao atpic_eoi_master(struct intsrc *isrc)
23232580301SAttilio Rao {
23332580301SAttilio Rao #ifndef AUTO_EOI_1
23432580301SAttilio Rao 	spinlock_enter();
23532580301SAttilio Rao 	_atpic_eoi_master(isrc);
23632580301SAttilio Rao 	spinlock_exit();
23732580301SAttilio Rao #endif
23832580301SAttilio Rao }
23932580301SAttilio Rao 
24032580301SAttilio Rao static void
24132580301SAttilio Rao atpic_eoi_slave(struct intsrc *isrc)
24232580301SAttilio Rao {
24332580301SAttilio Rao #ifndef AUTO_EOI_2
24432580301SAttilio Rao 	spinlock_enter();
24532580301SAttilio Rao 	_atpic_eoi_slave(isrc);
24632580301SAttilio Rao 	spinlock_exit();
24732580301SAttilio Rao #endif
24832580301SAttilio Rao }
24932580301SAttilio Rao 
25032580301SAttilio Rao static void
25132580301SAttilio Rao atpic_enable_intr(struct intsrc *isrc)
25232580301SAttilio Rao {
25332580301SAttilio Rao }
25432580301SAttilio Rao 
25532580301SAttilio Rao static void
25632580301SAttilio Rao atpic_disable_intr(struct intsrc *isrc)
25732580301SAttilio Rao {
25832580301SAttilio Rao }
25932580301SAttilio Rao 
26032580301SAttilio Rao 
26132580301SAttilio Rao static int
26232580301SAttilio Rao atpic_vector(struct intsrc *isrc)
26332580301SAttilio Rao {
26432580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
26532580301SAttilio Rao 	struct atpic *ap = (struct atpic *)isrc->is_pic;
26632580301SAttilio Rao 
26732580301SAttilio Rao 	return (IRQ(ap, ai));
26832580301SAttilio Rao }
26932580301SAttilio Rao 
27032580301SAttilio Rao static int
27132580301SAttilio Rao atpic_source_pending(struct intsrc *isrc)
27232580301SAttilio Rao {
27332580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
27432580301SAttilio Rao 	struct atpic *ap = (struct atpic *)isrc->is_pic;
27532580301SAttilio Rao 
27632580301SAttilio Rao 	return (inb(ap->at_ioaddr) & IMEN_MASK(ai));
27732580301SAttilio Rao }
27832580301SAttilio Rao 
27932580301SAttilio Rao static void
280428b7ca2SJustin T. Gibbs atpic_resume(struct pic *pic, bool suspend_cancelled)
28132580301SAttilio Rao {
28232580301SAttilio Rao 	struct atpic *ap = (struct atpic *)pic;
28332580301SAttilio Rao 
28432580301SAttilio Rao 	i8259_init(ap, ap == &atpics[SLAVE]);
28532580301SAttilio Rao #ifndef PC98
28632580301SAttilio Rao 	if (ap == &atpics[SLAVE] && elcr_found)
28732580301SAttilio Rao 		elcr_resume();
28832580301SAttilio Rao #endif
28932580301SAttilio Rao }
29032580301SAttilio Rao 
29132580301SAttilio Rao static int
29232580301SAttilio Rao atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
29332580301SAttilio Rao     enum intr_polarity pol)
29432580301SAttilio Rao {
29532580301SAttilio Rao 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
29632580301SAttilio Rao 	u_int vector;
29732580301SAttilio Rao 
29832580301SAttilio Rao 	/* Map conforming values to edge/hi and sanity check the values. */
29932580301SAttilio Rao 	if (trig == INTR_TRIGGER_CONFORM)
30032580301SAttilio Rao 		trig = INTR_TRIGGER_EDGE;
30132580301SAttilio Rao 	if (pol == INTR_POLARITY_CONFORM)
30232580301SAttilio Rao 		pol = INTR_POLARITY_HIGH;
30332580301SAttilio Rao 	vector = atpic_vector(isrc);
30432580301SAttilio Rao 	if ((trig == INTR_TRIGGER_EDGE && pol == INTR_POLARITY_LOW) ||
30532580301SAttilio Rao 	    (trig == INTR_TRIGGER_LEVEL && pol == INTR_POLARITY_HIGH)) {
30632580301SAttilio Rao 		printf(
30732580301SAttilio Rao 		"atpic: Mismatched config for IRQ%u: trigger %s, polarity %s\n",
30832580301SAttilio Rao 		    vector, trig == INTR_TRIGGER_EDGE ? "edge" : "level",
30932580301SAttilio Rao 		    pol == INTR_POLARITY_HIGH ? "high" : "low");
31032580301SAttilio Rao 		return (EINVAL);
31132580301SAttilio Rao 	}
31232580301SAttilio Rao 
31332580301SAttilio Rao 	/* If there is no change, just return. */
31432580301SAttilio Rao 	if (ai->at_trigger == trig)
31532580301SAttilio Rao 		return (0);
31632580301SAttilio Rao 
31732580301SAttilio Rao #ifdef PC98
31832580301SAttilio Rao 	if ((vector == 0 || vector == 1 || vector == 7 || vector == 8) &&
31932580301SAttilio Rao 	    trig == INTR_TRIGGER_LEVEL) {
32032580301SAttilio Rao 		if (bootverbose)
32132580301SAttilio Rao 			printf(
32232580301SAttilio Rao 		"atpic: Ignoring invalid level/low configuration for IRQ%u\n",
32332580301SAttilio Rao 			    vector);
32432580301SAttilio Rao 		return (EINVAL);
32532580301SAttilio Rao 	}
32632580301SAttilio Rao 	return (ENXIO);
32732580301SAttilio Rao #else
32832580301SAttilio Rao 	/*
32932580301SAttilio Rao 	 * Certain IRQs can never be level/lo, so don't try to set them
33032580301SAttilio Rao 	 * that way if asked.  At least some ELCR registers ignore setting
33132580301SAttilio Rao 	 * these bits as well.
33232580301SAttilio Rao 	 */
33332580301SAttilio Rao 	if ((vector == 0 || vector == 1 || vector == 2 || vector == 13) &&
33432580301SAttilio Rao 	    trig == INTR_TRIGGER_LEVEL) {
33532580301SAttilio Rao 		if (bootverbose)
33632580301SAttilio Rao 			printf(
33732580301SAttilio Rao 		"atpic: Ignoring invalid level/low configuration for IRQ%u\n",
33832580301SAttilio Rao 			    vector);
33932580301SAttilio Rao 		return (EINVAL);
34032580301SAttilio Rao 	}
34132580301SAttilio Rao 	if (!elcr_found) {
34232580301SAttilio Rao 		if (bootverbose)
34332580301SAttilio Rao 			printf("atpic: No ELCR to configure IRQ%u as %s\n",
34432580301SAttilio Rao 			    vector, trig == INTR_TRIGGER_EDGE ? "edge/high" :
34532580301SAttilio Rao 			    "level/low");
34632580301SAttilio Rao 		return (ENXIO);
34732580301SAttilio Rao 	}
34832580301SAttilio Rao 	if (bootverbose)
34932580301SAttilio Rao 		printf("atpic: Programming IRQ%u as %s\n", vector,
35032580301SAttilio Rao 		    trig == INTR_TRIGGER_EDGE ? "edge/high" : "level/low");
35132580301SAttilio Rao 	spinlock_enter();
35232580301SAttilio Rao 	elcr_write_trigger(atpic_vector(isrc), trig);
35332580301SAttilio Rao 	ai->at_trigger = trig;
35432580301SAttilio Rao 	spinlock_exit();
35532580301SAttilio Rao 	return (0);
35632580301SAttilio Rao #endif /* PC98 */
35732580301SAttilio Rao }
35832580301SAttilio Rao 
35932580301SAttilio Rao static int
36032580301SAttilio Rao atpic_assign_cpu(struct intsrc *isrc, u_int apic_id)
36132580301SAttilio Rao {
36232580301SAttilio Rao 
36332580301SAttilio Rao 	/*
36432580301SAttilio Rao 	 * 8259A's are only used in UP in which case all interrupts always
36532580301SAttilio Rao 	 * go to the sole CPU and this function shouldn't even be called.
36632580301SAttilio Rao 	 */
36732580301SAttilio Rao 	panic("%s: bad cookie", __func__);
36832580301SAttilio Rao }
36932580301SAttilio Rao 
37032580301SAttilio Rao static void
37132580301SAttilio Rao i8259_init(struct atpic *pic, int slave)
37232580301SAttilio Rao {
37332580301SAttilio Rao 	int imr_addr;
37432580301SAttilio Rao 
37532580301SAttilio Rao 	/* Reset the PIC and program with next four bytes. */
37632580301SAttilio Rao 	spinlock_enter();
37732580301SAttilio Rao #ifdef DEV_MCA
37832580301SAttilio Rao 	/* MCA uses level triggered interrupts. */
37932580301SAttilio Rao 	if (MCA_system)
38032580301SAttilio Rao 		outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM);
38132580301SAttilio Rao 	else
38232580301SAttilio Rao #endif
38332580301SAttilio Rao 		outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
38432580301SAttilio Rao 	imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
38532580301SAttilio Rao 
38632580301SAttilio Rao 	/* Start vector. */
38732580301SAttilio Rao 	outb(imr_addr, pic->at_intbase);
38832580301SAttilio Rao 
38932580301SAttilio Rao 	/*
39032580301SAttilio Rao 	 * Setup slave links.  For the master pic, indicate what line
39132580301SAttilio Rao 	 * the slave is configured on.  For the slave indicate
39232580301SAttilio Rao 	 * which line on the master we are connected to.
39332580301SAttilio Rao 	 */
39432580301SAttilio Rao 	if (slave)
39532580301SAttilio Rao 		outb(imr_addr, ICU_SLAVEID);
39632580301SAttilio Rao 	else
39732580301SAttilio Rao 		outb(imr_addr, IRQ_MASK(ICU_SLAVEID));
39832580301SAttilio Rao 
39932580301SAttilio Rao 	/* Set mode. */
40032580301SAttilio Rao 	if (slave)
40132580301SAttilio Rao 		outb(imr_addr, SLAVE_MODE);
40232580301SAttilio Rao 	else
40332580301SAttilio Rao 		outb(imr_addr, MASTER_MODE);
40432580301SAttilio Rao 
40532580301SAttilio Rao 	/* Set interrupt enable mask. */
40632580301SAttilio Rao 	outb(imr_addr, *pic->at_imen);
40732580301SAttilio Rao 
40832580301SAttilio Rao 	/* Reset is finished, default to IRR on read. */
40932580301SAttilio Rao 	outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
41032580301SAttilio Rao 
41132580301SAttilio Rao #ifndef PC98
41232580301SAttilio Rao 	/* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
41332580301SAttilio Rao 	if (!slave)
41432580301SAttilio Rao 		outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
41532580301SAttilio Rao #endif
41632580301SAttilio Rao 	spinlock_exit();
41732580301SAttilio Rao }
41832580301SAttilio Rao 
41932580301SAttilio Rao void
42032580301SAttilio Rao atpic_startup(void)
42132580301SAttilio Rao {
42232580301SAttilio Rao 	struct atpic_intsrc *ai;
42332580301SAttilio Rao 	int i;
42432580301SAttilio Rao 
42532580301SAttilio Rao 	/* Start off with all interrupts disabled. */
42632580301SAttilio Rao 	imen = 0xffff;
42732580301SAttilio Rao 	i8259_init(&atpics[MASTER], 0);
42832580301SAttilio Rao 	i8259_init(&atpics[SLAVE], 1);
42932580301SAttilio Rao 	atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
43032580301SAttilio Rao 
43132580301SAttilio Rao 	/* Install low-level interrupt handlers for all of our IRQs. */
43232580301SAttilio Rao 	for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
43332580301SAttilio Rao 		if (i == ICU_SLAVEID)
43432580301SAttilio Rao 			continue;
43532580301SAttilio Rao 		ai->at_intsrc.is_count = &ai->at_count;
43632580301SAttilio Rao 		ai->at_intsrc.is_straycount = &ai->at_straycount;
43732580301SAttilio Rao 		setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
43832580301SAttilio Rao 		    ai->at_irq, ai->at_intr, SDT_ATPIC, SEL_KPL, GSEL_ATPIC);
43932580301SAttilio Rao 	}
44032580301SAttilio Rao 
44132580301SAttilio Rao #ifdef DEV_MCA
44232580301SAttilio Rao 	/* For MCA systems, all interrupts are level triggered. */
44332580301SAttilio Rao 	if (MCA_system)
44432580301SAttilio Rao 		for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
44532580301SAttilio Rao 			ai->at_trigger = INTR_TRIGGER_LEVEL;
44632580301SAttilio Rao 	else
44732580301SAttilio Rao #endif
44832580301SAttilio Rao 
44932580301SAttilio Rao #ifdef PC98
45032580301SAttilio Rao 	for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
45132580301SAttilio Rao 		switch (i) {
45232580301SAttilio Rao 		case 0:
45332580301SAttilio Rao 		case 1:
45432580301SAttilio Rao 		case 7:
45532580301SAttilio Rao 		case 8:
45632580301SAttilio Rao 			ai->at_trigger = INTR_TRIGGER_EDGE;
45732580301SAttilio Rao 			break;
45832580301SAttilio Rao 		default:
45932580301SAttilio Rao 			ai->at_trigger = INTR_TRIGGER_LEVEL;
46032580301SAttilio Rao 			break;
46132580301SAttilio Rao 		}
46232580301SAttilio Rao #else
46332580301SAttilio Rao 	/*
46432580301SAttilio Rao 	 * Look for an ELCR.  If we find one, update the trigger modes.
46532580301SAttilio Rao 	 * If we don't find one, assume that IRQs 0, 1, 2, and 13 are
46632580301SAttilio Rao 	 * edge triggered and that everything else is level triggered.
46732580301SAttilio Rao 	 * We only use the trigger information to reprogram the ELCR if
46832580301SAttilio Rao 	 * we have one and as an optimization to avoid masking edge
46932580301SAttilio Rao 	 * triggered interrupts.  For the case that we don't have an ELCR,
47032580301SAttilio Rao 	 * it doesn't hurt to mask an edge triggered interrupt, so we
47132580301SAttilio Rao 	 * assume level trigger for any interrupt that we aren't sure is
47232580301SAttilio Rao 	 * edge triggered.
47332580301SAttilio Rao 	 */
47432580301SAttilio Rao 	if (elcr_found) {
47532580301SAttilio Rao 		for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
47632580301SAttilio Rao 			ai->at_trigger = elcr_read_trigger(i);
47732580301SAttilio Rao 	} else {
47832580301SAttilio Rao 		for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
47932580301SAttilio Rao 			switch (i) {
48032580301SAttilio Rao 			case 0:
48132580301SAttilio Rao 			case 1:
48232580301SAttilio Rao 			case 2:
48332580301SAttilio Rao 			case 8:
48432580301SAttilio Rao 			case 13:
48532580301SAttilio Rao 				ai->at_trigger = INTR_TRIGGER_EDGE;
48632580301SAttilio Rao 				break;
48732580301SAttilio Rao 			default:
48832580301SAttilio Rao 				ai->at_trigger = INTR_TRIGGER_LEVEL;
48932580301SAttilio Rao 				break;
49032580301SAttilio Rao 			}
49132580301SAttilio Rao 	}
49232580301SAttilio Rao #endif /* PC98 */
49332580301SAttilio Rao }
49432580301SAttilio Rao 
49532580301SAttilio Rao static void
49632580301SAttilio Rao atpic_init(void *dummy __unused)
49732580301SAttilio Rao {
49832580301SAttilio Rao 	struct atpic_intsrc *ai;
49932580301SAttilio Rao 	int i;
50032580301SAttilio Rao 
50132580301SAttilio Rao 	/*
50232580301SAttilio Rao 	 * Register our PICs, even if we aren't going to use any of their
50332580301SAttilio Rao 	 * pins so that they are suspended and resumed.
50432580301SAttilio Rao 	 */
50532580301SAttilio Rao 	if (intr_register_pic(&atpics[0].at_pic) != 0 ||
50632580301SAttilio Rao 	    intr_register_pic(&atpics[1].at_pic) != 0)
50732580301SAttilio Rao 		panic("Unable to register ATPICs");
50832580301SAttilio Rao 
50932580301SAttilio Rao 	/*
51032580301SAttilio Rao 	 * If any of the ISA IRQs have an interrupt source already, then
51132580301SAttilio Rao 	 * assume that the APICs are being used and don't register any
51232580301SAttilio Rao 	 * of our interrupt sources.  This makes sure we don't accidentally
51332580301SAttilio Rao 	 * use mixed mode.  The "accidental" use could otherwise occur on
51432580301SAttilio Rao 	 * machines that route the ACPI SCI interrupt to a different ISA
51532580301SAttilio Rao 	 * IRQ (at least one machines routes it to IRQ 13) thus disabling
51632580301SAttilio Rao 	 * that APIC ISA routing and allowing the ATPIC source for that IRQ
51732580301SAttilio Rao 	 * to leak through.  We used to depend on this feature for routing
51832580301SAttilio Rao 	 * IRQ0 via mixed mode, but now we don't use mixed mode at all.
51932580301SAttilio Rao 	 */
52032580301SAttilio Rao 	for (i = 0; i < NUM_ISA_IRQS; i++)
52132580301SAttilio Rao 		if (intr_lookup_source(i) != NULL)
52232580301SAttilio Rao 			return;
52332580301SAttilio Rao 
52432580301SAttilio Rao 	/* Loop through all interrupt sources and add them. */
52532580301SAttilio Rao 	for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
52632580301SAttilio Rao 		if (i == ICU_SLAVEID)
52732580301SAttilio Rao 			continue;
52832580301SAttilio Rao 		intr_register_source(&ai->at_intsrc);
52932580301SAttilio Rao 	}
53032580301SAttilio Rao }
531e68c8d7fSRoger Pau Monné SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_FOURTH, atpic_init, NULL);
53232580301SAttilio Rao 
53332580301SAttilio Rao void
53432580301SAttilio Rao atpic_handle_intr(u_int vector, struct trapframe *frame)
53532580301SAttilio Rao {
53632580301SAttilio Rao 	struct intsrc *isrc;
53732580301SAttilio Rao 
53832580301SAttilio Rao 	KASSERT(vector < NUM_ISA_IRQS, ("unknown int %u\n", vector));
53932580301SAttilio Rao 	isrc = &atintrs[vector].at_intsrc;
54032580301SAttilio Rao 
54132580301SAttilio Rao 	/*
54232580301SAttilio Rao 	 * If we don't have an event, see if this is a spurious
54332580301SAttilio Rao 	 * interrupt.
54432580301SAttilio Rao 	 */
54532580301SAttilio Rao 	if (isrc->is_event == NULL && (vector == 7 || vector == 15)) {
54632580301SAttilio Rao 		int port, isr;
54732580301SAttilio Rao 
54832580301SAttilio Rao 		/*
54932580301SAttilio Rao 		 * Read the ISR register to see if IRQ 7/15 is really
55032580301SAttilio Rao 		 * pending.  Reset read register back to IRR when done.
55132580301SAttilio Rao 		 */
55232580301SAttilio Rao 		port = ((struct atpic *)isrc->is_pic)->at_ioaddr;
55332580301SAttilio Rao 		spinlock_enter();
55432580301SAttilio Rao 		outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS);
55532580301SAttilio Rao 		isr = inb(port);
55632580301SAttilio Rao 		outb(port, OCW3_SEL | OCW3_RR);
55732580301SAttilio Rao 		spinlock_exit();
55832580301SAttilio Rao 		if ((isr & IRQ_MASK(7)) == 0)
55932580301SAttilio Rao 			return;
56032580301SAttilio Rao 	}
56132580301SAttilio Rao 	intr_execute_handlers(isrc, frame);
56232580301SAttilio Rao }
56332580301SAttilio Rao 
56432580301SAttilio Rao #ifdef DEV_ISA
56532580301SAttilio Rao /*
56632580301SAttilio Rao  * Bus attachment for the ISA PIC.
56732580301SAttilio Rao  */
56832580301SAttilio Rao static struct isa_pnp_id atpic_ids[] = {
56932580301SAttilio Rao 	{ 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
57032580301SAttilio Rao 	{ 0 }
57132580301SAttilio Rao };
57232580301SAttilio Rao 
57332580301SAttilio Rao static int
57432580301SAttilio Rao atpic_probe(device_t dev)
57532580301SAttilio Rao {
57632580301SAttilio Rao 	int result;
57732580301SAttilio Rao 
57832580301SAttilio Rao 	result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
57932580301SAttilio Rao 	if (result <= 0)
58032580301SAttilio Rao 		device_quiet(dev);
58132580301SAttilio Rao 	return (result);
58232580301SAttilio Rao }
58332580301SAttilio Rao 
58432580301SAttilio Rao /*
58532580301SAttilio Rao  * We might be granted IRQ 2, as this is typically consumed by chaining
58632580301SAttilio Rao  * between the two PIC components.  If we're using the APIC, however,
58732580301SAttilio Rao  * this may not be the case, and as such we should free the resource.
58832580301SAttilio Rao  * (XXX untested)
58932580301SAttilio Rao  *
59032580301SAttilio Rao  * The generic ISA attachment code will handle allocating any other resources
59132580301SAttilio Rao  * that we don't explicitly claim here.
59232580301SAttilio Rao  */
59332580301SAttilio Rao static int
59432580301SAttilio Rao atpic_attach(device_t dev)
59532580301SAttilio Rao {
59632580301SAttilio Rao 	struct resource *res;
59732580301SAttilio Rao 	int rid;
59832580301SAttilio Rao 
59932580301SAttilio Rao 	/* Try to allocate our IRQ and then free it. */
60032580301SAttilio Rao 	rid = 0;
60132580301SAttilio Rao 	res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 0);
60232580301SAttilio Rao 	if (res != NULL)
60332580301SAttilio Rao 		bus_release_resource(dev, SYS_RES_IRQ, rid, res);
60432580301SAttilio Rao 	return (0);
60532580301SAttilio Rao }
60632580301SAttilio Rao 
60732580301SAttilio Rao static device_method_t atpic_methods[] = {
60832580301SAttilio Rao 	/* Device interface */
60932580301SAttilio Rao 	DEVMETHOD(device_probe,		atpic_probe),
61032580301SAttilio Rao 	DEVMETHOD(device_attach,	atpic_attach),
61132580301SAttilio Rao 	DEVMETHOD(device_detach,	bus_generic_detach),
61232580301SAttilio Rao 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
61332580301SAttilio Rao 	DEVMETHOD(device_suspend,	bus_generic_suspend),
61432580301SAttilio Rao 	DEVMETHOD(device_resume,	bus_generic_resume),
61532580301SAttilio Rao 	{ 0, 0 }
61632580301SAttilio Rao };
61732580301SAttilio Rao 
61832580301SAttilio Rao static driver_t atpic_driver = {
61932580301SAttilio Rao 	"atpic",
62032580301SAttilio Rao 	atpic_methods,
62132580301SAttilio Rao 	1,		/* no softc */
62232580301SAttilio Rao };
62332580301SAttilio Rao 
62432580301SAttilio Rao static devclass_t atpic_devclass;
62532580301SAttilio Rao 
62632580301SAttilio Rao DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0);
62732580301SAttilio Rao #ifndef PC98
62832580301SAttilio Rao DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0);
62932580301SAttilio Rao #endif
63032580301SAttilio Rao 
63132580301SAttilio Rao /*
63232580301SAttilio Rao  * Return a bitmap of the current interrupt requests.  This is 8259-specific
63332580301SAttilio Rao  * and is only suitable for use at probe time.
63432580301SAttilio Rao  */
63532580301SAttilio Rao intrmask_t
63632580301SAttilio Rao isa_irq_pending(void)
63732580301SAttilio Rao {
63832580301SAttilio Rao 	u_char irr1;
63932580301SAttilio Rao 	u_char irr2;
64032580301SAttilio Rao 
64132580301SAttilio Rao 	irr1 = inb(IO_ICU1);
64232580301SAttilio Rao 	irr2 = inb(IO_ICU2);
64332580301SAttilio Rao 	return ((irr2 << 8) | irr1);
64432580301SAttilio Rao }
64532580301SAttilio Rao #endif /* DEV_ISA */
646