atpic.c revision 121985
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 121985 2003-11-03 21:34:45Z 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 <i386/isa/icu.h>
56#include <i386/isa/isa.h>
57#include <isa/isavar.h>
58
59#define	MASTER	0
60#define	SLAVE	1
61
62/* XXX: Magic numbers */
63#ifdef PC98
64#ifdef AUTO_EOI_1
65#define	MASTER_MODE	0x1f	/* Master auto EOI, 8086 mode */
66#else
67#define	MASTER_MODE	0x1d	/* Master 8086 mode */
68#endif
69#define	SLAVE_MODE	9	/* 8086 mode */
70#else /* IBM-PC */
71#ifdef AUTO_EOI_1
72#define	MASTER_MODE	(2 | 1)	/* Auto EOI, 8086 mode */
73#else
74#define	MASTER_MODE	1	/* 8086 mode */
75#endif
76#ifdef AUTO_EOI_2
77#define	SLAVE_MODE	(2 | 1)	/* Auto EOI, 8086 mode */
78#else
79#define	SLAVE_MODE	1	/* 8086 mode */
80#endif
81#endif /* PC98 */
82
83static void	atpic_init(void *dummy);
84
85unsigned int imen;	/* XXX */
86
87inthand_t
88	IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2),
89	IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5),
90	IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8),
91	IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11),
92	IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14),
93	IDTVEC(atpic_intr15);
94
95#define	IRQ(ap, ai)	((ap)->at_irqbase + (ai)->at_irq)
96
97#define	ATPIC(io, base, eoi, imenptr)				\
98     	{ { atpic_enable_source, atpic_disable_source, (eoi),		\
99	    atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \
100	    atpic_resume }, (io), (base), IDT_IO_INTS + (base), (imenptr) }
101
102#define	INTSRC(irq)							\
103	{ { &atpics[(irq) / 8].at_pic }, (irq) % 8,			\
104	    IDTVEC(atpic_intr ## irq ) }
105
106struct atpic {
107	struct pic at_pic;
108	int	at_ioaddr;
109	int	at_irqbase;
110	uint8_t	at_intbase;
111	uint8_t	*at_imen;
112};
113
114struct atpic_intsrc {
115	struct intsrc at_intsrc;
116	int	at_irq;		/* Relative to PIC base. */
117	inthand_t *at_intr;
118};
119
120static void atpic_enable_source(struct intsrc *isrc);
121static void atpic_disable_source(struct intsrc *isrc);
122static void atpic_eoi_master(struct intsrc *isrc);
123static void atpic_eoi_slave(struct intsrc *isrc);
124static void atpic_enable_intr(struct intsrc *isrc);
125static int atpic_vector(struct intsrc *isrc);
126static void atpic_resume(struct intsrc *isrc);
127static int atpic_source_pending(struct intsrc *isrc);
128static void i8259_init(struct atpic *pic, int slave);
129
130static struct atpic atpics[] = {
131	ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen),
132	ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1)
133};
134
135static struct atpic_intsrc atintrs[] = {
136	INTSRC(0),
137	INTSRC(1),
138	INTSRC(2),
139	INTSRC(3),
140	INTSRC(4),
141	INTSRC(5),
142	INTSRC(6),
143	INTSRC(7),
144	INTSRC(8),
145	INTSRC(9),
146	INTSRC(10),
147	INTSRC(11),
148	INTSRC(12),
149	INTSRC(13),
150	INTSRC(14),
151	INTSRC(15),
152};
153
154static void
155atpic_enable_source(struct intsrc *isrc)
156{
157	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
158	struct atpic *ap = (struct atpic *)isrc->is_pic;
159
160	mtx_lock_spin(&icu_lock);
161	*ap->at_imen &= ~(1 << ai->at_irq);
162	outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
163	mtx_unlock_spin(&icu_lock);
164}
165
166static void
167atpic_disable_source(struct intsrc *isrc)
168{
169	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
170	struct atpic *ap = (struct atpic *)isrc->is_pic;
171
172	mtx_lock_spin(&icu_lock);
173	*ap->at_imen |= (1 << ai->at_irq);
174	outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
175	mtx_unlock_spin(&icu_lock);
176}
177
178static void
179atpic_eoi_master(struct intsrc *isrc)
180{
181
182	KASSERT(isrc->is_pic == &atpics[MASTER].at_pic,
183	    ("%s: mismatched pic", __func__));
184#ifndef AUTO_EOI_1
185	mtx_lock_spin(&icu_lock);
186	outb(atpics[MASTER].at_ioaddr, ICU_EOI);
187	mtx_unlock_spin(&icu_lock);
188#endif
189}
190
191static void
192atpic_eoi_slave(struct intsrc *isrc)
193{
194
195	KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic,
196	    ("%s: mismatched pic", __func__));
197#ifndef AUTO_EOI_2
198	mtx_lock_spin(&icu_lock);
199	outb(atpics[SLAVE].at_ioaddr, ICU_EOI);
200#ifndef AUTO_EOI_1
201	outb(atpics[MASTER].at_ioaddr, ICU_EOI);
202#endif
203	mtx_unlock_spin(&icu_lock);
204#endif
205}
206
207static void
208atpic_enable_intr(struct intsrc *isrc)
209{
210	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
211	struct atpic *ap = (struct atpic *)isrc->is_pic;
212	register_t eflags;
213
214	mtx_lock_spin(&icu_lock);
215	eflags = intr_disable();
216	setidt(ap->at_intbase + ai->at_irq, ai->at_intr, SDT_SYS386IGT,
217	    SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
218	intr_restore(eflags);
219	mtx_unlock_spin(&icu_lock);
220}
221
222static int
223atpic_vector(struct intsrc *isrc)
224{
225	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
226	struct atpic *ap = (struct atpic *)isrc->is_pic;
227
228	return (IRQ(ap, ai));
229}
230
231static int
232atpic_source_pending(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 (inb(ap->at_ioaddr) & (1 << ai->at_irq));
238}
239
240static void
241atpic_resume(struct intsrc *isrc)
242{
243	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
244	struct atpic *ap = (struct atpic *)isrc->is_pic;
245
246	if (ai->at_irq == 0)
247		i8259_init(ap, ap == &atpics[SLAVE]);
248}
249
250static void
251i8259_init(struct atpic *pic, int slave)
252{
253	int imr_addr;
254
255	/* Reset the PIC and program with next four bytes. */
256	mtx_lock_spin(&icu_lock);
257#ifdef DEV_MCA
258	if (MCA_system)
259		outb(pic->at_ioaddr, 0x19);
260	else
261#endif
262		outb(pic->at_ioaddr, 0x11);
263	imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
264
265	/* Start vector. */
266	outb(imr_addr, pic->at_intbase);
267
268	/*
269	 * Setup slave links.  For the master pic, indicate what line
270	 * the slave is configured on.  For the slave indicate
271	 * which line on the master we are connected to.
272	 */
273	if (slave)
274		outb(imr_addr, ICU_SLAVEID);	/* my slave id is 7 */
275	else
276		outb(imr_addr, IRQ_SLAVE);	/* slave on line 7 */
277
278	/* Set mode. */
279	if (slave)
280		outb(imr_addr, SLAVE_MODE);
281	else
282		outb(imr_addr, MASTER_MODE);
283
284	/* Set interrupt enable mask. */
285	outb(imr_addr, *pic->at_imen);
286
287	/* Reset is finished, default to IRR on read. */
288	outb(pic->at_ioaddr, 0x0a);
289
290#ifndef PC98
291	/* Set priority order to 3-7, 0-2 (com2 first). */
292	if (!slave)
293		outb(pic->at_ioaddr, 0xc0 | (3 - 1));
294#endif
295	mtx_unlock_spin(&icu_lock);
296}
297
298void
299atpic_startup(void)
300{
301
302	/* Start off with all interrupts disabled. */
303	imen = 0xffff;
304	i8259_init(&atpics[MASTER], 0);
305	i8259_init(&atpics[SLAVE], 1);
306	atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
307}
308
309static void
310atpic_init(void *dummy __unused)
311{
312	struct atpic_intsrc *ai;
313	int i;
314
315	/* Loop through all interrupt sources and add them. */
316	for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) {
317		if (i == ICU_SLAVEID)
318			continue;
319		ai = &atintrs[i];
320		intr_register_source(&ai->at_intsrc);
321	}
322}
323SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL)
324
325void
326atpic_sched_ithd(struct intrframe iframe)
327{
328	struct intsrc *isrc;
329
330	KASSERT((uint)iframe.if_vec < ICU_LEN,
331	    ("unknown int %d\n", iframe.if_vec));
332	isrc = &atintrs[iframe.if_vec].at_intsrc;
333	intr_execute_handlers(isrc, &iframe);
334}
335
336#ifdef DEV_ISA
337/*
338 * Bus attachment for the ISA PIC.
339 */
340static struct isa_pnp_id atpic_ids[] = {
341	{ 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
342	{ 0 }
343};
344
345static int
346atpic_probe(device_t dev)
347{
348	int result;
349
350	result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
351	if (result <= 0)
352		device_quiet(dev);
353	return (result);
354}
355
356/*
357 * We might be granted IRQ 2, as this is typically consumed by chaining
358 * between the two PIC components.  If we're using the APIC, however,
359 * this may not be the case, and as such we should free the resource.
360 * (XXX untested)
361 *
362 * The generic ISA attachment code will handle allocating any other resources
363 * that we don't explicitly claim here.
364 */
365static int
366atpic_attach(device_t dev)
367{
368	struct resource *res;
369	int rid;
370
371	/* Try to allocate our IRQ and then free it. */
372	rid = 0;
373	res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 0);
374	if (res != NULL)
375		bus_release_resource(dev, SYS_RES_IRQ, rid, res);
376	return (0);
377}
378
379static device_method_t atpic_methods[] = {
380	/* Device interface */
381	DEVMETHOD(device_probe,		atpic_probe),
382	DEVMETHOD(device_attach,	atpic_attach),
383	DEVMETHOD(device_detach,	bus_generic_detach),
384	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
385	DEVMETHOD(device_suspend,	bus_generic_suspend),
386	DEVMETHOD(device_resume,	bus_generic_resume),
387	{ 0, 0 }
388};
389
390static driver_t atpic_driver = {
391	"atpic",
392	atpic_methods,
393	1,		/* no softc */
394};
395
396static devclass_t atpic_devclass;
397
398DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0);
399DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0);
400
401/*
402 * Return a bitmap of the current interrupt requests.  This is 8259-specific
403 * and is only suitable for use at probe time.
404 */
405intrmask_t
406isa_irq_pending(void)
407{
408	u_char irr1;
409	u_char irr2;
410
411	irr1 = inb(IO_ICU1);
412	irr2 = inb(IO_ICU2);
413	return ((irr2 << 8) | irr1);
414}
415#endif /* DEV_ISA */
416