atpic.c revision 128438
1/*-
2 * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the author nor the names of any co-contributors
14 *    may be used to endorse or promote products derived from this software
15 *    without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30/*
31 * PIC driver for the 8259A Master and Slave PICs in PC/AT machines.
32 */
33
34#include <sys/cdefs.h>
35__FBSDID("$FreeBSD: head/sys/i386/isa/atpic.c 128438 2004-04-19 18:38:04Z obrien $");
36
37#include "opt_auto_eoi.h"
38#include "opt_isa.h"
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/bus.h>
43#include <sys/interrupt.h>
44#include <sys/kernel.h>
45#include <sys/lock.h>
46#include <sys/mutex.h>
47
48#include <machine/cpufunc.h>
49#include <machine/frame.h>
50#include <machine/intr_machdep.h>
51#include <machine/md_var.h>
52#include <machine/resource.h>
53#include <machine/segments.h>
54
55#include <dev/ic/i8259.h>
56#include <i386/isa/icu.h>
57#ifdef PC98
58#include <pc98/pc98/pc98.h>
59#else
60#include <i386/isa/isa.h>
61#endif
62#include <isa/isavar.h>
63
64#define	MASTER	0
65#define	SLAVE	1
66
67/*
68 * Determine the base master and slave modes not including auto EOI support.
69 * All machines that FreeBSD supports use 8086 mode.
70 */
71#ifdef PC98
72/*
73 * PC-98 machines do not support auto EOI on the second PIC.  Also, it
74 * seems that PC-98 machine PICs use buffered mode, and the master PIC
75 * uses special fully nested mode.
76 */
77#define	BASE_MASTER_MODE	(ICW4_SFNM | ICW4_BUF | ICW4_MS | ICW4_8086)
78#define	BASE_SLAVE_MODE		(ICW4_BUF | ICW4_8086)
79#else
80#define	BASE_MASTER_MODE	ICW4_8086
81#define	BASE_SLAVE_MODE		ICW4_8086
82#endif
83
84/* Enable automatic EOI if requested. */
85#ifdef AUTO_EOI_1
86#define	MASTER_MODE		(BASE_MASTER_MODE | ICW4_AEOI)
87#else
88#define	MASTER_MODE		BASE_MASTER_MODE
89#endif
90#ifdef AUTO_EOI_2
91#define	SLAVE_MODE		(BASE_SLAVE_MODE | ICW4_AEOI)
92#else
93#define	SLAVE_MODE		BASE_SLAVE_MODE
94#endif
95
96static void	atpic_init(void *dummy);
97
98unsigned int imen;	/* XXX */
99
100inthand_t
101	IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2),
102	IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5),
103	IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8),
104	IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11),
105	IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14),
106	IDTVEC(atpic_intr15);
107
108#define	IRQ(ap, ai)	((ap)->at_irqbase + (ai)->at_irq)
109
110#define	ATPIC(io, base, eoi, imenptr)				\
111     	{ { atpic_enable_source, atpic_disable_source, (eoi),		\
112	    atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \
113	    atpic_resume }, (io), (base), IDT_IO_INTS + (base), (imenptr) }
114
115#define	INTSRC(irq)							\
116	{ { &atpics[(irq) / 8].at_pic }, (irq) % 8,			\
117	    IDTVEC(atpic_intr ## irq ) }
118
119struct atpic {
120	struct pic at_pic;
121	int	at_ioaddr;
122	int	at_irqbase;
123	uint8_t	at_intbase;
124	uint8_t	*at_imen;
125};
126
127struct atpic_intsrc {
128	struct intsrc at_intsrc;
129	int	at_irq;		/* Relative to PIC base. */
130	inthand_t *at_intr;
131	u_long	at_count;
132	u_long	at_straycount;
133};
134
135static void atpic_enable_source(struct intsrc *isrc);
136static void atpic_disable_source(struct intsrc *isrc);
137static void atpic_eoi_master(struct intsrc *isrc);
138static void atpic_eoi_slave(struct intsrc *isrc);
139static void atpic_enable_intr(struct intsrc *isrc);
140static int atpic_vector(struct intsrc *isrc);
141static void atpic_resume(struct intsrc *isrc);
142static int atpic_source_pending(struct intsrc *isrc);
143static void i8259_init(struct atpic *pic, int slave);
144
145static struct atpic atpics[] = {
146	ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen),
147	ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1)
148};
149
150static struct atpic_intsrc atintrs[] = {
151	INTSRC(0),
152	INTSRC(1),
153	INTSRC(2),
154	INTSRC(3),
155	INTSRC(4),
156	INTSRC(5),
157	INTSRC(6),
158	INTSRC(7),
159	INTSRC(8),
160	INTSRC(9),
161	INTSRC(10),
162	INTSRC(11),
163	INTSRC(12),
164	INTSRC(13),
165	INTSRC(14),
166	INTSRC(15),
167};
168
169static void
170atpic_enable_source(struct intsrc *isrc)
171{
172	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
173	struct atpic *ap = (struct atpic *)isrc->is_pic;
174
175	mtx_lock_spin(&icu_lock);
176	*ap->at_imen &= ~(1 << ai->at_irq);
177	outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
178	mtx_unlock_spin(&icu_lock);
179}
180
181static void
182atpic_disable_source(struct intsrc *isrc)
183{
184	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
185	struct atpic *ap = (struct atpic *)isrc->is_pic;
186
187	mtx_lock_spin(&icu_lock);
188	*ap->at_imen |= (1 << ai->at_irq);
189	outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
190	mtx_unlock_spin(&icu_lock);
191}
192
193static void
194atpic_eoi_master(struct intsrc *isrc)
195{
196
197	KASSERT(isrc->is_pic == &atpics[MASTER].at_pic,
198	    ("%s: mismatched pic", __func__));
199#ifndef AUTO_EOI_1
200	mtx_lock_spin(&icu_lock);
201	outb(atpics[MASTER].at_ioaddr, ICU_EOI);
202	mtx_unlock_spin(&icu_lock);
203#endif
204}
205
206/*
207 * The data sheet says no auto-EOI on slave, but it sometimes works.
208 * So, if AUTO_EOI_2 is enabled, we use it.
209 */
210static void
211atpic_eoi_slave(struct intsrc *isrc)
212{
213
214	KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic,
215	    ("%s: mismatched pic", __func__));
216#ifndef AUTO_EOI_2
217	mtx_lock_spin(&icu_lock);
218	outb(atpics[SLAVE].at_ioaddr, ICU_EOI);
219#ifndef AUTO_EOI_1
220	outb(atpics[MASTER].at_ioaddr, ICU_EOI);
221#endif
222	mtx_unlock_spin(&icu_lock);
223#endif
224}
225
226static void
227atpic_enable_intr(struct intsrc *isrc)
228{
229}
230
231static int
232atpic_vector(struct intsrc *isrc)
233{
234	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
235	struct atpic *ap = (struct atpic *)isrc->is_pic;
236
237	return (IRQ(ap, ai));
238}
239
240static int
241atpic_source_pending(struct intsrc *isrc)
242{
243	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
244	struct atpic *ap = (struct atpic *)isrc->is_pic;
245
246	return (inb(ap->at_ioaddr) & (1 << ai->at_irq));
247}
248
249static void
250atpic_resume(struct intsrc *isrc)
251{
252	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
253	struct atpic *ap = (struct atpic *)isrc->is_pic;
254
255	if (ai->at_irq == 0)
256		i8259_init(ap, ap == &atpics[SLAVE]);
257}
258
259static void
260i8259_init(struct atpic *pic, int slave)
261{
262	int imr_addr;
263
264	/* Reset the PIC and program with next four bytes. */
265	mtx_lock_spin(&icu_lock);
266#ifdef DEV_MCA
267	/* MCA uses level triggered interrupts. */
268	if (MCA_system)
269		outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM);
270	else
271#endif
272		outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
273	imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
274
275	/* Start vector. */
276	outb(imr_addr, pic->at_intbase);
277
278	/*
279	 * Setup slave links.  For the master pic, indicate what line
280	 * the slave is configured on.  For the slave indicate
281	 * which line on the master we are connected to.
282	 */
283	if (slave)
284		outb(imr_addr, ICU_SLAVEID);	/* my slave id is 7 */
285	else
286		outb(imr_addr, IRQ_SLAVE);	/* slave on line 7 */
287
288	/* Set mode. */
289	if (slave)
290		outb(imr_addr, SLAVE_MODE);
291	else
292		outb(imr_addr, MASTER_MODE);
293
294	/* Set interrupt enable mask. */
295	outb(imr_addr, *pic->at_imen);
296
297	/* Reset is finished, default to IRR on read. */
298	outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
299
300#ifndef PC98
301	/* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
302	if (!slave)
303		outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
304#endif
305	mtx_unlock_spin(&icu_lock);
306}
307
308void
309atpic_startup(void)
310{
311	struct atpic_intsrc *ai;
312	int i;
313
314	/* Start off with all interrupts disabled. */
315	imen = 0xffff;
316	i8259_init(&atpics[MASTER], 0);
317	i8259_init(&atpics[SLAVE], 1);
318	atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
319
320	/* Install low-level interrupt handlers for all of our IRQs. */
321	for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) {
322		if (i == ICU_SLAVEID)
323			continue;
324		ai = &atintrs[i];
325		ai->at_intsrc.is_count = &ai->at_count;
326		ai->at_intsrc.is_straycount = &ai->at_straycount;
327		setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
328		    ai->at_irq, ai->at_intr, SDT_SYS386IGT, SEL_KPL,
329		    GSEL(GCODE_SEL, SEL_KPL));
330	}
331}
332
333static void
334atpic_init(void *dummy __unused)
335{
336	int i;
337
338	/* Loop through all interrupt sources and add them. */
339	for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) {
340		if (i == ICU_SLAVEID)
341			continue;
342		intr_register_source(&atintrs[i].at_intsrc);
343	}
344}
345SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL)
346
347void
348atpic_handle_intr(struct intrframe iframe)
349{
350	struct intsrc *isrc;
351
352	KASSERT((u_int)iframe.if_vec < ICU_LEN,
353	    ("unknown int %d\n", iframe.if_vec));
354	isrc = &atintrs[iframe.if_vec].at_intsrc;
355
356	/*
357	 * If we don't have an ithread, see if this is a spurious
358	 * interrupt.
359	 */
360	if (isrc->is_ithread == NULL &&
361	    (iframe.if_vec == 7 || iframe.if_vec == 15)) {
362		int port, isr;
363
364		/*
365		 * Read the ISR register to see if IRQ 7/15 is really
366		 * pending.  Reset read register back to IRR when done.
367		 */
368		port = ((struct atpic *)isrc->is_pic)->at_ioaddr;
369		mtx_lock_spin(&icu_lock);
370		outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS);
371		isr = inb(port);
372		outb(port, OCW3_SEL | OCW3_RR);
373		mtx_unlock_spin(&icu_lock);
374		if ((isr & IRQ7) == 0)
375			return;
376	}
377	intr_execute_handlers(isrc, &iframe);
378}
379
380#ifdef DEV_ISA
381/*
382 * Bus attachment for the ISA PIC.
383 */
384static struct isa_pnp_id atpic_ids[] = {
385	{ 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
386	{ 0 }
387};
388
389static int
390atpic_probe(device_t dev)
391{
392	int result;
393
394	result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
395	if (result <= 0)
396		device_quiet(dev);
397	return (result);
398}
399
400/*
401 * We might be granted IRQ 2, as this is typically consumed by chaining
402 * between the two PIC components.  If we're using the APIC, however,
403 * this may not be the case, and as such we should free the resource.
404 * (XXX untested)
405 *
406 * The generic ISA attachment code will handle allocating any other resources
407 * that we don't explicitly claim here.
408 */
409static int
410atpic_attach(device_t dev)
411{
412	struct resource *res;
413	int rid;
414
415	/* Try to allocate our IRQ and then free it. */
416	rid = 0;
417	res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 0);
418	if (res != NULL)
419		bus_release_resource(dev, SYS_RES_IRQ, rid, res);
420	return (0);
421}
422
423static device_method_t atpic_methods[] = {
424	/* Device interface */
425	DEVMETHOD(device_probe,		atpic_probe),
426	DEVMETHOD(device_attach,	atpic_attach),
427	DEVMETHOD(device_detach,	bus_generic_detach),
428	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
429	DEVMETHOD(device_suspend,	bus_generic_suspend),
430	DEVMETHOD(device_resume,	bus_generic_resume),
431	{ 0, 0 }
432};
433
434static driver_t atpic_driver = {
435	"atpic",
436	atpic_methods,
437	1,		/* no softc */
438};
439
440static devclass_t atpic_devclass;
441
442DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0);
443#ifndef PC98
444DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0);
445#endif
446
447/*
448 * Return a bitmap of the current interrupt requests.  This is 8259-specific
449 * and is only suitable for use at probe time.
450 */
451intrmask_t
452isa_irq_pending(void)
453{
454	u_char irr1;
455	u_char irr2;
456
457	irr1 = inb(IO_ICU1);
458	irr2 = inb(IO_ICU2);
459	return ((irr2 << 8) | irr1);
460}
461#endif /* DEV_ISA */
462