atpic.c revision 122703
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 122703 2003-11-14 21:02:49Z 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#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	(ICW4_8086 | ICW4_AEOI)
77#else
78#define	MASTER_MODE	ICW4_8086
79#endif
80#ifdef AUTO_EOI_2
81#define	SLAVE_MODE	(ICW4_8086 | ICW4_AEOI)
82#else
83#define	SLAVE_MODE	ICW4_8086
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
195/*
196 * The data sheet says no auto-EOI on slave, but it sometimes works.
197 * So, if AUTO_EOI_2 is enabled, we use it.
198 */
199static void
200atpic_eoi_slave(struct intsrc *isrc)
201{
202
203	KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic,
204	    ("%s: mismatched pic", __func__));
205#ifndef AUTO_EOI_2
206	mtx_lock_spin(&icu_lock);
207	outb(atpics[SLAVE].at_ioaddr, ICU_EOI);
208#ifndef AUTO_EOI_1
209	outb(atpics[MASTER].at_ioaddr, ICU_EOI);
210#endif
211	mtx_unlock_spin(&icu_lock);
212#endif
213}
214
215static void
216atpic_enable_intr(struct intsrc *isrc)
217{
218}
219
220static int
221atpic_vector(struct intsrc *isrc)
222{
223	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
224	struct atpic *ap = (struct atpic *)isrc->is_pic;
225
226	return (IRQ(ap, ai));
227}
228
229static int
230atpic_source_pending(struct intsrc *isrc)
231{
232	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
233	struct atpic *ap = (struct atpic *)isrc->is_pic;
234
235	return (inb(ap->at_ioaddr) & (1 << ai->at_irq));
236}
237
238static void
239atpic_resume(struct intsrc *isrc)
240{
241	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
242	struct atpic *ap = (struct atpic *)isrc->is_pic;
243
244	if (ai->at_irq == 0)
245		i8259_init(ap, ap == &atpics[SLAVE]);
246}
247
248static void
249i8259_init(struct atpic *pic, int slave)
250{
251	int imr_addr;
252
253	/* Reset the PIC and program with next four bytes. */
254	mtx_lock_spin(&icu_lock);
255#ifdef DEV_MCA
256	/* MCA uses level triggered interrupts. */
257	if (MCA_system)
258		outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM);
259	else
260#endif
261		outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
262	imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
263
264	/* Start vector. */
265	outb(imr_addr, pic->at_intbase);
266
267	/*
268	 * Setup slave links.  For the master pic, indicate what line
269	 * the slave is configured on.  For the slave indicate
270	 * which line on the master we are connected to.
271	 */
272	if (slave)
273		outb(imr_addr, ICU_SLAVEID);	/* my slave id is 7 */
274	else
275		outb(imr_addr, IRQ_SLAVE);	/* slave on line 7 */
276
277	/* Set mode. */
278	if (slave)
279		outb(imr_addr, SLAVE_MODE);
280	else
281		outb(imr_addr, MASTER_MODE);
282
283	/* Set interrupt enable mask. */
284	outb(imr_addr, *pic->at_imen);
285
286	/* Reset is finished, default to IRR on read. */
287	outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
288
289#ifndef PC98
290	/* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
291	if (!slave)
292		outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
293#endif
294	mtx_unlock_spin(&icu_lock);
295}
296
297void
298atpic_startup(void)
299{
300
301	/* Start off with all interrupts disabled. */
302	imen = 0xffff;
303	i8259_init(&atpics[MASTER], 0);
304	i8259_init(&atpics[SLAVE], 1);
305	atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
306}
307
308static void
309atpic_init(void *dummy __unused)
310{
311	struct atpic_intsrc *ai;
312	int i;
313
314	/* Loop through all interrupt sources and add them. */
315	for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) {
316		if (i == ICU_SLAVEID)
317			continue;
318		ai = &atintrs[i];
319		setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
320		    ai->at_irq, ai->at_intr, SDT_SYS386IGT, SEL_KPL,
321		    GSEL(GCODE_SEL, SEL_KPL));
322		intr_register_source(&ai->at_intsrc);
323	}
324}
325SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL)
326
327void
328atpic_handle_intr(struct intrframe iframe)
329{
330	struct intsrc *isrc;
331
332	KASSERT((uint)iframe.if_vec < ICU_LEN,
333	    ("unknown int %d\n", iframe.if_vec));
334	isrc = &atintrs[iframe.if_vec].at_intsrc;
335	intr_execute_handlers(isrc, &iframe);
336}
337
338#ifdef DEV_ISA
339/*
340 * Bus attachment for the ISA PIC.
341 */
342static struct isa_pnp_id atpic_ids[] = {
343	{ 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
344	{ 0 }
345};
346
347static int
348atpic_probe(device_t dev)
349{
350	int result;
351
352	result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
353	if (result <= 0)
354		device_quiet(dev);
355	return (result);
356}
357
358/*
359 * We might be granted IRQ 2, as this is typically consumed by chaining
360 * between the two PIC components.  If we're using the APIC, however,
361 * this may not be the case, and as such we should free the resource.
362 * (XXX untested)
363 *
364 * The generic ISA attachment code will handle allocating any other resources
365 * that we don't explicitly claim here.
366 */
367static int
368atpic_attach(device_t dev)
369{
370	struct resource *res;
371	int rid;
372
373	/* Try to allocate our IRQ and then free it. */
374	rid = 0;
375	res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 0);
376	if (res != NULL)
377		bus_release_resource(dev, SYS_RES_IRQ, rid, res);
378	return (0);
379}
380
381static device_method_t atpic_methods[] = {
382	/* Device interface */
383	DEVMETHOD(device_probe,		atpic_probe),
384	DEVMETHOD(device_attach,	atpic_attach),
385	DEVMETHOD(device_detach,	bus_generic_detach),
386	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
387	DEVMETHOD(device_suspend,	bus_generic_suspend),
388	DEVMETHOD(device_resume,	bus_generic_resume),
389	{ 0, 0 }
390};
391
392static driver_t atpic_driver = {
393	"atpic",
394	atpic_methods,
395	1,		/* no softc */
396};
397
398static devclass_t atpic_devclass;
399
400DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0);
401#ifndef PC98
402DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0);
403#endif
404
405/*
406 * Return a bitmap of the current interrupt requests.  This is 8259-specific
407 * and is only suitable for use at probe time.
408 */
409intrmask_t
410isa_irq_pending(void)
411{
412	u_char irr1;
413	u_char irr2;
414
415	irr1 = inb(IO_ICU1);
416	irr2 = inb(IO_ICU2);
417	return ((irr2 << 8) | irr1);
418}
419#endif /* DEV_ISA */
420