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