atpic.c revision 128875
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 128875 2004-05-03 14:52:41Z jhb $");
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
96#define	IMEN_MASK(ai)		(1 << (ai)->at_irq)
97
98#define	NUM_ISA_IRQS		16
99
100static void	atpic_init(void *dummy);
101
102unsigned int imen;	/* XXX */
103
104inthand_t
105	IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2),
106	IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5),
107	IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8),
108	IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11),
109	IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14),
110	IDTVEC(atpic_intr15);
111
112#define	IRQ(ap, ai)	((ap)->at_irqbase + (ai)->at_irq)
113
114#define	ATPIC(io, base, eoi, imenptr)				\
115     	{ { atpic_enable_source, atpic_disable_source, (eoi),		\
116	    atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \
117	    atpic_resume }, (io), (base), IDT_IO_INTS + (base), (imenptr) }
118
119#define	INTSRC(irq)							\
120	{ { &atpics[(irq) / 8].at_pic }, (irq) % 8,			\
121	    IDTVEC(atpic_intr ## irq ) }
122
123struct atpic {
124	struct pic at_pic;
125	int	at_ioaddr;
126	int	at_irqbase;
127	uint8_t	at_intbase;
128	uint8_t	*at_imen;
129};
130
131struct atpic_intsrc {
132	struct intsrc at_intsrc;
133	int	at_irq;		/* Relative to PIC base. */
134	inthand_t *at_intr;
135	u_long	at_count;
136	u_long	at_straycount;
137};
138
139static void atpic_enable_source(struct intsrc *isrc);
140static void atpic_disable_source(struct intsrc *isrc);
141static void atpic_eoi_master(struct intsrc *isrc);
142static void atpic_eoi_slave(struct intsrc *isrc);
143static void atpic_enable_intr(struct intsrc *isrc);
144static int atpic_vector(struct intsrc *isrc);
145static void atpic_resume(struct intsrc *isrc);
146static int atpic_source_pending(struct intsrc *isrc);
147static void i8259_init(struct atpic *pic, int slave);
148
149static struct atpic atpics[] = {
150	ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen),
151	ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1)
152};
153
154static struct atpic_intsrc atintrs[] = {
155	INTSRC(0),
156	INTSRC(1),
157	INTSRC(2),
158	INTSRC(3),
159	INTSRC(4),
160	INTSRC(5),
161	INTSRC(6),
162	INTSRC(7),
163	INTSRC(8),
164	INTSRC(9),
165	INTSRC(10),
166	INTSRC(11),
167	INTSRC(12),
168	INTSRC(13),
169	INTSRC(14),
170	INTSRC(15),
171};
172
173CTASSERT(sizeof(atintrs) / sizeof(struct atpic_intsrc) == NUM_ISA_IRQS);
174
175static void
176atpic_enable_source(struct intsrc *isrc)
177{
178	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
179	struct atpic *ap = (struct atpic *)isrc->is_pic;
180
181	mtx_lock_spin(&icu_lock);
182	if (*ap->at_imen & IMEN_MASK(ai)) {
183		*ap->at_imen &= ~IMEN_MASK(ai);
184		outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
185	}
186	mtx_unlock_spin(&icu_lock);
187}
188
189static void
190atpic_disable_source(struct intsrc *isrc)
191{
192	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
193	struct atpic *ap = (struct atpic *)isrc->is_pic;
194
195	mtx_lock_spin(&icu_lock);
196	*ap->at_imen |= IMEN_MASK(ai);
197	outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
198	mtx_unlock_spin(&icu_lock);
199}
200
201static void
202atpic_eoi_master(struct intsrc *isrc)
203{
204
205	KASSERT(isrc->is_pic == &atpics[MASTER].at_pic,
206	    ("%s: mismatched pic", __func__));
207#ifndef AUTO_EOI_1
208	mtx_lock_spin(&icu_lock);
209	outb(atpics[MASTER].at_ioaddr, ICU_EOI);
210	mtx_unlock_spin(&icu_lock);
211#endif
212}
213
214/*
215 * The data sheet says no auto-EOI on slave, but it sometimes works.
216 * So, if AUTO_EOI_2 is enabled, we use it.
217 */
218static void
219atpic_eoi_slave(struct intsrc *isrc)
220{
221
222	KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic,
223	    ("%s: mismatched pic", __func__));
224#ifndef AUTO_EOI_2
225	mtx_lock_spin(&icu_lock);
226	outb(atpics[SLAVE].at_ioaddr, ICU_EOI);
227#ifndef AUTO_EOI_1
228	outb(atpics[MASTER].at_ioaddr, ICU_EOI);
229#endif
230	mtx_unlock_spin(&icu_lock);
231#endif
232}
233
234static void
235atpic_enable_intr(struct intsrc *isrc)
236{
237}
238
239static int
240atpic_vector(struct intsrc *isrc)
241{
242	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
243	struct atpic *ap = (struct atpic *)isrc->is_pic;
244
245	return (IRQ(ap, ai));
246}
247
248static int
249atpic_source_pending(struct intsrc *isrc)
250{
251	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
252	struct atpic *ap = (struct atpic *)isrc->is_pic;
253
254	return (inb(ap->at_ioaddr) & IMEN_MASK(ai));
255}
256
257static void
258atpic_resume(struct intsrc *isrc)
259{
260	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
261	struct atpic *ap = (struct atpic *)isrc->is_pic;
262
263	if (ai->at_irq == 0)
264		i8259_init(ap, ap == &atpics[SLAVE]);
265}
266
267static void
268i8259_init(struct atpic *pic, int slave)
269{
270	int imr_addr;
271
272	/* Reset the PIC and program with next four bytes. */
273	mtx_lock_spin(&icu_lock);
274#ifdef DEV_MCA
275	/* MCA uses level triggered interrupts. */
276	if (MCA_system)
277		outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM);
278	else
279#endif
280		outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
281	imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
282
283	/* Start vector. */
284	outb(imr_addr, pic->at_intbase);
285
286	/*
287	 * Setup slave links.  For the master pic, indicate what line
288	 * the slave is configured on.  For the slave indicate
289	 * which line on the master we are connected to.
290	 */
291	if (slave)
292		outb(imr_addr, ICU_SLAVEID);	/* my slave id is 7 */
293	else
294		outb(imr_addr, IRQ_SLAVE);	/* slave on line 7 */
295
296	/* Set mode. */
297	if (slave)
298		outb(imr_addr, SLAVE_MODE);
299	else
300		outb(imr_addr, MASTER_MODE);
301
302	/* Set interrupt enable mask. */
303	outb(imr_addr, *pic->at_imen);
304
305	/* Reset is finished, default to IRR on read. */
306	outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
307
308#ifndef PC98
309	/* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
310	if (!slave)
311		outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
312#endif
313	mtx_unlock_spin(&icu_lock);
314}
315
316void
317atpic_startup(void)
318{
319	struct atpic_intsrc *ai;
320	int i;
321
322	/* Start off with all interrupts disabled. */
323	imen = 0xffff;
324	i8259_init(&atpics[MASTER], 0);
325	i8259_init(&atpics[SLAVE], 1);
326	atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
327
328	/* Install low-level interrupt handlers for all of our IRQs. */
329	for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
330		if (i == ICU_SLAVEID)
331			continue;
332		ai->at_intsrc.is_count = &ai->at_count;
333		ai->at_intsrc.is_straycount = &ai->at_straycount;
334		setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
335		    ai->at_irq, ai->at_intr, SDT_SYS386IGT, SEL_KPL,
336		    GSEL(GCODE_SEL, SEL_KPL));
337	}
338}
339
340static void
341atpic_init(void *dummy __unused)
342{
343	struct atpic_intsrc *ai;
344	int i;
345
346	/* Loop through all interrupt sources and add them. */
347	for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
348		if (i == ICU_SLAVEID)
349			continue;
350		intr_register_source(&ai->at_intsrc);
351	}
352}
353SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL)
354
355void
356atpic_handle_intr(struct intrframe iframe)
357{
358	struct intsrc *isrc;
359
360	KASSERT((u_int)iframe.if_vec < ICU_LEN,
361	    ("unknown int %d\n", iframe.if_vec));
362	isrc = &atintrs[iframe.if_vec].at_intsrc;
363
364	/*
365	 * If we don't have an ithread, see if this is a spurious
366	 * interrupt.
367	 */
368	if (isrc->is_ithread == NULL &&
369	    (iframe.if_vec == 7 || iframe.if_vec == 15)) {
370		int port, isr;
371
372		/*
373		 * Read the ISR register to see if IRQ 7/15 is really
374		 * pending.  Reset read register back to IRR when done.
375		 */
376		port = ((struct atpic *)isrc->is_pic)->at_ioaddr;
377		mtx_lock_spin(&icu_lock);
378		outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS);
379		isr = inb(port);
380		outb(port, OCW3_SEL | OCW3_RR);
381		mtx_unlock_spin(&icu_lock);
382		if ((isr & IRQ7) == 0)
383			return;
384	}
385	intr_execute_handlers(isrc, &iframe);
386}
387
388#ifdef DEV_ISA
389/*
390 * Bus attachment for the ISA PIC.
391 */
392static struct isa_pnp_id atpic_ids[] = {
393	{ 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
394	{ 0 }
395};
396
397static int
398atpic_probe(device_t dev)
399{
400	int result;
401
402	result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
403	if (result <= 0)
404		device_quiet(dev);
405	return (result);
406}
407
408/*
409 * We might be granted IRQ 2, as this is typically consumed by chaining
410 * between the two PIC components.  If we're using the APIC, however,
411 * this may not be the case, and as such we should free the resource.
412 * (XXX untested)
413 *
414 * The generic ISA attachment code will handle allocating any other resources
415 * that we don't explicitly claim here.
416 */
417static int
418atpic_attach(device_t dev)
419{
420	struct resource *res;
421	int rid;
422
423	/* Try to allocate our IRQ and then free it. */
424	rid = 0;
425	res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 0);
426	if (res != NULL)
427		bus_release_resource(dev, SYS_RES_IRQ, rid, res);
428	return (0);
429}
430
431static device_method_t atpic_methods[] = {
432	/* Device interface */
433	DEVMETHOD(device_probe,		atpic_probe),
434	DEVMETHOD(device_attach,	atpic_attach),
435	DEVMETHOD(device_detach,	bus_generic_detach),
436	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
437	DEVMETHOD(device_suspend,	bus_generic_suspend),
438	DEVMETHOD(device_resume,	bus_generic_resume),
439	{ 0, 0 }
440};
441
442static driver_t atpic_driver = {
443	"atpic",
444	atpic_methods,
445	1,		/* no softc */
446};
447
448static devclass_t atpic_devclass;
449
450DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0);
451#ifndef PC98
452DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0);
453#endif
454
455/*
456 * Return a bitmap of the current interrupt requests.  This is 8259-specific
457 * and is only suitable for use at probe time.
458 */
459intrmask_t
460isa_irq_pending(void)
461{
462	u_char irr1;
463	u_char irr2;
464
465	irr1 = inb(IO_ICU1);
466	irr2 = inb(IO_ICU2);
467	return ((irr2 << 8) | irr1);
468}
469#endif /* DEV_ISA */
470