atpic.c revision 153146
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 153146 2005-12-05 22:39:09Z 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/module.h> 47#include <sys/mutex.h> 48 49#include <machine/cpufunc.h> 50#include <machine/frame.h> 51#include <machine/intr_machdep.h> 52#include <machine/md_var.h> 53#include <machine/resource.h> 54#include <machine/segments.h> 55 56#include <dev/ic/i8259.h> 57#include <i386/isa/icu.h> 58#ifdef PC98 59#include <pc98/cbus/cbus.h> 60#else 61#include <i386/isa/isa.h> 62#endif 63#include <isa/isavar.h> 64 65#define MASTER 0 66#define SLAVE 1 67 68/* 69 * PC-98 machines wire the slave 8259A to pin 7 on the master PIC, and 70 * PC-AT machines wire the slave PIC to pin 2 on the master PIC. 71 */ 72#ifdef PC98 73#define ICU_SLAVEID 7 74#else 75#define ICU_SLAVEID 2 76#endif 77 78/* 79 * Determine the base master and slave modes not including auto EOI support. 80 * All machines that FreeBSD supports use 8086 mode. 81 */ 82#ifdef PC98 83/* 84 * PC-98 machines do not support auto EOI on the second PIC. Also, it 85 * seems that PC-98 machine PICs use buffered mode, and the master PIC 86 * uses special fully nested mode. 87 */ 88#define BASE_MASTER_MODE (ICW4_SFNM | ICW4_BUF | ICW4_MS | ICW4_8086) 89#define BASE_SLAVE_MODE (ICW4_BUF | ICW4_8086) 90#else 91#define BASE_MASTER_MODE ICW4_8086 92#define BASE_SLAVE_MODE ICW4_8086 93#endif 94 95/* Enable automatic EOI if requested. */ 96#ifdef AUTO_EOI_1 97#define MASTER_MODE (BASE_MASTER_MODE | ICW4_AEOI) 98#else 99#define MASTER_MODE BASE_MASTER_MODE 100#endif 101#ifdef AUTO_EOI_2 102#define SLAVE_MODE (BASE_SLAVE_MODE | ICW4_AEOI) 103#else 104#define SLAVE_MODE BASE_SLAVE_MODE 105#endif 106 107#define IRQ_MASK(irq) (1 << (irq)) 108#define IMEN_MASK(ai) (IRQ_MASK((ai)->at_irq)) 109 110#define NUM_ISA_IRQS 16 111 112static void atpic_init(void *dummy); 113 114unsigned int imen; /* XXX */ 115 116inthand_t 117 IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2), 118 IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5), 119 IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8), 120 IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11), 121 IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14), 122 IDTVEC(atpic_intr15); 123 124#define IRQ(ap, ai) ((ap)->at_irqbase + (ai)->at_irq) 125 126#define ATPIC(io, base, eoi, imenptr) \ 127 { { atpic_enable_source, atpic_disable_source, (eoi), \ 128 atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \ 129 atpic_resume, atpic_config_intr }, (io), (base), \ 130 IDT_IO_INTS + (base), (imenptr) } 131 132#define INTSRC(irq) \ 133 { { &atpics[(irq) / 8].at_pic }, IDTVEC(atpic_intr ## irq ), \ 134 (irq) % 8 } 135 136struct atpic { 137 struct pic at_pic; 138 int at_ioaddr; 139 int at_irqbase; 140 uint8_t at_intbase; 141 uint8_t *at_imen; 142}; 143 144struct atpic_intsrc { 145 struct intsrc at_intsrc; 146 inthand_t *at_intr; 147 int at_irq; /* Relative to PIC base. */ 148 enum intr_trigger at_trigger; 149 u_long at_count; 150 u_long at_straycount; 151}; 152 153static void atpic_enable_source(struct intsrc *isrc); 154static void atpic_disable_source(struct intsrc *isrc, int eoi); 155static void atpic_eoi_master(struct intsrc *isrc); 156static void atpic_eoi_slave(struct intsrc *isrc); 157static void atpic_enable_intr(struct intsrc *isrc); 158static int atpic_vector(struct intsrc *isrc); 159static void atpic_resume(struct intsrc *isrc); 160static int atpic_source_pending(struct intsrc *isrc); 161static int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig, 162 enum intr_polarity pol); 163static void i8259_init(struct atpic *pic, int slave); 164 165static struct atpic atpics[] = { 166 ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen), 167 ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1) 168}; 169 170static struct atpic_intsrc atintrs[] = { 171 INTSRC(0), 172 INTSRC(1), 173 INTSRC(2), 174 INTSRC(3), 175 INTSRC(4), 176 INTSRC(5), 177 INTSRC(6), 178 INTSRC(7), 179 INTSRC(8), 180 INTSRC(9), 181 INTSRC(10), 182 INTSRC(11), 183 INTSRC(12), 184 INTSRC(13), 185 INTSRC(14), 186 INTSRC(15), 187}; 188 189CTASSERT(sizeof(atintrs) / sizeof(atintrs[0]) == NUM_ISA_IRQS); 190 191static __inline void 192_atpic_eoi_master(struct intsrc *isrc) 193{ 194 195 KASSERT(isrc->is_pic == &atpics[MASTER].at_pic, 196 ("%s: mismatched pic", __func__)); 197#ifndef AUTO_EOI_1 198 outb(atpics[MASTER].at_ioaddr, OCW2_EOI); 199#endif 200} 201 202/* 203 * The data sheet says no auto-EOI on slave, but it sometimes works. 204 * So, if AUTO_EOI_2 is enabled, we use it. 205 */ 206static __inline void 207_atpic_eoi_slave(struct intsrc *isrc) 208{ 209 210 KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic, 211 ("%s: mismatched pic", __func__)); 212#ifndef AUTO_EOI_2 213 outb(atpics[SLAVE].at_ioaddr, OCW2_EOI); 214#ifndef AUTO_EOI_1 215 outb(atpics[MASTER].at_ioaddr, OCW2_EOI); 216#endif 217#endif 218} 219 220static void 221atpic_enable_source(struct intsrc *isrc) 222{ 223 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 224 struct atpic *ap = (struct atpic *)isrc->is_pic; 225 226 mtx_lock_spin(&icu_lock); 227 if (*ap->at_imen & IMEN_MASK(ai)) { 228 *ap->at_imen &= ~IMEN_MASK(ai); 229 outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); 230 } 231 mtx_unlock_spin(&icu_lock); 232} 233 234static void 235atpic_disable_source(struct intsrc *isrc, int eoi) 236{ 237 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 238 struct atpic *ap = (struct atpic *)isrc->is_pic; 239 240 mtx_lock_spin(&icu_lock); 241 if (ai->at_trigger != INTR_TRIGGER_EDGE) { 242 *ap->at_imen |= IMEN_MASK(ai); 243 outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); 244 } 245 246 /* 247 * Take care to call these functions directly instead of through 248 * a function pointer. All of the referenced variables should 249 * still be hot in the cache. 250 */ 251 if (eoi == PIC_EOI) { 252 if (isrc->is_pic == &atpics[MASTER].at_pic) 253 _atpic_eoi_master(isrc); 254 else 255 _atpic_eoi_slave(isrc); 256 } 257 258 mtx_unlock_spin(&icu_lock); 259} 260 261static void 262atpic_eoi_master(struct intsrc *isrc) 263{ 264#ifndef AUTO_EOI_1 265 mtx_lock_spin(&icu_lock); 266 _atpic_eoi_master(isrc); 267 mtx_unlock_spin(&icu_lock); 268#endif 269} 270 271static void 272atpic_eoi_slave(struct intsrc *isrc) 273{ 274#ifndef AUTO_EOI_2 275 mtx_lock_spin(&icu_lock); 276 _atpic_eoi_slave(isrc); 277 mtx_unlock_spin(&icu_lock); 278#endif 279} 280 281static void 282atpic_enable_intr(struct intsrc *isrc) 283{ 284} 285 286static int 287atpic_vector(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 (IRQ(ap, ai)); 293} 294 295static int 296atpic_source_pending(struct intsrc *isrc) 297{ 298 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 299 struct atpic *ap = (struct atpic *)isrc->is_pic; 300 301 return (inb(ap->at_ioaddr) & IMEN_MASK(ai)); 302} 303 304static void 305atpic_resume(struct intsrc *isrc) 306{ 307 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 308 struct atpic *ap = (struct atpic *)isrc->is_pic; 309 310 if (ai->at_irq == 0) { 311 i8259_init(ap, ap == &atpics[SLAVE]); 312#ifndef PC98 313 if (ap == &atpics[SLAVE] && elcr_found) 314 elcr_resume(); 315#endif 316 } 317} 318 319static int 320atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig, 321 enum intr_polarity pol) 322{ 323 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 324 u_int vector; 325 326 /* Map conforming values to edge/hi and sanity check the values. */ 327 if (trig == INTR_TRIGGER_CONFORM) 328 trig = INTR_TRIGGER_EDGE; 329 if (pol == INTR_POLARITY_CONFORM) 330 pol = INTR_POLARITY_HIGH; 331 vector = atpic_vector(isrc); 332 if ((trig == INTR_TRIGGER_EDGE && pol == INTR_POLARITY_LOW) || 333 (trig == INTR_TRIGGER_LEVEL && pol == INTR_POLARITY_HIGH)) { 334 printf( 335 "atpic: Mismatched config for IRQ%u: trigger %s, polarity %s\n", 336 vector, trig == INTR_TRIGGER_EDGE ? "edge" : "level", 337 pol == INTR_POLARITY_HIGH ? "high" : "low"); 338 return (EINVAL); 339 } 340 341 /* If there is no change, just return. */ 342 if (ai->at_trigger == trig) 343 return (0); 344 345#ifdef PC98 346 if ((vector == 0 || vector == 1 || vector == 7 || vector == 8) && 347 trig == INTR_TRIGGER_LEVEL) { 348 if (bootverbose) 349 printf( 350 "atpic: Ignoring invalid level/low configuration for IRQ%u\n", 351 vector); 352 return (EINVAL); 353 } 354 return (ENXIO); 355#else 356 /* 357 * Certain IRQs can never be level/lo, so don't try to set them 358 * that way if asked. At least some ELCR registers ignore setting 359 * these bits as well. 360 */ 361 if ((vector == 0 || vector == 1 || vector == 2 || vector == 13) && 362 trig == INTR_TRIGGER_LEVEL) { 363 if (bootverbose) 364 printf( 365 "atpic: Ignoring invalid level/low configuration for IRQ%u\n", 366 vector); 367 return (EINVAL); 368 } 369 if (!elcr_found) { 370 if (bootverbose) 371 printf("atpic: No ELCR to configure IRQ%u as %s\n", 372 vector, trig == INTR_TRIGGER_EDGE ? "edge/high" : 373 "level/low"); 374 return (ENXIO); 375 } 376 if (bootverbose) 377 printf("atpic: Programming IRQ%u as %s\n", vector, 378 trig == INTR_TRIGGER_EDGE ? "edge/high" : "level/low"); 379 mtx_lock_spin(&icu_lock); 380 elcr_write_trigger(atpic_vector(isrc), trig); 381 ai->at_trigger = trig; 382 mtx_unlock_spin(&icu_lock); 383 return (0); 384#endif /* PC98 */ 385} 386 387static void 388i8259_init(struct atpic *pic, int slave) 389{ 390 int imr_addr; 391 392 /* Reset the PIC and program with next four bytes. */ 393 mtx_lock_spin(&icu_lock); 394#ifdef DEV_MCA 395 /* MCA uses level triggered interrupts. */ 396 if (MCA_system) 397 outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM); 398 else 399#endif 400 outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4); 401 imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET; 402 403 /* Start vector. */ 404 outb(imr_addr, pic->at_intbase); 405 406 /* 407 * Setup slave links. For the master pic, indicate what line 408 * the slave is configured on. For the slave indicate 409 * which line on the master we are connected to. 410 */ 411 if (slave) 412 outb(imr_addr, ICU_SLAVEID); 413 else 414 outb(imr_addr, IRQ_MASK(ICU_SLAVEID)); 415 416 /* Set mode. */ 417 if (slave) 418 outb(imr_addr, SLAVE_MODE); 419 else 420 outb(imr_addr, MASTER_MODE); 421 422 /* Set interrupt enable mask. */ 423 outb(imr_addr, *pic->at_imen); 424 425 /* Reset is finished, default to IRR on read. */ 426 outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR); 427 428#ifndef PC98 429 /* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */ 430 if (!slave) 431 outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1); 432#endif 433 mtx_unlock_spin(&icu_lock); 434} 435 436void 437atpic_startup(void) 438{ 439 struct atpic_intsrc *ai; 440 int i; 441 442 /* Start off with all interrupts disabled. */ 443 imen = 0xffff; 444 i8259_init(&atpics[MASTER], 0); 445 i8259_init(&atpics[SLAVE], 1); 446 atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]); 447 448 /* Install low-level interrupt handlers for all of our IRQs. */ 449 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) { 450 if (i == ICU_SLAVEID) 451 continue; 452 ai->at_intsrc.is_count = &ai->at_count; 453 ai->at_intsrc.is_straycount = &ai->at_straycount; 454 setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase + 455 ai->at_irq, ai->at_intr, SDT_SYS386IGT, SEL_KPL, 456 GSEL(GCODE_SEL, SEL_KPL)); 457 } 458 459#ifdef DEV_MCA 460 /* For MCA systems, all interrupts are level triggered. */ 461 if (MCA_system) 462 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) 463 ai->at_trigger = INTR_TRIGGER_LEVEL; 464 else 465#endif 466 467#ifdef PC98 468 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) 469 switch (i) { 470 case 0: 471 case 1: 472 case 7: 473 case 8: 474 ai->at_trigger = INTR_TRIGGER_EDGE; 475 break; 476 default: 477 ai->at_trigger = INTR_TRIGGER_LEVEL; 478 break; 479 } 480#else 481 /* 482 * Look for an ELCR. If we find one, update the trigger modes. 483 * If we don't find one, assume that IRQs 0, 1, 2, and 13 are 484 * edge triggered and that everything else is level triggered. 485 * We only use the trigger information to reprogram the ELCR if 486 * we have one and as an optimization to avoid masking edge 487 * triggered interrupts. For the case that we don't have an ELCR, 488 * it doesn't hurt to mask an edge triggered interrupt, so we 489 * assume level trigger for any interrupt that we aren't sure is 490 * edge triggered. 491 */ 492 if (elcr_found) { 493 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) 494 ai->at_trigger = elcr_read_trigger(i); 495 } else { 496 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) 497 switch (i) { 498 case 0: 499 case 1: 500 case 2: 501 case 8: 502 case 13: 503 ai->at_trigger = INTR_TRIGGER_EDGE; 504 break; 505 default: 506 ai->at_trigger = INTR_TRIGGER_LEVEL; 507 break; 508 } 509 } 510#endif /* PC98 */ 511} 512 513static void 514atpic_init(void *dummy __unused) 515{ 516 struct atpic_intsrc *ai; 517 int i; 518 519 /* 520 * If any of the ISA IRQs have an interrupt source already, then 521 * assume that the APICs are being used and don't register any 522 * of our interrupt sources. This makes sure we don't accidentally 523 * use mixed mode. The "accidental" use could otherwise occur on 524 * machines that route the ACPI SCI interrupt to a different ISA 525 * IRQ (at least one machines routes it to IRQ 13) thus disabling 526 * that APIC ISA routing and allowing the ATPIC source for that IRQ 527 * to leak through. We used to depend on this feature for routing 528 * IRQ0 via mixed mode, but now we don't use mixed mode at all. 529 */ 530 for (i = 0; i < NUM_ISA_IRQS; i++) 531 if (intr_lookup_source(i) != NULL) 532 return; 533 534 /* Loop through all interrupt sources and add them. */ 535 for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) { 536 if (i == ICU_SLAVEID) 537 continue; 538 intr_register_source(&ai->at_intsrc); 539 } 540} 541SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL) 542 543void 544atpic_handle_intr(u_int vector, struct trapframe frame) 545{ 546 struct intsrc *isrc; 547 548 KASSERT(vector < NUM_ISA_IRQS, 549 ("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 mtx_lock_spin(&icu_lock); 565 outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS); 566 isr = inb(port); 567 outb(port, OCW3_SEL | OCW3_RR); 568 mtx_unlock_spin(&icu_lock); 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 */ 579static struct isa_pnp_id atpic_ids[] = { 580 { 0x0000d041 /* PNP0000 */, "AT interrupt controller" }, 581 { 0 } 582}; 583 584static int 585atpic_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 */ 604static int 605atpic_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 618static 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 629static driver_t atpic_driver = { 630 "atpic", 631 atpic_methods, 632 1, /* no softc */ 633}; 634 635static devclass_t atpic_devclass; 636 637DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0); 638#ifndef PC98 639DRIVER_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 */ 646intrmask_t 647isa_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 */ 657