xref: /freebsd/sys/x86/isa/atpic.c (revision 5fa29797910346fc0c54829bd979856e83b9b7ea)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 /*
30  * PIC driver for the 8259A Master and Slave PICs in PC/AT machines.
31  */
32 
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 #include "opt_auto_eoi.h"
37 #include "opt_isa.h"
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/bus.h>
42 #include <sys/interrupt.h>
43 #include <sys/kernel.h>
44 #include <sys/lock.h>
45 #include <sys/module.h>
46 
47 #include <machine/cpufunc.h>
48 #include <machine/frame.h>
49 #include <machine/intr_machdep.h>
50 #include <machine/md_var.h>
51 #include <machine/resource.h>
52 #include <machine/segments.h>
53 
54 #include <dev/ic/i8259.h>
55 #include <x86/isa/icu.h>
56 #include <isa/isareg.h>
57 #include <isa/isavar.h>
58 
59 #ifdef __amd64__
60 #define	SDT_ATPIC	SDT_SYSIGT
61 #define	GSEL_ATPIC	0
62 #else
63 #define	SDT_ATPIC	SDT_SYS386IGT
64 #define	GSEL_ATPIC	GSEL(GCODE_SEL, SEL_KPL)
65 #endif
66 
67 #define	MASTER	0
68 #define	SLAVE	1
69 
70 #define	IMEN_MASK(ai)		(IRQ_MASK((ai)->at_irq))
71 
72 #define	NUM_ISA_IRQS		16
73 
74 static void	atpic_init(void *dummy);
75 
76 inthand_t
77 	IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2),
78 	IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5),
79 	IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8),
80 	IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11),
81 	IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14),
82 	IDTVEC(atpic_intr15);
83 /* XXXKIB i386 uses stubs until pti comes */
84 inthand_t
85 	IDTVEC(atpic_intr0_pti), IDTVEC(atpic_intr1_pti),
86 	IDTVEC(atpic_intr2_pti), IDTVEC(atpic_intr3_pti),
87 	IDTVEC(atpic_intr4_pti), IDTVEC(atpic_intr5_pti),
88 	IDTVEC(atpic_intr6_pti), IDTVEC(atpic_intr7_pti),
89 	IDTVEC(atpic_intr8_pti), IDTVEC(atpic_intr9_pti),
90 	IDTVEC(atpic_intr10_pti), IDTVEC(atpic_intr11_pti),
91 	IDTVEC(atpic_intr12_pti), IDTVEC(atpic_intr13_pti),
92 	IDTVEC(atpic_intr14_pti), IDTVEC(atpic_intr15_pti);
93 
94 #define	IRQ(ap, ai)	((ap)->at_irqbase + (ai)->at_irq)
95 
96 #define	ATPIC(io, base, eoi) {						\
97 		.at_pic = {						\
98 			.pic_enable_source = atpic_enable_source,	\
99 			.pic_disable_source = atpic_disable_source,	\
100 			.pic_eoi_source = (eoi),			\
101 			.pic_enable_intr = atpic_enable_intr,		\
102 			.pic_disable_intr = atpic_disable_intr,		\
103 			.pic_vector = atpic_vector,			\
104 			.pic_source_pending = atpic_source_pending,	\
105 			.pic_resume = atpic_resume,			\
106 			.pic_config_intr = atpic_config_intr,		\
107 			.pic_assign_cpu = atpic_assign_cpu		\
108 		},							\
109 		.at_ioaddr = (io),					\
110 		.at_irqbase = (base),					\
111 		.at_intbase = IDT_IO_INTS + (base),			\
112 		.at_imen = 0xff,					\
113 	}
114 
115 #define	INTSRC(irq)							\
116 	{ { &atpics[(irq) / 8].at_pic }, IDTVEC(atpic_intr ## irq ),	\
117 	    IDTVEC(atpic_intr ## irq ## _pti), (irq) % 8 }
118 
119 struct atpic {
120 	struct pic at_pic;
121 	int	at_ioaddr;
122 	int	at_irqbase;
123 	uint8_t	at_intbase;
124 	uint8_t	at_imen;
125 };
126 
127 struct atpic_intsrc {
128 	struct intsrc at_intsrc;
129 	inthand_t *at_intr, *at_intr_pti;
130 	int	at_irq;			/* Relative to PIC base. */
131 	enum intr_trigger at_trigger;
132 	u_long	at_count;
133 	u_long	at_straycount;
134 };
135 
136 static void atpic_enable_source(struct intsrc *isrc);
137 static void atpic_disable_source(struct intsrc *isrc, int eoi);
138 static void atpic_eoi_master(struct intsrc *isrc);
139 static void atpic_eoi_slave(struct intsrc *isrc);
140 static void atpic_enable_intr(struct intsrc *isrc);
141 static void atpic_disable_intr(struct intsrc *isrc);
142 static int atpic_vector(struct intsrc *isrc);
143 static void atpic_resume(struct pic *pic, bool suspend_cancelled);
144 static int atpic_source_pending(struct intsrc *isrc);
145 static int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
146     enum intr_polarity pol);
147 static int atpic_assign_cpu(struct intsrc *isrc, u_int apic_id);
148 static void i8259_init(struct atpic *pic, int slave);
149 
150 static struct atpic atpics[] = {
151 	ATPIC(IO_ICU1, 0, atpic_eoi_master),
152 	ATPIC(IO_ICU2, 8, atpic_eoi_slave)
153 };
154 
155 static struct atpic_intsrc atintrs[] = {
156 	INTSRC(0),
157 	INTSRC(1),
158 	INTSRC(2),
159 	INTSRC(3),
160 	INTSRC(4),
161 	INTSRC(5),
162 	INTSRC(6),
163 	INTSRC(7),
164 	INTSRC(8),
165 	INTSRC(9),
166 	INTSRC(10),
167 	INTSRC(11),
168 	INTSRC(12),
169 	INTSRC(13),
170 	INTSRC(14),
171 	INTSRC(15),
172 };
173 
174 CTASSERT(nitems(atintrs) == NUM_ISA_IRQS);
175 
176 static __inline void
177 _atpic_eoi_master(struct intsrc *isrc)
178 {
179 
180 	KASSERT(isrc->is_pic == &atpics[MASTER].at_pic,
181 	    ("%s: mismatched pic", __func__));
182 #ifndef AUTO_EOI_1
183 	outb(atpics[MASTER].at_ioaddr, OCW2_EOI);
184 #endif
185 }
186 
187 /*
188  * The data sheet says no auto-EOI on slave, but it sometimes works.
189  * So, if AUTO_EOI_2 is enabled, we use it.
190  */
191 static __inline void
192 _atpic_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 	outb(atpics[SLAVE].at_ioaddr, OCW2_EOI);
199 #ifndef AUTO_EOI_1
200 	outb(atpics[MASTER].at_ioaddr, OCW2_EOI);
201 #endif
202 #endif
203 }
204 
205 static void
206 atpic_enable_source(struct intsrc *isrc)
207 {
208 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
209 	struct atpic *ap = (struct atpic *)isrc->is_pic;
210 
211 	spinlock_enter();
212 	if (ap->at_imen & IMEN_MASK(ai)) {
213 		ap->at_imen &= ~IMEN_MASK(ai);
214 		outb(ap->at_ioaddr + ICU_IMR_OFFSET, ap->at_imen);
215 	}
216 	spinlock_exit();
217 }
218 
219 static void
220 atpic_disable_source(struct intsrc *isrc, int eoi)
221 {
222 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
223 	struct atpic *ap = (struct atpic *)isrc->is_pic;
224 
225 	spinlock_enter();
226 	if (ai->at_trigger != INTR_TRIGGER_EDGE) {
227 		ap->at_imen |= IMEN_MASK(ai);
228 		outb(ap->at_ioaddr + ICU_IMR_OFFSET, ap->at_imen);
229 	}
230 
231 	/*
232 	 * Take care to call these functions directly instead of through
233 	 * a function pointer.  All of the referenced variables should
234 	 * still be hot in the cache.
235 	 */
236 	if (eoi == PIC_EOI) {
237 		if (isrc->is_pic == &atpics[MASTER].at_pic)
238 			_atpic_eoi_master(isrc);
239 		else
240 			_atpic_eoi_slave(isrc);
241 	}
242 
243 	spinlock_exit();
244 }
245 
246 static void
247 atpic_eoi_master(struct intsrc *isrc)
248 {
249 #ifndef AUTO_EOI_1
250 	spinlock_enter();
251 	_atpic_eoi_master(isrc);
252 	spinlock_exit();
253 #endif
254 }
255 
256 static void
257 atpic_eoi_slave(struct intsrc *isrc)
258 {
259 #ifndef AUTO_EOI_2
260 	spinlock_enter();
261 	_atpic_eoi_slave(isrc);
262 	spinlock_exit();
263 #endif
264 }
265 
266 static void
267 atpic_enable_intr(struct intsrc *isrc)
268 {
269 }
270 
271 static void
272 atpic_disable_intr(struct intsrc *isrc)
273 {
274 }
275 
276 
277 static int
278 atpic_vector(struct intsrc *isrc)
279 {
280 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
281 	struct atpic *ap = (struct atpic *)isrc->is_pic;
282 
283 	return (IRQ(ap, ai));
284 }
285 
286 static int
287 atpic_source_pending(struct intsrc *isrc)
288 {
289 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
290 	struct atpic *ap = (struct atpic *)isrc->is_pic;
291 
292 	return (inb(ap->at_ioaddr) & IMEN_MASK(ai));
293 }
294 
295 static void
296 atpic_resume(struct pic *pic, bool suspend_cancelled)
297 {
298 	struct atpic *ap = (struct atpic *)pic;
299 
300 	i8259_init(ap, ap == &atpics[SLAVE]);
301 	if (ap == &atpics[SLAVE] && elcr_found)
302 		elcr_resume();
303 }
304 
305 static int
306 atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
307     enum intr_polarity pol)
308 {
309 	struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
310 	u_int vector;
311 
312 	/* Map conforming values to edge/hi and sanity check the values. */
313 	if (trig == INTR_TRIGGER_CONFORM)
314 		trig = INTR_TRIGGER_EDGE;
315 	if (pol == INTR_POLARITY_CONFORM)
316 		pol = INTR_POLARITY_HIGH;
317 	vector = atpic_vector(isrc);
318 	if ((trig == INTR_TRIGGER_EDGE && pol == INTR_POLARITY_LOW) ||
319 	    (trig == INTR_TRIGGER_LEVEL && pol == INTR_POLARITY_HIGH)) {
320 		printf(
321 		"atpic: Mismatched config for IRQ%u: trigger %s, polarity %s\n",
322 		    vector, trig == INTR_TRIGGER_EDGE ? "edge" : "level",
323 		    pol == INTR_POLARITY_HIGH ? "high" : "low");
324 		return (EINVAL);
325 	}
326 
327 	/* If there is no change, just return. */
328 	if (ai->at_trigger == trig)
329 		return (0);
330 
331 	/*
332 	 * Certain IRQs can never be level/lo, so don't try to set them
333 	 * that way if asked.  At least some ELCR registers ignore setting
334 	 * these bits as well.
335 	 */
336 	if ((vector == 0 || vector == 1 || vector == 2 || vector == 13) &&
337 	    trig == INTR_TRIGGER_LEVEL) {
338 		if (bootverbose)
339 			printf(
340 		"atpic: Ignoring invalid level/low configuration for IRQ%u\n",
341 			    vector);
342 		return (EINVAL);
343 	}
344 	if (!elcr_found) {
345 		if (bootverbose)
346 			printf("atpic: No ELCR to configure IRQ%u as %s\n",
347 			    vector, trig == INTR_TRIGGER_EDGE ? "edge/high" :
348 			    "level/low");
349 		return (ENXIO);
350 	}
351 	if (bootverbose)
352 		printf("atpic: Programming IRQ%u as %s\n", vector,
353 		    trig == INTR_TRIGGER_EDGE ? "edge/high" : "level/low");
354 	spinlock_enter();
355 	elcr_write_trigger(atpic_vector(isrc), trig);
356 	ai->at_trigger = trig;
357 	spinlock_exit();
358 	return (0);
359 }
360 
361 static int
362 atpic_assign_cpu(struct intsrc *isrc, u_int apic_id)
363 {
364 
365 	/*
366 	 * 8259A's are only used in UP in which case all interrupts always
367 	 * go to the sole CPU and this function shouldn't even be called.
368 	 */
369 	panic("%s: bad cookie", __func__);
370 }
371 
372 static void
373 i8259_init(struct atpic *pic, int slave)
374 {
375 	int imr_addr;
376 
377 	/* Reset the PIC and program with next four bytes. */
378 	spinlock_enter();
379 	outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
380 	imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
381 
382 	/* Start vector. */
383 	outb(imr_addr, pic->at_intbase);
384 
385 	/*
386 	 * Setup slave links.  For the master pic, indicate what line
387 	 * the slave is configured on.  For the slave indicate
388 	 * which line on the master we are connected to.
389 	 */
390 	if (slave)
391 		outb(imr_addr, ICU_SLAVEID);
392 	else
393 		outb(imr_addr, IRQ_MASK(ICU_SLAVEID));
394 
395 	/* Set mode. */
396 	if (slave)
397 		outb(imr_addr, SLAVE_MODE);
398 	else
399 		outb(imr_addr, MASTER_MODE);
400 
401 	/* Set interrupt enable mask. */
402 	outb(imr_addr, pic->at_imen);
403 
404 	/* Reset is finished, default to IRR on read. */
405 	outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
406 
407 	/* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
408 	if (!slave)
409 		outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
410 
411 	spinlock_exit();
412 }
413 
414 void
415 atpic_startup(void)
416 {
417 	struct atpic_intsrc *ai;
418 	int i;
419 
420 	/* Start off with all interrupts disabled. */
421 	i8259_init(&atpics[MASTER], 0);
422 	i8259_init(&atpics[SLAVE], 1);
423 	atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
424 
425 	/* Install low-level interrupt handlers for all of our IRQs. */
426 	for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
427 		if (i == ICU_SLAVEID)
428 			continue;
429 		ai->at_intsrc.is_count = &ai->at_count;
430 		ai->at_intsrc.is_straycount = &ai->at_straycount;
431 		setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
432 		    ai->at_irq, pti ? ai->at_intr_pti : ai->at_intr, SDT_ATPIC,
433 		    SEL_KPL, GSEL_ATPIC);
434 	}
435 
436 	/*
437 	 * Look for an ELCR.  If we find one, update the trigger modes.
438 	 * If we don't find one, assume that IRQs 0, 1, 2, and 13 are
439 	 * edge triggered and that everything else is level triggered.
440 	 * We only use the trigger information to reprogram the ELCR if
441 	 * we have one and as an optimization to avoid masking edge
442 	 * triggered interrupts.  For the case that we don't have an ELCR,
443 	 * it doesn't hurt to mask an edge triggered interrupt, so we
444 	 * assume level trigger for any interrupt that we aren't sure is
445 	 * edge triggered.
446 	 */
447 	if (elcr_found) {
448 		for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
449 			ai->at_trigger = elcr_read_trigger(i);
450 	} else {
451 		for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
452 			switch (i) {
453 			case 0:
454 			case 1:
455 			case 2:
456 			case 8:
457 			case 13:
458 				ai->at_trigger = INTR_TRIGGER_EDGE;
459 				break;
460 			default:
461 				ai->at_trigger = INTR_TRIGGER_LEVEL;
462 				break;
463 			}
464 	}
465 }
466 
467 static void
468 atpic_init(void *dummy __unused)
469 {
470 	struct atpic_intsrc *ai;
471 	int i;
472 
473 	/*
474 	 * Register our PICs, even if we aren't going to use any of their
475 	 * pins so that they are suspended and resumed.
476 	 */
477 	if (intr_register_pic(&atpics[0].at_pic) != 0 ||
478 	    intr_register_pic(&atpics[1].at_pic) != 0)
479 		panic("Unable to register ATPICs");
480 
481 	/*
482 	 * If any of the ISA IRQs have an interrupt source already, then
483 	 * assume that the APICs are being used and don't register any
484 	 * of our interrupt sources.  This makes sure we don't accidentally
485 	 * use mixed mode.  The "accidental" use could otherwise occur on
486 	 * machines that route the ACPI SCI interrupt to a different ISA
487 	 * IRQ (at least one machines routes it to IRQ 13) thus disabling
488 	 * that APIC ISA routing and allowing the ATPIC source for that IRQ
489 	 * to leak through.  We used to depend on this feature for routing
490 	 * IRQ0 via mixed mode, but now we don't use mixed mode at all.
491 	 */
492 	for (i = 0; i < NUM_ISA_IRQS; i++)
493 		if (intr_lookup_source(i) != NULL)
494 			return;
495 
496 	/* Loop through all interrupt sources and add them. */
497 	for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
498 		if (i == ICU_SLAVEID)
499 			continue;
500 		intr_register_source(&ai->at_intsrc);
501 	}
502 }
503 SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_FOURTH, atpic_init, NULL);
504 
505 void
506 atpic_handle_intr(u_int vector, struct trapframe *frame)
507 {
508 	struct intsrc *isrc;
509 
510 	KASSERT(vector < NUM_ISA_IRQS, ("unknown int %u\n", vector));
511 	isrc = &atintrs[vector].at_intsrc;
512 
513 	/*
514 	 * If we don't have an event, see if this is a spurious
515 	 * interrupt.
516 	 */
517 	if (isrc->is_event == NULL && (vector == 7 || vector == 15)) {
518 		int port, isr;
519 
520 		/*
521 		 * Read the ISR register to see if IRQ 7/15 is really
522 		 * pending.  Reset read register back to IRR when done.
523 		 */
524 		port = ((struct atpic *)isrc->is_pic)->at_ioaddr;
525 		spinlock_enter();
526 		outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS);
527 		isr = inb(port);
528 		outb(port, OCW3_SEL | OCW3_RR);
529 		spinlock_exit();
530 		if ((isr & IRQ_MASK(7)) == 0)
531 			return;
532 	}
533 	intr_execute_handlers(isrc, frame);
534 }
535 
536 #ifdef DEV_ISA
537 /*
538  * Bus attachment for the ISA PIC.
539  */
540 static struct isa_pnp_id atpic_ids[] = {
541 	{ 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
542 	{ 0 }
543 };
544 
545 static int
546 atpic_probe(device_t dev)
547 {
548 	int result;
549 
550 	result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
551 	if (result <= 0)
552 		device_quiet(dev);
553 	return (result);
554 }
555 
556 /*
557  * We might be granted IRQ 2, as this is typically consumed by chaining
558  * between the two PIC components.  If we're using the APIC, however,
559  * this may not be the case, and as such we should free the resource.
560  * (XXX untested)
561  *
562  * The generic ISA attachment code will handle allocating any other resources
563  * that we don't explicitly claim here.
564  */
565 static int
566 atpic_attach(device_t dev)
567 {
568 	struct resource *res;
569 	int rid;
570 
571 	/* Try to allocate our IRQ and then free it. */
572 	rid = 0;
573 	res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 0);
574 	if (res != NULL)
575 		bus_release_resource(dev, SYS_RES_IRQ, rid, res);
576 	return (0);
577 }
578 
579 /*
580  * Return a bitmap of the current interrupt requests.  This is 8259-specific
581  * and is only suitable for use at probe time.
582  */
583 intrmask_t
584 isa_irq_pending(void)
585 {
586 	u_char irr1;
587 	u_char irr2;
588 
589 	irr1 = inb(IO_ICU1);
590 	irr2 = inb(IO_ICU2);
591 	return ((irr2 << 8) | irr1);
592 }
593 
594 static device_method_t atpic_methods[] = {
595 	/* Device interface */
596 	DEVMETHOD(device_probe,		atpic_probe),
597 	DEVMETHOD(device_attach,	atpic_attach),
598 	DEVMETHOD(device_detach,	bus_generic_detach),
599 	DEVMETHOD(device_shutdown,	bus_generic_shutdown),
600 	DEVMETHOD(device_suspend,	bus_generic_suspend),
601 	DEVMETHOD(device_resume,	bus_generic_resume),
602 	{ 0, 0 }
603 };
604 
605 static driver_t atpic_driver = {
606 	"atpic",
607 	atpic_methods,
608 	1,		/* no softc */
609 };
610 
611 static devclass_t atpic_devclass;
612 
613 DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0);
614 DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0);
615 ISA_PNP_INFO(atpic_ids);
616 #endif /* DEV_ISA */
617