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