atpic.c revision 122692
1306196Sjkim/*-
2160819Ssimon * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
3160819Ssimon * All rights reserved.
4160819Ssimon *
5160819Ssimon * Redistribution and use in source and binary forms, with or without
6160819Ssimon * modification, are permitted provided that the following conditions
7160819Ssimon * are met:
8160819Ssimon * 1. Redistributions of source code must retain the above copyright
9160819Ssimon *    notice, this list of conditions and the following disclaimer.
10160819Ssimon * 2. Redistributions in binary form must reproduce the above copyright
11160819Ssimon *    notice, this list of conditions and the following disclaimer in the
12160819Ssimon *    documentation and/or other materials provided with the distribution.
13160819Ssimon * 3. Neither the name of the author nor the names of any co-contributors
14160819Ssimon *    may be used to endorse or promote products derived from this software
15160819Ssimon *    without specific prior written permission.
16160819Ssimon *
17160819Ssimon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18160819Ssimon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19160819Ssimon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20215698Ssimon * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21215698Ssimon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22215698Ssimon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23215698Ssimon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24215698Ssimon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25160819Ssimon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26160819Ssimon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27160819Ssimon * SUCH DAMAGE.
28160819Ssimon */
29160819Ssimon
30160819Ssimon/*
31160819Ssimon * PIC driver for the 8259A Master and Slave PICs in PC/AT machines.
32160819Ssimon */
33160819Ssimon
34160819Ssimon#include <sys/cdefs.h>
35160819Ssimon__FBSDID("$FreeBSD: head/sys/i386/isa/atpic.c 122692 2003-11-14 19:13:06Z jhb $");
36160819Ssimon
37160819Ssimon#include "opt_auto_eoi.h"
38160819Ssimon#include "opt_isa.h"
39160819Ssimon
40160819Ssimon#include <sys/param.h>
41276864Sjkim#include <sys/systm.h>
42276864Sjkim#include <sys/bus.h>
43160819Ssimon#include <sys/interrupt.h>
44160819Ssimon#include <sys/kernel.h>
45215698Ssimon#include <sys/lock.h>
46215698Ssimon#include <sys/mutex.h>
47215698Ssimon
48215698Ssimon#include <machine/cpufunc.h>
49160819Ssimon#include <machine/frame.h>
50215698Ssimon#include <machine/intr_machdep.h>
51160819Ssimon#include <machine/md_var.h>
52160819Ssimon#include <machine/resource.h>
53276864Sjkim#include <machine/segments.h>
54276864Sjkim
55276864Sjkim#include <i386/isa/icu.h>
56160819Ssimon#ifdef PC98
57276864Sjkim#include <pc98/pc98/pc98.h>
58276864Sjkim#else
59276864Sjkim#include <i386/isa/isa.h>
60276864Sjkim#endif
61276864Sjkim#include <isa/isavar.h>
62276864Sjkim
63215698Ssimon#define	MASTER	0
64276864Sjkim#define	SLAVE	1
65276864Sjkim
66276864Sjkim/* XXX: Magic numbers */
67276864Sjkim#ifdef PC98
68276864Sjkim#ifdef AUTO_EOI_1
69215698Ssimon#define	MASTER_MODE	0x1f	/* Master auto EOI, 8086 mode */
70276864Sjkim#else
71160819Ssimon#define	MASTER_MODE	0x1d	/* Master 8086 mode */
72160819Ssimon#endif
73160819Ssimon#define	SLAVE_MODE	9	/* 8086 mode */
74160819Ssimon#else /* IBM-PC */
75160819Ssimon#ifdef AUTO_EOI_1
76160819Ssimon#define	MASTER_MODE	(ICW4_8086 | ICW4_AEOI)
77160819Ssimon#else
78160819Ssimon#define	MASTER_MODE	ICW4_8086
79160819Ssimon#endif
80160819Ssimon#ifdef AUTO_EOI_2
81160819Ssimon#define	SLAVE_MODE	(ICW4_8086 | ICW4_AEOI)
82160819Ssimon#else
83160819Ssimon#define	SLAVE_MODE	ICW4_8086
84160819Ssimon#endif
85160819Ssimon#endif /* PC98 */
86160819Ssimon
87160819Ssimonstatic void	atpic_init(void *dummy);
88160819Ssimon
89160819Ssimonunsigned int imen;	/* XXX */
90160819Ssimon
91160819Ssimoninthand_t
92160819Ssimon	IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2),
93160819Ssimon	IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5),
94160819Ssimon	IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8),
95160819Ssimon	IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11),
96160819Ssimon	IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14),
97160819Ssimon	IDTVEC(atpic_intr15);
98160819Ssimon
99160819Ssimon#define	IRQ(ap, ai)	((ap)->at_irqbase + (ai)->at_irq)
100160819Ssimon
101160819Ssimon#define	ATPIC(io, base, eoi, imenptr)				\
102160819Ssimon     	{ { atpic_enable_source, atpic_disable_source, (eoi),		\
103160819Ssimon	    atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \
104160819Ssimon	    atpic_resume }, (io), (base), IDT_IO_INTS + (base), (imenptr) }
105160819Ssimon
106160819Ssimon#define	INTSRC(irq)							\
107160819Ssimon	{ { &atpics[(irq) / 8].at_pic }, (irq) % 8,			\
108160819Ssimon	    IDTVEC(atpic_intr ## irq ) }
109160819Ssimon
110160819Ssimonstruct atpic {
111160819Ssimon	struct pic at_pic;
112160819Ssimon	int	at_ioaddr;
113160819Ssimon	int	at_irqbase;
114160819Ssimon	uint8_t	at_intbase;
115160819Ssimon	uint8_t	*at_imen;
116160819Ssimon};
117160819Ssimon
118160819Ssimonstruct atpic_intsrc {
119160819Ssimon	struct intsrc at_intsrc;
120160819Ssimon	int	at_irq;		/* Relative to PIC base. */
121160819Ssimon	inthand_t *at_intr;
122160819Ssimon};
123160819Ssimon
124160819Ssimonstatic void atpic_enable_source(struct intsrc *isrc);
125160819Ssimonstatic void atpic_disable_source(struct intsrc *isrc);
126160819Ssimonstatic void atpic_eoi_master(struct intsrc *isrc);
127160819Ssimonstatic void atpic_eoi_slave(struct intsrc *isrc);
128160819Ssimonstatic void atpic_enable_intr(struct intsrc *isrc);
129160819Ssimonstatic int atpic_vector(struct intsrc *isrc);
130160819Ssimonstatic void atpic_resume(struct intsrc *isrc);
131160819Ssimonstatic int atpic_source_pending(struct intsrc *isrc);
132160819Ssimonstatic void i8259_init(struct atpic *pic, int slave);
133160819Ssimon
134160819Ssimonstatic struct atpic atpics[] = {
135160819Ssimon	ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen),
136306196Sjkim	ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1)
137215698Ssimon};
138215698Ssimon
139215698Ssimonstatic struct atpic_intsrc atintrs[] = {
140215698Ssimon	INTSRC(0),
141160819Ssimon	INTSRC(1),
142160819Ssimon	INTSRC(2),
143160819Ssimon	INTSRC(3),
144160819Ssimon	INTSRC(4),
145160819Ssimon	INTSRC(5),
146160819Ssimon	INTSRC(6),
147160819Ssimon	INTSRC(7),
148160819Ssimon	INTSRC(8),
149160819Ssimon	INTSRC(9),
150160819Ssimon	INTSRC(10),
151160819Ssimon	INTSRC(11),
152160819Ssimon	INTSRC(12),
153160819Ssimon	INTSRC(13),
154160819Ssimon	INTSRC(14),
155160819Ssimon	INTSRC(15),
156160819Ssimon};
157194208Ssimon
158194208Ssimonstatic void
159194208Ssimonatpic_enable_source(struct intsrc *isrc)
160194208Ssimon{
161194208Ssimon	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
162194208Ssimon	struct atpic *ap = (struct atpic *)isrc->is_pic;
163194208Ssimon
164194208Ssimon	mtx_lock_spin(&icu_lock);
165194208Ssimon	*ap->at_imen &= ~(1 << ai->at_irq);
166194208Ssimon	outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
167194208Ssimon	mtx_unlock_spin(&icu_lock);
168194208Ssimon}
169194208Ssimon
170194208Ssimonstatic void
171276864Sjkimatpic_disable_source(struct intsrc *isrc)
172194208Ssimon{
173194208Ssimon	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
174194208Ssimon	struct atpic *ap = (struct atpic *)isrc->is_pic;
175194208Ssimon
176194208Ssimon	mtx_lock_spin(&icu_lock);
177194208Ssimon	*ap->at_imen |= (1 << ai->at_irq);
178194208Ssimon	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	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
219	struct atpic *ap = (struct atpic *)isrc->is_pic;
220	register_t eflags;
221
222	mtx_lock_spin(&icu_lock);
223	eflags = intr_disable();
224	setidt(ap->at_intbase + ai->at_irq, ai->at_intr, SDT_SYS386IGT,
225	    SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
226	intr_restore(eflags);
227	mtx_unlock_spin(&icu_lock);
228}
229
230static int
231atpic_vector(struct intsrc *isrc)
232{
233	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
234	struct atpic *ap = (struct atpic *)isrc->is_pic;
235
236	return (IRQ(ap, ai));
237}
238
239static int
240atpic_source_pending(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 (inb(ap->at_ioaddr) & (1 << ai->at_irq));
246}
247
248static void
249atpic_resume(struct intsrc *isrc)
250{
251	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
252	struct atpic *ap = (struct atpic *)isrc->is_pic;
253
254	if (ai->at_irq == 0)
255		i8259_init(ap, ap == &atpics[SLAVE]);
256}
257
258static void
259i8259_init(struct atpic *pic, int slave)
260{
261	int imr_addr;
262
263	/* Reset the PIC and program with next four bytes. */
264	mtx_lock_spin(&icu_lock);
265#ifdef DEV_MCA
266	/* MCA uses level triggered interrupts. */
267	if (MCA_system)
268		outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM);
269	else
270#endif
271		outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
272	imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
273
274	/* Start vector. */
275	outb(imr_addr, pic->at_intbase);
276
277	/*
278	 * Setup slave links.  For the master pic, indicate what line
279	 * the slave is configured on.  For the slave indicate
280	 * which line on the master we are connected to.
281	 */
282	if (slave)
283		outb(imr_addr, ICU_SLAVEID);	/* my slave id is 7 */
284	else
285		outb(imr_addr, IRQ_SLAVE);	/* slave on line 7 */
286
287	/* Set mode. */
288	if (slave)
289		outb(imr_addr, SLAVE_MODE);
290	else
291		outb(imr_addr, MASTER_MODE);
292
293	/* Set interrupt enable mask. */
294	outb(imr_addr, *pic->at_imen);
295
296	/* Reset is finished, default to IRR on read. */
297	outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
298
299#ifndef PC98
300	/* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
301	if (!slave)
302		outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
303#endif
304	mtx_unlock_spin(&icu_lock);
305}
306
307void
308atpic_startup(void)
309{
310
311	/* Start off with all interrupts disabled. */
312	imen = 0xffff;
313	i8259_init(&atpics[MASTER], 0);
314	i8259_init(&atpics[SLAVE], 1);
315	atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
316}
317
318static void
319atpic_init(void *dummy __unused)
320{
321	struct atpic_intsrc *ai;
322	int i;
323
324	/* Loop through all interrupt sources and add them. */
325	for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) {
326		if (i == ICU_SLAVEID)
327			continue;
328		ai = &atintrs[i];
329		intr_register_source(&ai->at_intsrc);
330	}
331}
332SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL)
333
334void
335atpic_handle_intr(struct intrframe iframe)
336{
337	struct intsrc *isrc;
338
339	KASSERT((uint)iframe.if_vec < ICU_LEN,
340	    ("unknown int %d\n", iframe.if_vec));
341	isrc = &atintrs[iframe.if_vec].at_intsrc;
342	intr_execute_handlers(isrc, &iframe);
343}
344
345#ifdef DEV_ISA
346/*
347 * Bus attachment for the ISA PIC.
348 */
349static struct isa_pnp_id atpic_ids[] = {
350	{ 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
351	{ 0 }
352};
353
354static int
355atpic_probe(device_t dev)
356{
357	int result;
358
359	result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
360	if (result <= 0)
361		device_quiet(dev);
362	return (result);
363}
364
365/*
366 * We might be granted IRQ 2, as this is typically consumed by chaining
367 * between the two PIC components.  If we're using the APIC, however,
368 * this may not be the case, and as such we should free the resource.
369 * (XXX untested)
370 *
371 * The generic ISA attachment code will handle allocating any other resources
372 * that we don't explicitly claim here.
373 */
374static int
375atpic_attach(device_t dev)
376{
377	struct resource *res;
378	int rid;
379
380	/* Try to allocate our IRQ and then free it. */
381	rid = 0;
382	res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 0);
383	if (res != NULL)
384		bus_release_resource(dev, SYS_RES_IRQ, rid, res);
385	return (0);
386}
387
388static device_method_t atpic_methods[] = {
389	/* Device interface */
390	DEVMETHOD(device_probe,		atpic_probe),
391	DEVMETHOD(device_attach,	atpic_attach),
392	DEVMETHOD(device_detach,	bus_generic_detach),
393	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
394	DEVMETHOD(device_suspend,	bus_generic_suspend),
395	DEVMETHOD(device_resume,	bus_generic_resume),
396	{ 0, 0 }
397};
398
399static driver_t atpic_driver = {
400	"atpic",
401	atpic_methods,
402	1,		/* no softc */
403};
404
405static devclass_t atpic_devclass;
406
407DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0);
408#ifndef PC98
409DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0);
410#endif
411
412/*
413 * Return a bitmap of the current interrupt requests.  This is 8259-specific
414 * and is only suitable for use at probe time.
415 */
416intrmask_t
417isa_irq_pending(void)
418{
419	u_char irr1;
420	u_char irr2;
421
422	irr1 = inb(IO_ICU1);
423	irr2 = inb(IO_ICU2);
424	return ((irr2 << 8) | irr1);
425}
426#endif /* DEV_ISA */
427