atpic.c revision 195249
1227006Smarius/*-
2227006Smarius * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
3227006Smarius * All rights reserved.
4227006Smarius *
5227006Smarius * Redistribution and use in source and binary forms, with or without
6227006Smarius * modification, are permitted provided that the following conditions
7227006Smarius * are met:
8227006Smarius * 1. Redistributions of source code must retain the above copyright
9227006Smarius *    notice, this list of conditions and the following disclaimer.
10227006Smarius * 2. Redistributions in binary form must reproduce the above copyright
11227006Smarius *    notice, this list of conditions and the following disclaimer in the
12227006Smarius *    documentation and/or other materials provided with the distribution.
13227006Smarius * 3. Neither the name of the author nor the names of any co-contributors
14227006Smarius *    may be used to endorse or promote products derived from this software
15227006Smarius *    without specific prior written permission.
16227006Smarius *
17227006Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18227006Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19227006Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20227006Smarius * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21227006Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22227006Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23227006Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24227006Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25227006Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26227006Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27227006Smarius * SUCH DAMAGE.
28227006Smarius */
29227006Smarius
30227006Smarius/*
31227006Smarius * PIC driver for the 8259A Master and Slave PICs in PC/AT machines.
32227006Smarius */
33227006Smarius
34227006Smarius#include <sys/cdefs.h>
35227006Smarius__FBSDID("$FreeBSD: head/sys/i386/isa/atpic.c 195249 2009-07-01 17:20:07Z jhb $");
36227006Smarius
37227006Smarius#include "opt_auto_eoi.h"
38227006Smarius#include "opt_isa.h"
39227006Smarius
40227006Smarius#include <sys/param.h>
41227006Smarius#include <sys/systm.h>
42227006Smarius#include <sys/bus.h>
43227006Smarius#include <sys/interrupt.h>
44227006Smarius#include <sys/kernel.h>
45227006Smarius#include <sys/lock.h>
46227006Smarius#include <sys/module.h>
47227006Smarius
48227006Smarius#include <machine/cpufunc.h>
49227006Smarius#include <machine/frame.h>
50227006Smarius#include <machine/intr_machdep.h>
51227006Smarius#include <machine/md_var.h>
52227006Smarius#include <machine/resource.h>
53227006Smarius#include <machine/segments.h>
54227006Smarius
55227006Smarius#include <dev/ic/i8259.h>
56227006Smarius#include <i386/isa/icu.h>
57227006Smarius#ifdef PC98
58227006Smarius#include <pc98/cbus/cbus.h>
59227006Smarius#else
60227006Smarius#include <i386/isa/isa.h>
61227006Smarius#endif
62227006Smarius#include <isa/isavar.h>
63227006Smarius
64227006Smarius#define	MASTER	0
65227006Smarius#define	SLAVE	1
66227006Smarius
67227006Smarius/*
68227006Smarius * PC-98 machines wire the slave 8259A to pin 7 on the master PIC, and
69227006Smarius * PC-AT machines wire the slave PIC to pin 2 on the master PIC.
70227006Smarius */
71227006Smarius#ifdef PC98
72227006Smarius#define	ICU_SLAVEID	7
73227006Smarius#else
74227006Smarius#define	ICU_SLAVEID	2
75227006Smarius#endif
76227006Smarius
77227006Smarius/*
78227006Smarius * Determine the base master and slave modes not including auto EOI support.
79227006Smarius * All machines that FreeBSD supports use 8086 mode.
80227006Smarius */
81227006Smarius#ifdef PC98
82227006Smarius/*
83227006Smarius * PC-98 machines do not support auto EOI on the second PIC.  Also, it
84227006Smarius * seems that PC-98 machine PICs use buffered mode, and the master PIC
85227006Smarius * uses special fully nested mode.
86227006Smarius */
87227006Smarius#define	BASE_MASTER_MODE	(ICW4_SFNM | ICW4_BUF | ICW4_MS | ICW4_8086)
88227006Smarius#define	BASE_SLAVE_MODE		(ICW4_BUF | ICW4_8086)
89227006Smarius#else
90227006Smarius#define	BASE_MASTER_MODE	ICW4_8086
91227006Smarius#define	BASE_SLAVE_MODE		ICW4_8086
92227006Smarius#endif
93227006Smarius
94227006Smarius/* Enable automatic EOI if requested. */
95227006Smarius#ifdef AUTO_EOI_1
96227006Smarius#define	MASTER_MODE		(BASE_MASTER_MODE | ICW4_AEOI)
97227006Smarius#else
98227006Smarius#define	MASTER_MODE		BASE_MASTER_MODE
99227006Smarius#endif
100227006Smarius#ifdef AUTO_EOI_2
101227006Smarius#define	SLAVE_MODE		(BASE_SLAVE_MODE | ICW4_AEOI)
102227006Smarius#else
103227006Smarius#define	SLAVE_MODE		BASE_SLAVE_MODE
104227006Smarius#endif
105227006Smarius
106227006Smarius#define	IRQ_MASK(irq)		(1 << (irq))
107227006Smarius#define	IMEN_MASK(ai)		(IRQ_MASK((ai)->at_irq))
108227006Smarius
109227006Smarius#define	NUM_ISA_IRQS		16
110227006Smarius
111227006Smariusstatic void	atpic_init(void *dummy);
112227006Smarius
113227006Smariusunsigned int imen;	/* XXX */
114227006Smarius
115227006Smariusinthand_t
116227006Smarius	IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2),
117227006Smarius	IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5),
118227006Smarius	IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8),
119227006Smarius	IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11),
120227006Smarius	IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14),
121227006Smarius	IDTVEC(atpic_intr15);
122227006Smarius
123227006Smarius#define	IRQ(ap, ai)	((ap)->at_irqbase + (ai)->at_irq)
124227006Smarius
125227006Smarius#define	ATPIC(io, base, eoi, imenptr)					\
126227006Smarius     	{ { atpic_enable_source, atpic_disable_source, (eoi),		\
127227006Smarius	    atpic_enable_intr, atpic_disable_intr, atpic_vector,	\
128227006Smarius	    atpic_source_pending, NULL,	atpic_resume, atpic_config_intr,\
129227006Smarius	    atpic_assign_cpu }, (io), (base), IDT_IO_INTS + (base),	\
130227006Smarius	    (imenptr) }
131227006Smarius
132227006Smarius#define	INTSRC(irq)							\
133227006Smarius	{ { &atpics[(irq) / 8].at_pic }, IDTVEC(atpic_intr ## irq ),	\
134227006Smarius	    (irq) % 8 }
135227006Smarius
136227006Smariusstruct atpic {
137227006Smarius	struct pic at_pic;
138227006Smarius	int	at_ioaddr;
139227006Smarius	int	at_irqbase;
140227006Smarius	uint8_t	at_intbase;
141227006Smarius	uint8_t	*at_imen;
142227006Smarius};
143227006Smarius
144227006Smariusstruct atpic_intsrc {
145227006Smarius	struct intsrc at_intsrc;
146227006Smarius	inthand_t *at_intr;
147227006Smarius	int	at_irq;			/* Relative to PIC base. */
148227006Smarius	enum intr_trigger at_trigger;
149227848Smarius	u_long	at_count;
150227006Smarius	u_long	at_straycount;
151227006Smarius};
152227006Smarius
153227006Smariusstatic void atpic_enable_source(struct intsrc *isrc);
154227006Smariusstatic void atpic_disable_source(struct intsrc *isrc, int eoi);
155227006Smariusstatic void atpic_eoi_master(struct intsrc *isrc);
156227006Smariusstatic void atpic_eoi_slave(struct intsrc *isrc);
157227006Smariusstatic void atpic_enable_intr(struct intsrc *isrc);
158227006Smariusstatic void atpic_disable_intr(struct intsrc *isrc);
159227006Smariusstatic int atpic_vector(struct intsrc *isrc);
160227006Smariusstatic void atpic_resume(struct pic *pic);
161227006Smariusstatic int atpic_source_pending(struct intsrc *isrc);
162227006Smariusstatic int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
163227006Smarius    enum intr_polarity pol);
164227006Smariusstatic int atpic_assign_cpu(struct intsrc *isrc, u_int apic_id);
165227006Smariusstatic void i8259_init(struct atpic *pic, int slave);
166227006Smarius
167227006Smariusstatic struct atpic atpics[] = {
168227006Smarius	ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen),
169227006Smarius	ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1)
170227006Smarius};
171227006Smarius
172227006Smariusstatic struct atpic_intsrc atintrs[] = {
173227006Smarius	INTSRC(0),
174227006Smarius	INTSRC(1),
175227006Smarius	INTSRC(2),
176227006Smarius	INTSRC(3),
177227006Smarius	INTSRC(4),
178227006Smarius	INTSRC(5),
179227006Smarius	INTSRC(6),
180227006Smarius	INTSRC(7),
181227006Smarius	INTSRC(8),
182227006Smarius	INTSRC(9),
183227006Smarius	INTSRC(10),
184227006Smarius	INTSRC(11),
185227006Smarius	INTSRC(12),
186227006Smarius	INTSRC(13),
187227006Smarius	INTSRC(14),
188227006Smarius	INTSRC(15),
189227006Smarius};
190227006Smarius
191227006SmariusCTASSERT(sizeof(atintrs) / sizeof(atintrs[0]) == NUM_ISA_IRQS);
192227006Smarius
193227006Smariusstatic __inline void
194227006Smarius_atpic_eoi_master(struct intsrc *isrc)
195227006Smarius{
196227006Smarius
197227006Smarius	KASSERT(isrc->is_pic == &atpics[MASTER].at_pic,
198227006Smarius	    ("%s: mismatched pic", __func__));
199227006Smarius#ifndef AUTO_EOI_1
200227006Smarius	outb(atpics[MASTER].at_ioaddr, OCW2_EOI);
201227006Smarius#endif
202227006Smarius}
203227006Smarius
204227006Smarius/*
205227006Smarius * The data sheet says no auto-EOI on slave, but it sometimes works.
206227006Smarius * So, if AUTO_EOI_2 is enabled, we use it.
207227006Smarius */
208227006Smariusstatic __inline void
209227006Smarius_atpic_eoi_slave(struct intsrc *isrc)
210227006Smarius{
211227006Smarius
212227006Smarius	KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic,
213227006Smarius	    ("%s: mismatched pic", __func__));
214227006Smarius#ifndef AUTO_EOI_2
215227006Smarius	outb(atpics[SLAVE].at_ioaddr, OCW2_EOI);
216227006Smarius#ifndef AUTO_EOI_1
217227006Smarius	outb(atpics[MASTER].at_ioaddr, OCW2_EOI);
218227006Smarius#endif
219227006Smarius#endif
220227006Smarius}
221227006Smarius
222227006Smariusstatic void
223227006Smariusatpic_enable_source(struct intsrc *isrc)
224227006Smarius{
225227006Smarius	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
226227006Smarius	struct atpic *ap = (struct atpic *)isrc->is_pic;
227227006Smarius
228227006Smarius	spinlock_enter();
229227006Smarius	if (*ap->at_imen & IMEN_MASK(ai)) {
230227006Smarius		*ap->at_imen &= ~IMEN_MASK(ai);
231227006Smarius		outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
232227006Smarius	}
233227006Smarius	spinlock_exit();
234227006Smarius}
235227006Smarius
236227006Smariusstatic void
237227006Smariusatpic_disable_source(struct intsrc *isrc, int eoi)
238227006Smarius{
239227006Smarius	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
240227006Smarius	struct atpic *ap = (struct atpic *)isrc->is_pic;
241227006Smarius
242227006Smarius	spinlock_enter();
243227006Smarius	if (ai->at_trigger != INTR_TRIGGER_EDGE) {
244227006Smarius		*ap->at_imen |= IMEN_MASK(ai);
245227006Smarius		outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
246227006Smarius	}
247227006Smarius
248227006Smarius	/*
249227006Smarius	 * Take care to call these functions directly instead of through
250227006Smarius	 * a function pointer.  All of the referenced variables should
251227006Smarius	 * still be hot in the cache.
252227006Smarius	 */
253227006Smarius	if (eoi == PIC_EOI) {
254227006Smarius		if (isrc->is_pic == &atpics[MASTER].at_pic)
255227006Smarius			_atpic_eoi_master(isrc);
256227006Smarius		else
257227006Smarius			_atpic_eoi_slave(isrc);
258227006Smarius	}
259227006Smarius
260227006Smarius	spinlock_exit();
261227006Smarius}
262227006Smarius
263227006Smariusstatic void
264227006Smariusatpic_eoi_master(struct intsrc *isrc)
265227006Smarius{
266227006Smarius#ifndef AUTO_EOI_1
267227006Smarius	spinlock_enter();
268227006Smarius	_atpic_eoi_master(isrc);
269227006Smarius	spinlock_exit();
270227006Smarius#endif
271227006Smarius}
272227006Smarius
273227006Smariusstatic void
274227006Smariusatpic_eoi_slave(struct intsrc *isrc)
275227006Smarius{
276227006Smarius#ifndef AUTO_EOI_2
277227006Smarius	spinlock_enter();
278227006Smarius	_atpic_eoi_slave(isrc);
279227006Smarius	spinlock_exit();
280227006Smarius#endif
281227006Smarius}
282227006Smarius
283227006Smariusstatic void
284227006Smariusatpic_enable_intr(struct intsrc *isrc)
285227006Smarius{
286227006Smarius}
287227006Smarius
288227006Smariusstatic void
289227006Smariusatpic_disable_intr(struct intsrc *isrc)
290227006Smarius{
291227006Smarius}
292227006Smarius
293227006Smarius
294227006Smariusstatic int
295298955Spfgatpic_vector(struct intsrc *isrc)
296227006Smarius{
297227006Smarius	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
298227006Smarius	struct atpic *ap = (struct atpic *)isrc->is_pic;
299227006Smarius
300227006Smarius	return (IRQ(ap, ai));
301227006Smarius}
302227006Smarius
303227006Smariusstatic int
304227006Smariusatpic_source_pending(struct intsrc *isrc)
305227006Smarius{
306227006Smarius	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
307227006Smarius	struct atpic *ap = (struct atpic *)isrc->is_pic;
308227006Smarius
309227006Smarius	return (inb(ap->at_ioaddr) & IMEN_MASK(ai));
310227006Smarius}
311227006Smarius
312227006Smariusstatic void
313227006Smariusatpic_resume(struct pic *pic)
314227006Smarius{
315227006Smarius	struct atpic *ap = (struct atpic *)pic;
316227006Smarius
317227006Smarius	i8259_init(ap, ap == &atpics[SLAVE]);
318227006Smarius#ifndef PC98
319227006Smarius	if (ap == &atpics[SLAVE] && elcr_found)
320227006Smarius		elcr_resume();
321227006Smarius#endif
322227006Smarius}
323227006Smarius
324227006Smariusstatic int
325227006Smariusatpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
326227006Smarius    enum intr_polarity pol)
327227006Smarius{
328227006Smarius	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
329227006Smarius	u_int vector;
330227006Smarius
331227006Smarius	/* Map conforming values to edge/hi and sanity check the values. */
332227006Smarius	if (trig == INTR_TRIGGER_CONFORM)
333227006Smarius		trig = INTR_TRIGGER_EDGE;
334227006Smarius	if (pol == INTR_POLARITY_CONFORM)
335227006Smarius		pol = INTR_POLARITY_HIGH;
336227006Smarius	vector = atpic_vector(isrc);
337227006Smarius	if ((trig == INTR_TRIGGER_EDGE && pol == INTR_POLARITY_LOW) ||
338227006Smarius	    (trig == INTR_TRIGGER_LEVEL && pol == INTR_POLARITY_HIGH)) {
339227006Smarius		printf(
340227006Smarius		"atpic: Mismatched config for IRQ%u: trigger %s, polarity %s\n",
341227006Smarius		    vector, trig == INTR_TRIGGER_EDGE ? "edge" : "level",
342227006Smarius		    pol == INTR_POLARITY_HIGH ? "high" : "low");
343227006Smarius		return (EINVAL);
344227006Smarius	}
345227006Smarius
346227006Smarius	/* If there is no change, just return. */
347227006Smarius	if (ai->at_trigger == trig)
348227006Smarius		return (0);
349227006Smarius
350227006Smarius#ifdef PC98
351227006Smarius	if ((vector == 0 || vector == 1 || vector == 7 || vector == 8) &&
352227006Smarius	    trig == INTR_TRIGGER_LEVEL) {
353227006Smarius		if (bootverbose)
354227006Smarius			printf(
355227006Smarius		"atpic: Ignoring invalid level/low configuration for IRQ%u\n",
356227006Smarius			    vector);
357227006Smarius		return (EINVAL);
358227006Smarius	}
359227006Smarius	return (ENXIO);
360227006Smarius#else
361227006Smarius	/*
362227006Smarius	 * Certain IRQs can never be level/lo, so don't try to set them
363227006Smarius	 * that way if asked.  At least some ELCR registers ignore setting
364227006Smarius	 * these bits as well.
365227006Smarius	 */
366227006Smarius	if ((vector == 0 || vector == 1 || vector == 2 || vector == 13) &&
367227006Smarius	    trig == INTR_TRIGGER_LEVEL) {
368227006Smarius		if (bootverbose)
369227006Smarius			printf(
370227006Smarius		"atpic: Ignoring invalid level/low configuration for IRQ%u\n",
371227006Smarius			    vector);
372227006Smarius		return (EINVAL);
373227006Smarius	}
374227006Smarius	if (!elcr_found) {
375227006Smarius		if (bootverbose)
376227006Smarius			printf("atpic: No ELCR to configure IRQ%u as %s\n",
377227006Smarius			    vector, trig == INTR_TRIGGER_EDGE ? "edge/high" :
378227006Smarius			    "level/low");
379227006Smarius		return (ENXIO);
380227006Smarius	}
381227006Smarius	if (bootverbose)
382227006Smarius		printf("atpic: Programming IRQ%u as %s\n", vector,
383227006Smarius		    trig == INTR_TRIGGER_EDGE ? "edge/high" : "level/low");
384227006Smarius	spinlock_enter();
385227006Smarius	elcr_write_trigger(atpic_vector(isrc), trig);
386227006Smarius	ai->at_trigger = trig;
387227006Smarius	spinlock_exit();
388227006Smarius	return (0);
389227006Smarius#endif /* PC98 */
390227006Smarius}
391227006Smarius
392227006Smariusstatic int
393227006Smariusatpic_assign_cpu(struct intsrc *isrc, u_int apic_id)
394227006Smarius{
395227006Smarius
396227006Smarius	/*
397227006Smarius	 * 8259A's are only used in UP in which case all interrupts always
398227006Smarius	 * go to the sole CPU and this function shouldn't even be called.
399227006Smarius	 */
400227006Smarius	panic("%s: bad cookie", __func__);
401227006Smarius}
402227006Smarius
403227006Smariusstatic void
404227006Smariusi8259_init(struct atpic *pic, int slave)
405227006Smarius{
406227006Smarius	int imr_addr;
407227006Smarius
408227006Smarius	/* Reset the PIC and program with next four bytes. */
409227006Smarius	spinlock_enter();
410227006Smarius#ifdef DEV_MCA
411227006Smarius	/* MCA uses level triggered interrupts. */
412227006Smarius	if (MCA_system)
413227006Smarius		outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM);
414227006Smarius	else
415227006Smarius#endif
416227006Smarius		outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
417227006Smarius	imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
418227006Smarius
419227006Smarius	/* Start vector. */
420227006Smarius	outb(imr_addr, pic->at_intbase);
421227006Smarius
422227006Smarius	/*
423227006Smarius	 * Setup slave links.  For the master pic, indicate what line
424227006Smarius	 * the slave is configured on.  For the slave indicate
425227006Smarius	 * which line on the master we are connected to.
426227006Smarius	 */
427227006Smarius	if (slave)
428227006Smarius		outb(imr_addr, ICU_SLAVEID);
429227006Smarius	else
430227006Smarius		outb(imr_addr, IRQ_MASK(ICU_SLAVEID));
431227006Smarius
432227006Smarius	/* Set mode. */
433227006Smarius	if (slave)
434227006Smarius		outb(imr_addr, SLAVE_MODE);
435227006Smarius	else
436227006Smarius		outb(imr_addr, MASTER_MODE);
437227006Smarius
438227006Smarius	/* Set interrupt enable mask. */
439227006Smarius	outb(imr_addr, *pic->at_imen);
440227006Smarius
441227006Smarius	/* Reset is finished, default to IRR on read. */
442227006Smarius	outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
443227006Smarius
444227006Smarius#ifndef PC98
445227006Smarius	/* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
446227006Smarius	if (!slave)
447227006Smarius		outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
448227006Smarius#endif
449227006Smarius	spinlock_exit();
450227006Smarius}
451227006Smarius
452227006Smariusvoid
453227006Smariusatpic_startup(void)
454227006Smarius{
455227006Smarius	struct atpic_intsrc *ai;
456227006Smarius	int i;
457227006Smarius
458227006Smarius	/* Start off with all interrupts disabled. */
459227006Smarius	imen = 0xffff;
460227006Smarius	i8259_init(&atpics[MASTER], 0);
461227006Smarius	i8259_init(&atpics[SLAVE], 1);
462227006Smarius	atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
463227006Smarius
464227006Smarius	/* Install low-level interrupt handlers for all of our IRQs. */
465227006Smarius	for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
466227006Smarius		if (i == ICU_SLAVEID)
467227006Smarius			continue;
468227006Smarius		ai->at_intsrc.is_count = &ai->at_count;
469227006Smarius		ai->at_intsrc.is_straycount = &ai->at_straycount;
470227006Smarius		setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
471227006Smarius		    ai->at_irq, ai->at_intr, SDT_SYS386IGT, SEL_KPL,
472227006Smarius		    GSEL(GCODE_SEL, SEL_KPL));
473227006Smarius	}
474227006Smarius
475227006Smarius#ifdef DEV_MCA
476227006Smarius	/* For MCA systems, all interrupts are level triggered. */
477227006Smarius	if (MCA_system)
478227006Smarius		for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
479227006Smarius			ai->at_trigger = INTR_TRIGGER_LEVEL;
480227006Smarius	else
481227006Smarius#endif
482227006Smarius
483227006Smarius#ifdef PC98
484227006Smarius	for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
485227006Smarius		switch (i) {
486227006Smarius		case 0:
487227006Smarius		case 1:
488227006Smarius		case 7:
489227006Smarius		case 8:
490227006Smarius			ai->at_trigger = INTR_TRIGGER_EDGE;
491227006Smarius			break;
492227006Smarius		default:
493227006Smarius			ai->at_trigger = INTR_TRIGGER_LEVEL;
494227006Smarius			break;
495227006Smarius		}
496227006Smarius#else
497227006Smarius	/*
498227006Smarius	 * Look for an ELCR.  If we find one, update the trigger modes.
499227006Smarius	 * If we don't find one, assume that IRQs 0, 1, 2, and 13 are
500227006Smarius	 * edge triggered and that everything else is level triggered.
501227006Smarius	 * We only use the trigger information to reprogram the ELCR if
502227006Smarius	 * we have one and as an optimization to avoid masking edge
503227006Smarius	 * triggered interrupts.  For the case that we don't have an ELCR,
504227006Smarius	 * it doesn't hurt to mask an edge triggered interrupt, so we
505227006Smarius	 * assume level trigger for any interrupt that we aren't sure is
506227006Smarius	 * edge triggered.
507227006Smarius	 */
508227006Smarius	if (elcr_found) {
509227006Smarius		for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
510227006Smarius			ai->at_trigger = elcr_read_trigger(i);
511227006Smarius	} else {
512227006Smarius		for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
513227006Smarius			switch (i) {
514227006Smarius			case 0:
515227006Smarius			case 1:
516227006Smarius			case 2:
517227006Smarius			case 8:
518227006Smarius			case 13:
519227006Smarius				ai->at_trigger = INTR_TRIGGER_EDGE;
520227006Smarius				break;
521227006Smarius			default:
522227006Smarius				ai->at_trigger = INTR_TRIGGER_LEVEL;
523227006Smarius				break;
524227006Smarius			}
525227006Smarius	}
526227006Smarius#endif /* PC98 */
527227006Smarius}
528227006Smarius
529227006Smariusstatic void
530227006Smariusatpic_init(void *dummy __unused)
531227006Smarius{
532227006Smarius	struct atpic_intsrc *ai;
533227006Smarius	int i;
534227006Smarius
535227006Smarius	/*
536227006Smarius	 * Register our PICs, even if we aren't going to use any of their
537227006Smarius	 * pins so that they are suspended and resumed.
538227006Smarius	 */
539227006Smarius	if (intr_register_pic(&atpics[0].at_pic) != 0 ||
540227006Smarius	    intr_register_pic(&atpics[1].at_pic) != 0)
541227006Smarius		panic("Unable to register ATPICs");
542227006Smarius
543227006Smarius	/*
544227006Smarius	 * If any of the ISA IRQs have an interrupt source already, then
545227006Smarius	 * assume that the APICs are being used and don't register any
546227006Smarius	 * of our interrupt sources.  This makes sure we don't accidentally
547227006Smarius	 * use mixed mode.  The "accidental" use could otherwise occur on
548227006Smarius	 * machines that route the ACPI SCI interrupt to a different ISA
549227006Smarius	 * IRQ (at least one machines routes it to IRQ 13) thus disabling
550227006Smarius	 * that APIC ISA routing and allowing the ATPIC source for that IRQ
551227006Smarius	 * to leak through.  We used to depend on this feature for routing
552227006Smarius	 * IRQ0 via mixed mode, but now we don't use mixed mode at all.
553227006Smarius	 */
554227006Smarius	for (i = 0; i < NUM_ISA_IRQS; i++)
555227006Smarius		if (intr_lookup_source(i) != NULL)
556227006Smarius			return;
557227006Smarius
558227006Smarius	/* Loop through all interrupt sources and add them. */
559227006Smarius	for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
560227006Smarius		if (i == ICU_SLAVEID)
561227006Smarius			continue;
562227006Smarius		intr_register_source(&ai->at_intsrc);
563227006Smarius	}
564227006Smarius}
565227006SmariusSYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL);
566227006Smarius
567227006Smariusvoid
568227006Smariusatpic_handle_intr(u_int vector, struct trapframe *frame)
569227006Smarius{
570227006Smarius	struct intsrc *isrc;
571227006Smarius
572227006Smarius	KASSERT(vector < NUM_ISA_IRQS, ("unknown int %u\n", vector));
573227006Smarius	isrc = &atintrs[vector].at_intsrc;
574227006Smarius
575227006Smarius	/*
576227006Smarius	 * If we don't have an event, see if this is a spurious
577227006Smarius	 * interrupt.
578227006Smarius	 */
579227006Smarius	if (isrc->is_event == NULL && (vector == 7 || vector == 15)) {
580227006Smarius		int port, isr;
581227006Smarius
582227006Smarius		/*
583227006Smarius		 * Read the ISR register to see if IRQ 7/15 is really
584227006Smarius		 * pending.  Reset read register back to IRR when done.
585227006Smarius		 */
586227006Smarius		port = ((struct atpic *)isrc->is_pic)->at_ioaddr;
587227006Smarius		spinlock_enter();
588227006Smarius		outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS);
589227006Smarius		isr = inb(port);
590227006Smarius		outb(port, OCW3_SEL | OCW3_RR);
591227006Smarius		spinlock_exit();
592227006Smarius		if ((isr & IRQ_MASK(7)) == 0)
593227006Smarius			return;
594227006Smarius	}
595227006Smarius	intr_execute_handlers(isrc, frame);
596227006Smarius}
597227006Smarius
598227006Smarius#ifdef DEV_ISA
599227006Smarius/*
600227006Smarius * Bus attachment for the ISA PIC.
601227006Smarius */
602227006Smariusstatic struct isa_pnp_id atpic_ids[] = {
603227006Smarius	{ 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
604227006Smarius	{ 0 }
605227006Smarius};
606227006Smarius
607227006Smariusstatic int
608227006Smariusatpic_probe(device_t dev)
609227006Smarius{
610227006Smarius	int result;
611227006Smarius
612227006Smarius	result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
613227006Smarius	if (result <= 0)
614227006Smarius		device_quiet(dev);
615227006Smarius	return (result);
616227006Smarius}
617227006Smarius
618227006Smarius/*
619227006Smarius * We might be granted IRQ 2, as this is typically consumed by chaining
620227006Smarius * between the two PIC components.  If we're using the APIC, however,
621227006Smarius * this may not be the case, and as such we should free the resource.
622227006Smarius * (XXX untested)
623227006Smarius *
624227006Smarius * The generic ISA attachment code will handle allocating any other resources
625227006Smarius * that we don't explicitly claim here.
626227006Smarius */
627227006Smariusstatic int
628227006Smariusatpic_attach(device_t dev)
629227006Smarius{
630227006Smarius	struct resource *res;
631227006Smarius	int rid;
632227006Smarius
633227006Smarius	/* Try to allocate our IRQ and then free it. */
634227006Smarius	rid = 0;
635227006Smarius	res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 0);
636227006Smarius	if (res != NULL)
637227006Smarius		bus_release_resource(dev, SYS_RES_IRQ, rid, res);
638227006Smarius	return (0);
639227006Smarius}
640227006Smarius
641227006Smariusstatic device_method_t atpic_methods[] = {
642227006Smarius	/* Device interface */
643227006Smarius	DEVMETHOD(device_probe,		atpic_probe),
644227006Smarius	DEVMETHOD(device_attach,	atpic_attach),
645227006Smarius	DEVMETHOD(device_detach,	bus_generic_detach),
646227006Smarius	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
647227006Smarius	DEVMETHOD(device_suspend,	bus_generic_suspend),
648227006Smarius	DEVMETHOD(device_resume,	bus_generic_resume),
649227006Smarius	{ 0, 0 }
650227006Smarius};
651227006Smarius
652227006Smariusstatic driver_t atpic_driver = {
653227006Smarius	"atpic",
654227006Smarius	atpic_methods,
655	1,		/* no softc */
656};
657
658static devclass_t atpic_devclass;
659
660DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0);
661#ifndef PC98
662DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0);
663#endif
664
665/*
666 * Return a bitmap of the current interrupt requests.  This is 8259-specific
667 * and is only suitable for use at probe time.
668 */
669intrmask_t
670isa_irq_pending(void)
671{
672	u_char irr1;
673	u_char irr2;
674
675	irr1 = inb(IO_ICU1);
676	irr2 = inb(IO_ICU2);
677	return ((irr2 << 8) | irr1);
678}
679#endif /* DEV_ISA */
680