]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/x86/isa/atpic.c
Update our devicetree to 4.19 for arm and arm64
[FreeBSD/FreeBSD.git] / sys / x86 / isa / atpic.c
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_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
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
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
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
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
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
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
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
305 atpic_enable_intr(struct intsrc *isrc)
306 {
307 }
308
309 static void
310 atpic_disable_intr(struct intsrc *isrc)
311 {
312 }
313
314
315 static int
316 atpic_vector(struct intsrc *isrc)
317 {
318         struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
319         struct atpic *ap = (struct atpic *)isrc->is_pic;
320
321         return (IRQ(ap, ai));
322 }
323
324 static int
325 atpic_source_pending(struct intsrc *isrc)
326 {
327         struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
328         struct atpic *ap = (struct atpic *)isrc->is_pic;
329
330         return (inb(ap->at_ioaddr) & IMEN_MASK(ai));
331 }
332
333 static void
334 atpic_resume(struct pic *pic, bool suspend_cancelled)
335 {
336         struct atpic *ap = (struct atpic *)pic;
337
338         i8259_init(ap, ap == &atpics[SLAVE]);
339         if (ap == &atpics[SLAVE] && elcr_found)
340                 elcr_resume();
341 }
342
343 static int
344 atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig,
345     enum intr_polarity pol)
346 {
347         struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
348         u_int vector;
349
350         /* Map conforming values to edge/hi and sanity check the values. */
351         if (trig == INTR_TRIGGER_CONFORM)
352                 trig = INTR_TRIGGER_EDGE;
353         if (pol == INTR_POLARITY_CONFORM)
354                 pol = INTR_POLARITY_HIGH;
355         vector = atpic_vector(isrc);
356         if ((trig == INTR_TRIGGER_EDGE && pol == INTR_POLARITY_LOW) ||
357             (trig == INTR_TRIGGER_LEVEL && pol == INTR_POLARITY_HIGH)) {
358                 printf(
359                 "atpic: Mismatched config for IRQ%u: trigger %s, polarity %s\n",
360                     vector, trig == INTR_TRIGGER_EDGE ? "edge" : "level",
361                     pol == INTR_POLARITY_HIGH ? "high" : "low");
362                 return (EINVAL);
363         }
364
365         /* If there is no change, just return. */
366         if (ai->at_trigger == trig)
367                 return (0);
368
369         /*
370          * Certain IRQs can never be level/lo, so don't try to set them
371          * that way if asked.  At least some ELCR registers ignore setting
372          * these bits as well.
373          */
374         if ((vector == 0 || vector == 1 || vector == 2 || vector == 13) &&
375             trig == INTR_TRIGGER_LEVEL) {
376                 if (bootverbose)
377                         printf(
378                 "atpic: Ignoring invalid level/low configuration for IRQ%u\n",
379                             vector);
380                 return (EINVAL);
381         }
382         if (!elcr_found) {
383                 if (bootverbose)
384                         printf("atpic: No ELCR to configure IRQ%u as %s\n",
385                             vector, trig == INTR_TRIGGER_EDGE ? "edge/high" :
386                             "level/low");
387                 return (ENXIO);
388         }
389         if (bootverbose)
390                 printf("atpic: Programming IRQ%u as %s\n", vector,
391                     trig == INTR_TRIGGER_EDGE ? "edge/high" : "level/low");
392         spinlock_enter();
393         elcr_write_trigger(atpic_vector(isrc), trig);
394         ai->at_trigger = trig;
395         spinlock_exit();
396         return (0);
397 }
398
399 static int
400 atpic_assign_cpu(struct intsrc *isrc, u_int apic_id)
401 {
402
403         /*
404          * 8259A's are only used in UP in which case all interrupts always
405          * go to the sole CPU and this function shouldn't even be called.
406          */
407         panic("%s: bad cookie", __func__);
408 }
409
410 static void
411 i8259_init(struct atpic *pic, int slave)
412 {
413         int imr_addr;
414
415         /* Reset the PIC and program with next four bytes. */
416         spinlock_enter();
417         outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
418         imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
419
420         /* Start vector. */
421         outb(imr_addr, pic->at_intbase);
422
423         /*
424          * Setup slave links.  For the master pic, indicate what line
425          * the slave is configured on.  For the slave indicate
426          * which line on the master we are connected to.
427          */
428         if (slave)
429                 outb(imr_addr, ICU_SLAVEID);
430         else
431                 outb(imr_addr, IRQ_MASK(ICU_SLAVEID));
432
433         /* Set mode. */
434         if (slave)
435                 outb(imr_addr, SLAVE_MODE);
436         else
437                 outb(imr_addr, MASTER_MODE);
438
439         /* Set interrupt enable mask. */
440         outb(imr_addr, pic->at_imen);
441
442         /* Reset is finished, default to IRR on read. */
443         outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
444
445         /* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
446         if (!slave)
447                 outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
448
449         spinlock_exit();
450 }
451
452 void
453 atpic_startup(void)
454 {
455         struct atpic_intsrc *ai;
456         int i;
457
458         /* Start off with all interrupts disabled. */
459         i8259_init(&atpics[MASTER], 0);
460         i8259_init(&atpics[SLAVE], 1);
461         atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
462
463         /* Install low-level interrupt handlers for all of our IRQs. */
464         for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) {
465                 if (i == ICU_SLAVEID)
466                         continue;
467                 ai->at_intsrc.is_count = &ai->at_count;
468                 ai->at_intsrc.is_straycount = &ai->at_straycount;
469                 setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
470                     ai->at_irq, pti ? ai->at_intr_pti : ai->at_intr, SDT_ATPIC,
471                     SEL_KPL, GSEL_ATPIC);
472         }
473
474         /*
475          * Look for an ELCR.  If we find one, update the trigger modes.
476          * If we don't find one, assume that IRQs 0, 1, 2, and 13 are
477          * edge triggered and that everything else is level triggered.
478          * We only use the trigger information to reprogram the ELCR if
479          * we have one and as an optimization to avoid masking edge
480          * triggered interrupts.  For the case that we don't have an ELCR,
481          * it doesn't hurt to mask an edge triggered interrupt, so we
482          * assume level trigger for any interrupt that we aren't sure is
483          * edge triggered.
484          */
485         if (elcr_found) {
486                 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
487                         ai->at_trigger = elcr_read_trigger(i);
488         } else {
489                 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++)
490                         switch (i) {
491                         case 0:
492                         case 1:
493                         case 2:
494                         case 8:
495                         case 13:
496                                 ai->at_trigger = INTR_TRIGGER_EDGE;
497                                 break;
498                         default:
499                                 ai->at_trigger = INTR_TRIGGER_LEVEL;
500                                 break;
501                         }
502         }
503 }
504
505 static void
506 atpic_init(void *dummy __unused)
507 {
508
509         /*
510          * Register our PICs, even if we aren't going to use any of their
511          * pins so that they are suspended and resumed.
512          */
513         if (intr_register_pic(&atpics[0].at_pic) != 0 ||
514             intr_register_pic(&atpics[1].at_pic) != 0)
515                 panic("Unable to register ATPICs");
516
517         if (num_io_irqs == 0)
518                 num_io_irqs = NUM_ISA_IRQS;
519 }
520 SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_FOURTH, atpic_init, NULL);
521
522 void
523 atpic_handle_intr(u_int vector, struct trapframe *frame)
524 {
525         struct intsrc *isrc;
526
527         KASSERT(vector < NUM_ISA_IRQS, ("unknown int %u\n", vector));
528         isrc = &atintrs[vector].at_intsrc;
529
530         /*
531          * If we don't have an event, see if this is a spurious
532          * interrupt.
533          */
534         if (isrc->is_event == NULL && (vector == 7 || vector == 15)) {
535                 int port, isr;
536
537                 /*
538                  * Read the ISR register to see if IRQ 7/15 is really
539                  * pending.  Reset read register back to IRR when done.
540                  */
541                 port = ((struct atpic *)isrc->is_pic)->at_ioaddr;
542                 spinlock_enter();
543                 outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS);
544                 isr = inb(port);
545                 outb(port, OCW3_SEL | OCW3_RR);
546                 spinlock_exit();
547                 if ((isr & IRQ_MASK(7)) == 0)
548                         return;
549         }
550         intr_execute_handlers(isrc, frame);
551 }
552
553 #ifdef DEV_ISA
554 /*
555  * Bus attachment for the ISA PIC.
556  */
557 static struct isa_pnp_id atpic_ids[] = {
558         { 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
559         { 0 }
560 };
561
562 static int
563 atpic_probe(device_t dev)
564 {
565         int result;
566         
567         result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
568         if (result <= 0)
569                 device_quiet(dev);
570         return (result);
571 }
572
573 /*
574  * We might be granted IRQ 2, as this is typically consumed by chaining
575  * between the two PIC components.  If we're using the APIC, however,
576  * this may not be the case, and as such we should free the resource.
577  * (XXX untested)
578  *
579  * The generic ISA attachment code will handle allocating any other resources
580  * that we don't explicitly claim here.
581  */
582 static int
583 atpic_attach(device_t dev)
584 {
585         struct resource *res;
586         int rid;
587
588         /* Try to allocate our IRQ and then free it. */
589         rid = 0;
590         res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 0);
591         if (res != NULL)
592                 bus_release_resource(dev, SYS_RES_IRQ, rid, res);
593         return (0);
594 }
595
596 /*
597  * Return a bitmap of the current interrupt requests.  This is 8259-specific
598  * and is only suitable for use at probe time.
599  */
600 intrmask_t
601 isa_irq_pending(void)
602 {
603         u_char irr1;
604         u_char irr2;
605
606         irr1 = inb(IO_ICU1);
607         irr2 = inb(IO_ICU2);
608         return ((irr2 << 8) | irr1);
609 }
610
611 static device_method_t atpic_methods[] = {
612         /* Device interface */
613         DEVMETHOD(device_probe,         atpic_probe),
614         DEVMETHOD(device_attach,        atpic_attach),
615         DEVMETHOD(device_detach,        bus_generic_detach),
616         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
617         DEVMETHOD(device_suspend,       bus_generic_suspend),
618         DEVMETHOD(device_resume,        bus_generic_resume),
619         { 0, 0 }
620 };
621
622 static driver_t atpic_driver = {
623         "atpic",
624         atpic_methods,
625         1,              /* no softc */
626 };
627
628 static devclass_t atpic_devclass;
629
630 DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0);
631 DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0);
632 ISA_PNP_INFO(atpic_ids);
633 #endif /* DEV_ISA */