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