nmi.c revision 46454
1/*- 2 * Copyright (c) 1991 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * William Jolitz. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 3. All advertising materials mentioning features or use of this software 17 * must display the following acknowledgement: 18 * This product includes software developed by the University of 19 * California, Berkeley and its contributors. 20 * 4. Neither the name of the University nor the names of its contributors 21 * may be used to endorse or promote products derived from this software 22 * without specific prior written permission. 23 * 24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 * SUCH DAMAGE. 35 * 36 * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 37 * $Id: intr_machdep.c,v 1.20 1999/04/23 21:01:19 peter Exp $ 38 */ 39/* 40 * This file contains an aggregated module marked: 41 * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 42 * All rights reserved. 43 * See the notice for details. 44 */ 45 46#include "opt_auto_eoi.h" 47 48#include <sys/param.h> 49#ifndef SMP 50#include <machine/lock.h> 51#endif 52#include <sys/systm.h> 53#include <sys/syslog.h> 54#include <sys/malloc.h> 55#include <sys/errno.h> 56#include <sys/interrupt.h> 57#include <machine/ipl.h> 58#include <machine/md_var.h> 59#include <machine/segments.h> 60#include <sys/bus.h> 61 62#if defined(APIC_IO) 63#include <machine/smp.h> 64#include <machine/smptests.h> /** FAST_HI */ 65#endif /* APIC_IO */ 66#include <i386/isa/isa_device.h> 67#ifdef PC98 68#include <pc98/pc98/pc98.h> 69#include <pc98/pc98/pc98_machdep.h> 70#include <pc98/pc98/epsonio.h> 71#else 72#include <i386/isa/isa.h> 73#endif 74#include <i386/isa/icu.h> 75 76#include <isa/isavar.h> 77#include <i386/isa/intr_machdep.h> 78#include <sys/interrupt.h> 79#ifdef APIC_IO 80#include <machine/clock.h> 81#endif 82 83/* XXX should be in suitable include files */ 84#ifdef PC98 85#define ICU_IMR_OFFSET 2 /* IO_ICU{1,2} + 2 */ 86#define ICU_SLAVEID 7 87#else 88#define ICU_IMR_OFFSET 1 /* IO_ICU{1,2} + 1 */ 89#define ICU_SLAVEID 2 90#endif 91 92#ifdef APIC_IO 93/* 94 * This is to accommodate "mixed-mode" programming for 95 * motherboards that don't connect the 8254 to the IO APIC. 96 */ 97#define AUTO_EOI_1 1 98#endif 99 100#define NR_INTRNAMES (1 + ICU_LEN + 2 * ICU_LEN) 101 102u_long *intr_countp[ICU_LEN]; 103inthand2_t *intr_handler[ICU_LEN]; 104u_int intr_mask[ICU_LEN]; 105static u_int* intr_mptr[ICU_LEN]; 106void *intr_unit[ICU_LEN]; 107 108static inthand_t *fastintr[ICU_LEN] = { 109 &IDTVEC(fastintr0), &IDTVEC(fastintr1), 110 &IDTVEC(fastintr2), &IDTVEC(fastintr3), 111 &IDTVEC(fastintr4), &IDTVEC(fastintr5), 112 &IDTVEC(fastintr6), &IDTVEC(fastintr7), 113 &IDTVEC(fastintr8), &IDTVEC(fastintr9), 114 &IDTVEC(fastintr10), &IDTVEC(fastintr11), 115 &IDTVEC(fastintr12), &IDTVEC(fastintr13), 116 &IDTVEC(fastintr14), &IDTVEC(fastintr15) 117#if defined(APIC_IO) 118 , &IDTVEC(fastintr16), &IDTVEC(fastintr17), 119 &IDTVEC(fastintr18), &IDTVEC(fastintr19), 120 &IDTVEC(fastintr20), &IDTVEC(fastintr21), 121 &IDTVEC(fastintr22), &IDTVEC(fastintr23) 122#endif /* APIC_IO */ 123}; 124 125static inthand_t *slowintr[ICU_LEN] = { 126 &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), 127 &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), 128 &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), 129 &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) 130#if defined(APIC_IO) 131 , &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19), 132 &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23) 133#endif /* APIC_IO */ 134}; 135 136static inthand2_t isa_strayintr; 137 138#ifdef PC98 139#define NMI_PARITY 0x04 140#define NMI_EPARITY 0x02 141#else 142#define NMI_PARITY (1 << 7) 143#define NMI_IOCHAN (1 << 6) 144#define ENMI_WATCHDOG (1 << 7) 145#define ENMI_BUSTIMER (1 << 6) 146#define ENMI_IOSTATUS (1 << 5) 147#endif 148 149/* 150 * Handle a NMI, possibly a machine check. 151 * return true to panic system, false to ignore. 152 */ 153int 154isa_nmi(cd) 155 int cd; 156{ 157#ifdef PC98 158 int port = inb(0x33); 159 if (epson_machine_id == 0x20) 160 epson_outb(0xc16, epson_inb(0xc16) | 0x1); 161 if (port & NMI_PARITY) { 162 panic("BASE RAM parity error, likely hardware failure."); 163 } else if (port & NMI_EPARITY) { 164 panic("EXTENDED RAM parity error, likely hardware failure."); 165 } else { 166 printf("\nNMI Resume ??\n"); 167 return(0); 168 } 169#else /* IBM-PC */ 170 int isa_port = inb(0x61); 171 int eisa_port = inb(0x461); 172 173 if (isa_port & NMI_PARITY) 174 panic("RAM parity error, likely hardware failure."); 175 176 if (isa_port & NMI_IOCHAN) 177 panic("I/O channel check, likely hardware failure."); 178 179 /* 180 * On a real EISA machine, this will never happen. However it can 181 * happen on ISA machines which implement XT style floating point 182 * error handling (very rare). Save them from a meaningless panic. 183 */ 184 if (eisa_port == 0xff) 185 return(0); 186 187 if (eisa_port & ENMI_WATCHDOG) 188 panic("EISA watchdog timer expired, likely hardware failure."); 189 190 if (eisa_port & ENMI_BUSTIMER) 191 panic("EISA bus timeout, likely hardware failure."); 192 193 if (eisa_port & ENMI_IOSTATUS) 194 panic("EISA I/O port status error."); 195 196 printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port); 197 return(0); 198#endif 199} 200 201/* 202 * Fill in default interrupt table (in case of spuruious interrupt 203 * during configuration of kernel, setup interrupt control unit 204 */ 205void 206isa_defaultirq() 207{ 208 int i; 209 210 /* icu vectors */ 211 for (i = 0; i < ICU_LEN; i++) 212 icu_unset(i, (inthand2_t *)NULL); 213 214 /* initialize 8259's */ 215 outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ 216 217 outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT); /* starting at this vector index */ 218 outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE); /* slave on line 7 */ 219#ifdef PC98 220#ifdef AUTO_EOI_1 221 outb(IO_ICU1+ICU_IMR_OFFSET, 0x1f); /* (master) auto EOI, 8086 mode */ 222#else 223 outb(IO_ICU1+ICU_IMR_OFFSET, 0x1d); /* (master) 8086 mode */ 224#endif 225#else /* IBM-PC */ 226#ifdef AUTO_EOI_1 227 outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ 228#else 229 outb(IO_ICU1+ICU_IMR_OFFSET, 1); /* 8086 mode */ 230#endif 231#endif /* PC98 */ 232 outb(IO_ICU1+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ 233 outb(IO_ICU1, 0x0a); /* default to IRR on read */ 234#ifndef PC98 235 outb(IO_ICU1, 0xc0 | (3 - 1)); /* pri order 3-7, 0-2 (com2 first) */ 236#endif /* !PC98 */ 237 238 outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ 239 outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */ 240 outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID); /* my slave id is 7 */ 241#ifdef PC98 242 outb(IO_ICU2+ICU_IMR_OFFSET,9); /* 8086 mode */ 243#else /* IBM-PC */ 244#ifdef AUTO_EOI_2 245 outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1); /* auto EOI, 8086 mode */ 246#else 247 outb(IO_ICU2+ICU_IMR_OFFSET,1); /* 8086 mode */ 248#endif 249#endif /* PC98 */ 250 outb(IO_ICU2+ICU_IMR_OFFSET, 0xff); /* leave interrupts masked */ 251 outb(IO_ICU2, 0x0a); /* default to IRR on read */ 252} 253 254/* 255 * Caught a stray interrupt, notify 256 */ 257static void 258isa_strayintr(vcookiep) 259 void *vcookiep; 260{ 261 int intr = (void **)vcookiep - &intr_unit[0]; 262 263 /* DON'T BOTHER FOR NOW! */ 264 /* for some reason, we get bursts of intr #7, even if not enabled! */ 265 /* 266 * Well the reason you got bursts of intr #7 is because someone 267 * raised an interrupt line and dropped it before the 8259 could 268 * prioritize it. This is documented in the intel data book. This 269 * means you have BAD hardware! I have changed this so that only 270 * the first 5 get logged, then it quits logging them, and puts 271 * out a special message. rgrimes 3/25/1993 272 */ 273 /* 274 * XXX TODO print a different message for #7 if it is for a 275 * glitch. Glitches can be distinguished from real #7's by 276 * testing that the in-service bit is _not_ set. The test 277 * must be done before sending an EOI so it can't be done if 278 * we are using AUTO_EOI_1. 279 */ 280 if (intrcnt[1 + intr] <= 5) 281 log(LOG_ERR, "stray irq %d\n", intr); 282 if (intrcnt[1 + intr] == 5) 283 log(LOG_CRIT, 284 "too many stray irq %d's; not logging any more\n", intr); 285} 286 287/* 288 * Return a bitmap of the current interrupt requests. This is 8259-specific 289 * and is only suitable for use at probe time. 290 */ 291intrmask_t 292isa_irq_pending() 293{ 294 u_char irr1; 295 u_char irr2; 296 297 irr1 = inb(IO_ICU1); 298 irr2 = inb(IO_ICU2); 299 return ((irr2 << 8) | irr1); 300} 301 302int 303update_intr_masks(void) 304{ 305 int intr, n=0; 306 u_int mask,*maskptr; 307 308 for (intr=0; intr < ICU_LEN; intr ++) { 309#if defined(APIC_IO) 310 /* no 8259 SLAVE to ignore */ 311#else 312 if (intr==ICU_SLAVEID) continue; /* ignore 8259 SLAVE output */ 313#endif /* APIC_IO */ 314 maskptr = intr_mptr[intr]; 315 if (!maskptr) 316 continue; 317 *maskptr |= 1 << intr; 318 mask = *maskptr; 319 if (mask != intr_mask[intr]) { 320#if 0 321 printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n", 322 intr, intr_mask[intr], mask, maskptr); 323#endif 324 intr_mask[intr]=mask; 325 n++; 326 } 327 328 } 329 return (n); 330} 331 332static void 333update_intrname(int intr, char *name) 334{ 335 char buf[32]; 336 char *cp; 337 int name_index, off, strayintr; 338 339 /* 340 * Initialise strings for bitbucket and stray interrupt counters. 341 * These have statically allocated indices 0 and 1 through ICU_LEN. 342 */ 343 if (intrnames[0] == '\0') { 344 off = sprintf(intrnames, "???") + 1; 345 for (strayintr = 0; strayintr < ICU_LEN; strayintr++) 346 off += sprintf(intrnames + off, "stray irq%d", 347 strayintr) + 1; 348 } 349 350 if (name == NULL) 351 name = "???"; 352 if (snprintf(buf, sizeof(buf), "%s irq%d", name, intr) >= sizeof(buf)) 353 goto use_bitbucket; 354 355 /* 356 * Search for `buf' in `intrnames'. In the usual case when it is 357 * not found, append it to the end if there is enough space (the \0 358 * terminator for the previous string, if any, becomes a separator). 359 */ 360 for (cp = intrnames, name_index = 0; 361 cp != eintrnames && name_index < NR_INTRNAMES; 362 cp += strlen(cp) + 1, name_index++) { 363 if (*cp == '\0') { 364 if (strlen(buf) >= eintrnames - cp) 365 break; 366 strcpy(cp, buf); 367 goto found; 368 } 369 if (strcmp(cp, buf) == 0) 370 goto found; 371 } 372 373use_bitbucket: 374 printf("update_intrname: counting %s irq%d as %s\n", name, intr, 375 intrnames); 376 name_index = 0; 377found: 378 intr_countp[intr] = &intrcnt[name_index]; 379} 380 381int 382icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags) 383{ 384#ifdef FAST_HI 385 int select; /* the select register is 8 bits */ 386 int vector; 387 u_int32_t value; /* the window register is 32 bits */ 388#endif /* FAST_HI */ 389 u_long ef; 390 u_int mask = (maskptr ? *maskptr : 0); 391 392#if defined(APIC_IO) 393 if ((u_int)intr >= ICU_LEN) /* no 8259 SLAVE to ignore */ 394#else 395 if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID) 396#endif /* APIC_IO */ 397 if (intr_handler[intr] != isa_strayintr) 398 return (EBUSY); 399 400 ef = read_eflags(); 401 disable_intr(); 402 intr_handler[intr] = handler; 403 intr_mptr[intr] = maskptr; 404 intr_mask[intr] = mask | (1 << intr); 405 intr_unit[intr] = arg; 406#ifdef FAST_HI 407 if (flags & INTR_FAST) { 408 vector = TPR_FAST_INTS + intr; 409 setidt(vector, fastintr[intr], 410 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 411 } 412 else { 413 vector = TPR_SLOW_INTS + intr; 414#ifdef APIC_INTR_REORDER 415#ifdef APIC_INTR_HIGHPRI_CLOCK 416 /* XXX: Hack (kludge?) for more accurate clock. */ 417 if (intr == apic_8254_intr || intr == 8) { 418 vector = TPR_FAST_INTS + intr; 419 } 420#endif 421#endif 422 setidt(vector, slowintr[intr], 423 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 424 } 425#ifdef APIC_INTR_REORDER 426 set_lapic_isrloc(intr, vector); 427#endif 428 /* 429 * Reprogram the vector in the IO APIC. 430 */ 431 if (int_to_apicintpin[intr].ioapic >= 0) { 432 select = int_to_apicintpin[intr].redirindex; 433 value = io_apic_read(int_to_apicintpin[intr].ioapic, 434 select) & ~IOART_INTVEC; 435 io_apic_write(int_to_apicintpin[intr].ioapic, 436 select, value | vector); 437 } 438#else 439 setidt(ICU_OFFSET + intr, 440 flags & INTR_FAST ? fastintr[intr] : slowintr[intr], 441 SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 442#endif /* FAST_HI */ 443 INTREN(1 << intr); 444 MPINTR_UNLOCK(); 445 write_eflags(ef); 446 return (0); 447} 448 449void 450register_imask(dvp, mask) 451 struct isa_device *dvp; 452 u_int mask; 453{ 454 if (dvp->id_alive && dvp->id_irq) { 455 int intr; 456 457 intr = ffs(dvp->id_irq) - 1; 458 intr_mask[intr] = mask | (1 <<intr); 459 } 460 (void) update_intr_masks(); 461} 462 463int 464icu_unset(intr, handler) 465 int intr; 466 inthand2_t *handler; 467{ 468 u_long ef; 469 470 if ((u_int)intr >= ICU_LEN || handler != intr_handler[intr]) 471 return (EINVAL); 472 473 INTRDIS(1 << intr); 474 ef = read_eflags(); 475 disable_intr(); 476 intr_countp[intr] = &intrcnt[1 + intr]; 477 intr_handler[intr] = isa_strayintr; 478 intr_mptr[intr] = NULL; 479 intr_mask[intr] = HWI_MASK | SWI_MASK; 480 intr_unit[intr] = &intr_unit[intr]; 481#ifdef FAST_HI_XXX 482 /* XXX how do I re-create dvp here? */ 483 setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr, 484 slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 485#else /* FAST_HI */ 486#ifdef APIC_INTR_REORDER 487 set_lapic_isrloc(intr, ICU_OFFSET + intr); 488#endif 489 setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, 490 GSEL(GCODE_SEL, SEL_KPL)); 491#endif /* FAST_HI */ 492 MPINTR_UNLOCK(); 493 write_eflags(ef); 494 return (0); 495} 496 497/* The following notice applies beyond this point in the file */ 498 499/* 500 * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 501 * All rights reserved. 502 * 503 * Redistribution and use in source and binary forms, with or without 504 * modification, are permitted provided that the following conditions 505 * are met: 506 * 1. Redistributions of source code must retain the above copyright 507 * notice unmodified, this list of conditions, and the following 508 * disclaimer. 509 * 2. Redistributions in binary form must reproduce the above copyright 510 * notice, this list of conditions and the following disclaimer in the 511 * documentation and/or other materials provided with the distribution. 512 * 513 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 514 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 515 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 516 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 517 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 518 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 519 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 520 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 521 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 522 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 523 * 524 * $Id: intr_machdep.c,v 1.20 1999/04/23 21:01:19 peter Exp $ 525 * 526 */ 527 528typedef struct intrec { 529 intrmask_t mask; 530 inthand2_t *handler; 531 void *argument; 532 struct intrec *next; 533 char *name; 534 int intr; 535 intrmask_t *maskptr; 536 int flags; 537} intrec; 538 539static intrec *intreclist_head[ICU_LEN]; 540 541typedef struct isarec { 542 int id_unit; 543 ointhand2_t *id_handler; 544} isarec; 545 546static isarec *isareclist[ICU_LEN]; 547 548/* 549 * The interrupt multiplexer calls each of the handlers in turn, 550 * and applies the associated interrupt mask to "cpl", which is 551 * defined as a ".long" in /sys/i386/isa/ipl.s 552 */ 553 554static void 555intr_mux(void *arg) 556{ 557 intrec *p = arg; 558 int oldspl; 559 560 while (p != NULL) { 561 oldspl = splq(p->mask); 562 p->handler(p->argument); 563 splx(oldspl); 564 p = p->next; 565 } 566} 567 568static void 569isa_intr_wrap(void *cookie) 570{ 571 isarec *irec = (isarec *)cookie; 572 573 irec->id_handler(irec->id_unit); 574} 575 576static intrec* 577find_idesc(unsigned *maskptr, int irq) 578{ 579 intrec *p = intreclist_head[irq]; 580 581 while (p && p->maskptr != maskptr) 582 p = p->next; 583 584 return (p); 585} 586 587static intrec** 588find_pred(intrec *idesc, int irq) 589{ 590 intrec **pp = &intreclist_head[irq]; 591 intrec *p = *pp; 592 593 while (p != idesc) { 594 if (p == NULL) 595 return (NULL); 596 pp = &p->next; 597 p = *pp; 598 } 599 return (pp); 600} 601 602/* 603 * Both the low level handler and the shared interrupt multiplexer 604 * block out further interrupts as set in the handlers "mask", while 605 * the handler is running. In fact *maskptr should be used for this 606 * purpose, but since this requires one more pointer dereference on 607 * each interrupt, we rather bother update "mask" whenever *maskptr 608 * changes. The function "update_masks" should be called **after** 609 * all manipulation of the linked list of interrupt handlers hung 610 * off of intrdec_head[irq] is complete, since the chain of handlers 611 * will both determine the *maskptr values and the instances of mask 612 * that are fixed. This function should be called with the irq for 613 * which a new handler has been add blocked, since the masks may not 614 * yet know about the use of this irq for a device of a certain class. 615 */ 616 617static void 618update_mux_masks(void) 619{ 620 int irq; 621 for (irq = 0; irq < ICU_LEN; irq++) { 622 intrec *idesc = intreclist_head[irq]; 623 while (idesc != NULL) { 624 if (idesc->maskptr != NULL) { 625 /* our copy of *maskptr may be stale, refresh */ 626 idesc->mask = *idesc->maskptr; 627 } 628 idesc = idesc->next; 629 } 630 } 631} 632 633static void 634update_masks(intrmask_t *maskptr, int irq) 635{ 636 intrmask_t mask = 1 << irq; 637 638 if (maskptr == NULL) 639 return; 640 641 if (find_idesc(maskptr, irq) == NULL) { 642 /* no reference to this maskptr was found in this irq's chain */ 643 if ((*maskptr & mask) == 0) 644 return; 645 /* the irq was included in the classes mask, remove it */ 646 INTRUNMASK(*maskptr, mask); 647 } else { 648 /* a reference to this maskptr was found in this irq's chain */ 649 if ((*maskptr & mask) != 0) 650 return; 651 /* put the irq into the classes mask */ 652 INTRMASK(*maskptr, mask); 653 } 654 /* we need to update all values in the intr_mask[irq] array */ 655 update_intr_masks(); 656 /* update mask in chains of the interrupt multiplex handler as well */ 657 update_mux_masks(); 658} 659 660/* 661 * Add interrupt handler to linked list hung off of intreclist_head[irq] 662 * and install shared interrupt multiplex handler, if necessary 663 */ 664 665static int 666add_intrdesc(intrec *idesc) 667{ 668 int irq = idesc->intr; 669 670 intrec *head = intreclist_head[irq]; 671 672 if (head == NULL) { 673 /* first handler for this irq, just install it */ 674 if (icu_setup(irq, idesc->handler, idesc->argument, 675 idesc->maskptr, idesc->flags) != 0) 676 return (-1); 677 678 update_intrname(irq, idesc->name); 679 /* keep reference */ 680 intreclist_head[irq] = idesc; 681 } else { 682 if ((idesc->flags & INTR_EXCL) != 0 683 || (head->flags & INTR_EXCL) != 0) { 684 /* 685 * can't append new handler, if either list head or 686 * new handler do not allow interrupts to be shared 687 */ 688 if (bootverbose) 689 printf("\tdevice combination doesn't support " 690 "shared irq%d\n", irq); 691 return (-1); 692 } 693 if (head->next == NULL) { 694 /* 695 * second handler for this irq, replace device driver's 696 * handler by shared interrupt multiplexer function 697 */ 698 icu_unset(irq, head->handler); 699 if (icu_setup(irq, intr_mux, head, 0, 0) != 0) 700 return (-1); 701 if (bootverbose) 702 printf("\tusing shared irq%d.\n", irq); 703 update_intrname(irq, "mux"); 704 } 705 /* just append to the end of the chain */ 706 while (head->next != NULL) 707 head = head->next; 708 head->next = idesc; 709 } 710 update_masks(idesc->maskptr, irq); 711 return (0); 712} 713 714/* 715 * Create and activate an interrupt handler descriptor data structure. 716 * 717 * The dev_instance pointer is required for resource management, and will 718 * only be passed through to resource_claim(). 719 * 720 * There will be functions that derive a driver and unit name from a 721 * dev_instance variable, and those functions will be used to maintain the 722 * interrupt counter label array referenced by systat and vmstat to report 723 * device interrupt rates (->update_intrlabels). 724 * 725 * Add the interrupt handler descriptor data structure created by an 726 * earlier call of create_intr() to the linked list for its irq and 727 * adjust the interrupt masks if necessary. 728 */ 729 730intrec * 731inthand_add(const char *name, int irq, inthand2_t handler, void *arg, 732 intrmask_t *maskptr, int flags) 733{ 734 intrec *idesc; 735 int errcode = -1; 736 intrmask_t oldspl; 737 738 if (ICU_LEN > 8 * sizeof *maskptr) { 739 printf("create_intr: ICU_LEN of %d too high for %d bit intrmask\n", 740 ICU_LEN, 8 * sizeof *maskptr); 741 return (NULL); 742 } 743 if ((unsigned)irq >= ICU_LEN) { 744 printf("create_intr: requested irq%d too high, limit is %d\n", 745 irq, ICU_LEN -1); 746 return (NULL); 747 } 748 749 idesc = malloc(sizeof *idesc, M_DEVBUF, M_WAITOK); 750 if (idesc == NULL) 751 return NULL; 752 bzero(idesc, sizeof *idesc); 753 754 if (name == NULL) 755 name = "???"; 756 idesc->name = malloc(strlen(name) + 1, M_DEVBUF, M_WAITOK); 757 if (idesc->name == NULL) { 758 free(idesc, M_DEVBUF); 759 return NULL; 760 } 761 strcpy(idesc->name, name); 762 763 idesc->handler = handler; 764 idesc->argument = arg; 765 idesc->maskptr = maskptr; 766 idesc->intr = irq; 767 idesc->flags = flags; 768 769 /* block this irq */ 770 oldspl = splq(1 << irq); 771 772 /* add irq to class selected by maskptr */ 773 errcode = add_intrdesc(idesc); 774 splx(oldspl); 775 776 if (errcode != 0) { 777 if (bootverbose) 778 printf("\tintr_connect(irq%d) failed, result=%d\n", 779 irq, errcode); 780 free(idesc->name, M_DEVBUF); 781 free(idesc, M_DEVBUF); 782 idesc = NULL; 783 } 784 785 return (idesc); 786} 787 788/* 789 * Deactivate and remove the interrupt handler descriptor data connected 790 * created by an earlier call of intr_connect() from the linked list and 791 * adjust theinterrupt masks if necessary. 792 * 793 * Return the memory held by the interrupt handler descriptor data structure 794 * to the system. Make sure, the handler is not actively used anymore, before. 795 */ 796 797int 798inthand_remove(intrec *idesc) 799{ 800 intrec **hook, *head; 801 int irq; 802 int errcode = 0; 803 intrmask_t oldspl; 804 805 if (idesc == NULL) 806 return (-1); 807 808 irq = idesc->intr; 809 810 /* find pointer that keeps the reference to this interrupt descriptor */ 811 hook = find_pred(idesc, irq); 812 if (hook == NULL) 813 return (-1); 814 815 /* make copy of original list head, the line after may overwrite it */ 816 head = intreclist_head[irq]; 817 818 /* unlink: make predecessor point to idesc->next instead of to idesc */ 819 *hook = idesc->next; 820 821 /* now check whether the element we removed was the list head */ 822 if (idesc == head) { 823 824 oldspl = splq(1 << irq); 825 826 /* we want to remove the list head, which was known to intr_mux */ 827 icu_unset(irq, intr_mux); 828 829 /* check whether the new list head is the only element on list */ 830 head = intreclist_head[irq]; 831 if (head != NULL) { 832 if (head->next != NULL) { 833 /* install the multiplex handler with new list head as argument */ 834 errcode = icu_setup(irq, intr_mux, head, 0, 0); 835 if (errcode == 0) 836 update_intrname(irq, NULL); 837 } else { 838 /* install the one remaining handler for this irq */ 839 errcode = icu_setup(irq, head->handler, 840 head->argument, 841 head->maskptr, head->flags); 842 if (errcode == 0) 843 update_intrname(irq, head->name); 844 } 845 } 846 splx(oldspl); 847 } 848 update_masks(idesc->maskptr, irq); 849 850 free(idesc, M_DEVBUF); 851 return (0); 852} 853 854/* 855 * Emulate the register_intr() call previously defined as low level function. 856 * That function (now icu_setup()) may no longer be directly called, since 857 * a conflict between an ISA and PCI interrupt might go by unnocticed, else. 858 */ 859 860int 861register_intr(int intr, int device_id, u_int flags, 862 ointhand2_t handler, u_int *maskptr, int unit) 863{ 864 intrec *idesc; 865 isarec *irec; 866 867 irec = malloc(sizeof *irec, M_DEVBUF, M_WAITOK); 868 if (irec == NULL) 869 return NULL; 870 bzero(irec, sizeof *irec); 871 irec->id_unit = unit; 872 irec->id_handler = handler; 873 874 flags |= INTR_EXCL; 875 idesc = inthand_add("old", intr, isa_intr_wrap, irec, maskptr, flags); 876 if (idesc == NULL) { 877 free(irec, M_DEVBUF); 878 return -1; 879 } 880 isareclist[intr] = irec; 881 return 0; 882} 883 884/* 885 * Emulate the old unregister_intr() low level function. 886 * Make sure there is just one interrupt, that it was 887 * registered as non-shared, and that the handlers match. 888 */ 889 890int 891unregister_intr(int intr, ointhand2_t handler) 892{ 893 intrec *p = intreclist_head[intr]; 894 895 if (p != NULL && (p->flags & INTR_EXCL) != 0 && 896 p->handler == isa_intr_wrap && isareclist[intr] != NULL && 897 isareclist[intr]->id_handler == handler) { 898 free(isareclist[intr], M_DEVBUF); 899 isareclist[intr] = NULL; 900 return (inthand_remove(p)); 901 } 902 return (EINVAL); 903} 904