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