The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/i386/isa/intr_machdep.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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  * $FreeBSD$
   38  */
   39 
   40 #include "opt_auto_eoi.h"
   41 
   42 #include <sys/param.h>
   43 #ifndef SMP
   44 #include <machine/lock.h>
   45 #endif
   46 #include <sys/systm.h>
   47 #include <sys/syslog.h>
   48 #include <machine/ipl.h>
   49 #include <machine/md_var.h>
   50 #include <machine/segments.h>
   51 #if defined(APIC_IO)
   52 #include <machine/smp.h>
   53 #include <machine/smptests.h>                   /** FAST_HI */
   54 #endif /* APIC_IO */
   55 #include <i386/isa/isa_device.h>
   56 #ifdef PC98
   57 #include <pc98/pc98/pc98.h>
   58 #include <pc98/pc98/pc98_machdep.h>
   59 #include <pc98/pc98/epsonio.h>
   60 #else
   61 #include <i386/isa/isa.h>
   62 #endif
   63 #include <i386/isa/icu.h>
   64 #include "vector.h"
   65 
   66 #include <i386/isa/intr_machdep.h>
   67 #include <sys/interrupt.h>
   68 #ifdef APIC_IO
   69 #include <machine/clock.h>
   70 #endif
   71 
   72 /* XXX should be in suitable include files */
   73 #ifdef PC98
   74 #define ICU_IMR_OFFSET          2               /* IO_ICU{1,2} + 2 */
   75 #define ICU_SLAVEID                     7
   76 #else
   77 #define ICU_IMR_OFFSET          1               /* IO_ICU{1,2} + 1 */
   78 #define ICU_SLAVEID                     2
   79 #endif
   80 
   81 #ifdef APIC_IO
   82 /*
   83  * This is to accommodate "mixed-mode" programming for 
   84  * motherboards that don't connect the 8254 to the IO APIC.
   85  */
   86 #define AUTO_EOI_1      1
   87 #endif
   88 
   89 u_long  *intr_countp[ICU_LEN];
   90 inthand2_t *intr_handler[ICU_LEN];
   91 u_int   intr_mask[ICU_LEN];
   92 static u_int*   intr_mptr[ICU_LEN];
   93 void    *intr_unit[ICU_LEN];
   94 
   95 static inthand_t *fastintr[ICU_LEN] = {
   96         &IDTVEC(fastintr0), &IDTVEC(fastintr1),
   97         &IDTVEC(fastintr2), &IDTVEC(fastintr3),
   98         &IDTVEC(fastintr4), &IDTVEC(fastintr5),
   99         &IDTVEC(fastintr6), &IDTVEC(fastintr7),
  100         &IDTVEC(fastintr8), &IDTVEC(fastintr9),
  101         &IDTVEC(fastintr10), &IDTVEC(fastintr11),
  102         &IDTVEC(fastintr12), &IDTVEC(fastintr13),
  103         &IDTVEC(fastintr14), &IDTVEC(fastintr15)
  104 #if defined(APIC_IO)
  105         , &IDTVEC(fastintr16), &IDTVEC(fastintr17),
  106         &IDTVEC(fastintr18), &IDTVEC(fastintr19),
  107         &IDTVEC(fastintr20), &IDTVEC(fastintr21),
  108         &IDTVEC(fastintr22), &IDTVEC(fastintr23)
  109 #endif /* APIC_IO */
  110 };
  111 
  112 static inthand_t *slowintr[ICU_LEN] = {
  113         &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3),
  114         &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7),
  115         &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11),
  116         &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15)
  117 #if defined(APIC_IO)
  118         , &IDTVEC(intr16), &IDTVEC(intr17), &IDTVEC(intr18), &IDTVEC(intr19),
  119         &IDTVEC(intr20), &IDTVEC(intr21), &IDTVEC(intr22), &IDTVEC(intr23)
  120 #endif /* APIC_IO */
  121 };
  122 
  123 static inthand2_t isa_strayintr;
  124 
  125 #ifdef PC98
  126 #define NMI_PARITY 0x04
  127 #define NMI_EPARITY 0x02
  128 #else
  129 #define NMI_PARITY (1 << 7)
  130 #define NMI_IOCHAN (1 << 6)
  131 #define ENMI_WATCHDOG (1 << 7)
  132 #define ENMI_BUSTIMER (1 << 6)
  133 #define ENMI_IOSTATUS (1 << 5)
  134 #endif
  135 
  136 /*
  137  * Handle a NMI, possibly a machine check.
  138  * return true to panic system, false to ignore.
  139  */
  140 int
  141 isa_nmi(cd)
  142         int cd;
  143 {
  144 #ifdef PC98
  145         int port = inb(0x33);
  146         if (epson_machine_id == 0x20)
  147                 epson_outb(0xc16, epson_inb(0xc16) | 0x1);
  148         if (port & NMI_PARITY) {
  149                 panic("BASE RAM parity error, likely hardware failure.");
  150         } else if (port & NMI_EPARITY) {
  151                 panic("EXTENDED RAM parity error, likely hardware failure.");
  152         } else {
  153                 printf("\nNMI Resume ??\n");
  154                 return(0);
  155         }
  156 #else /* IBM-PC */
  157         int isa_port = inb(0x61);
  158         int eisa_port = inb(0x461);
  159 
  160         if (isa_port & NMI_PARITY)
  161                 panic("RAM parity error, likely hardware failure.");
  162 
  163         if (isa_port & NMI_IOCHAN)
  164                 panic("I/O channel check, likely hardware failure.");
  165 
  166         /*
  167          * On a real EISA machine, this will never happen.  However it can
  168          * happen on ISA machines which implement XT style floating point
  169          * error handling (very rare).  Save them from a meaningless panic.
  170          */
  171         if (eisa_port == 0xff)
  172                 return(0);
  173 
  174         if (eisa_port & ENMI_WATCHDOG)
  175                 panic("EISA watchdog timer expired, likely hardware failure.");
  176 
  177         if (eisa_port & ENMI_BUSTIMER)
  178                 panic("EISA bus timeout, likely hardware failure.");
  179 
  180         if (eisa_port & ENMI_IOSTATUS)
  181                 panic("EISA I/O port status error.");
  182 
  183         printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port);
  184         return(0);
  185 #endif
  186 }
  187 
  188 /*
  189  * Fill in default interrupt table (in case of spuruious interrupt
  190  * during configuration of kernel, setup interrupt control unit
  191  */
  192 void
  193 isa_defaultirq()
  194 {
  195         int i;
  196 
  197         /* icu vectors */
  198         for (i = 0; i < ICU_LEN; i++)
  199                 icu_unset(i, (inthand2_t *)NULL);
  200 
  201         /* initialize 8259's */
  202         outb(IO_ICU1, 0x11);            /* reset; program device, four bytes */
  203 
  204         outb(IO_ICU1+ICU_IMR_OFFSET, NRSVIDT);  /* starting at this vector index */
  205         outb(IO_ICU1+ICU_IMR_OFFSET, IRQ_SLAVE);                /* slave on line 7 */
  206 #ifdef PC98
  207 #ifdef AUTO_EOI_1
  208         outb(IO_ICU1+ICU_IMR_OFFSET, 0x1f);             /* (master) auto EOI, 8086 mode */
  209 #else
  210         outb(IO_ICU1+ICU_IMR_OFFSET, 0x1d);             /* (master) 8086 mode */
  211 #endif
  212 #else /* IBM-PC */
  213 #ifdef AUTO_EOI_1
  214         outb(IO_ICU1+ICU_IMR_OFFSET, 2 | 1);            /* auto EOI, 8086 mode */
  215 #else
  216         outb(IO_ICU1+ICU_IMR_OFFSET, 1);                /* 8086 mode */
  217 #endif
  218 #endif /* PC98 */
  219         outb(IO_ICU1+ICU_IMR_OFFSET, 0xff);             /* leave interrupts masked */
  220         outb(IO_ICU1, 0x0a);            /* default to IRR on read */
  221 #ifndef PC98
  222         outb(IO_ICU1, 0xc0 | (3 - 1));  /* pri order 3-7, 0-2 (com2 first) */
  223 #endif /* !PC98 */
  224 
  225         outb(IO_ICU2, 0x11);            /* reset; program device, four bytes */
  226         outb(IO_ICU2+ICU_IMR_OFFSET, NRSVIDT+8); /* staring at this vector index */
  227         outb(IO_ICU2+ICU_IMR_OFFSET, ICU_SLAVEID);         /* my slave id is 7 */
  228 #ifdef PC98
  229         outb(IO_ICU2+ICU_IMR_OFFSET,9);              /* 8086 mode */
  230 #else /* IBM-PC */
  231 #ifdef AUTO_EOI_2
  232         outb(IO_ICU2+ICU_IMR_OFFSET, 2 | 1);            /* auto EOI, 8086 mode */
  233 #else
  234         outb(IO_ICU2+ICU_IMR_OFFSET,1);         /* 8086 mode */
  235 #endif
  236 #endif /* PC98 */
  237         outb(IO_ICU2+ICU_IMR_OFFSET, 0xff);          /* leave interrupts masked */
  238         outb(IO_ICU2, 0x0a);            /* default to IRR on read */
  239 }
  240 
  241 /*
  242  * Caught a stray interrupt, notify
  243  */
  244 static void
  245 isa_strayintr(vcookiep)
  246         void *vcookiep;
  247 {
  248         int intr = (void **)vcookiep - &intr_unit[0];
  249 
  250         /* DON'T BOTHER FOR NOW! */
  251         /* for some reason, we get bursts of intr #7, even if not enabled! */
  252         /*
  253          * Well the reason you got bursts of intr #7 is because someone
  254          * raised an interrupt line and dropped it before the 8259 could
  255          * prioritize it.  This is documented in the intel data book.  This
  256          * means you have BAD hardware!  I have changed this so that only
  257          * the first 5 get logged, then it quits logging them, and puts
  258          * out a special message. rgrimes 3/25/1993
  259          */
  260         /*
  261          * XXX TODO print a different message for #7 if it is for a
  262          * glitch.  Glitches can be distinguished from real #7's by
  263          * testing that the in-service bit is _not_ set.  The test
  264          * must be done before sending an EOI so it can't be done if
  265          * we are using AUTO_EOI_1.
  266          */
  267         if (intrcnt[NR_DEVICES + intr] <= 5)
  268                 log(LOG_ERR, "stray irq %d\n", intr);
  269         if (intrcnt[NR_DEVICES + intr] == 5)
  270                 log(LOG_CRIT,
  271                     "too many stray irq %d's; not logging any more\n", intr);
  272 }
  273 
  274 /*
  275  * Return a bitmap of the current interrupt requests.  This is 8259-specific
  276  * and is only suitable for use at probe time.
  277  */
  278 intrmask_t
  279 isa_irq_pending()
  280 {
  281         u_char irr1;
  282         u_char irr2;
  283 
  284         irr1 = inb(IO_ICU1);
  285         irr2 = inb(IO_ICU2);
  286         return ((irr2 << 8) | irr1);
  287 }
  288 
  289 int
  290 update_intr_masks(void)
  291 {
  292         int intr, n=0;
  293         u_int mask,*maskptr;
  294 
  295         for (intr=0; intr < ICU_LEN; intr ++) {
  296 #if defined(APIC_IO)
  297                 /* no 8259 SLAVE to ignore */
  298 #else
  299                 if (intr==ICU_SLAVEID) continue;        /* ignore 8259 SLAVE output */
  300 #endif /* APIC_IO */
  301                 maskptr = intr_mptr[intr];
  302                 if (!maskptr) continue;
  303                 *maskptr |= SWI_CLOCK_MASK | (1 << intr);
  304                 mask = *maskptr;
  305                 if (mask != intr_mask[intr]) {
  306 #if 0
  307                         printf ("intr_mask[%2d] old=%08x new=%08x ptr=%p.\n",
  308                                 intr, intr_mask[intr], mask, maskptr);
  309 #endif
  310                         intr_mask[intr]=mask;
  311                         n++;
  312                 }
  313 
  314         }
  315         return (n);
  316 }
  317 
  318 /*
  319  * The find_device_id function is only required because of the way the
  320  * device names are currently stored for reporting in systat or vmstat.
  321  * In fact, those programs should be modified to use the sysctl interface
  322  * to obtain a list of driver names by traversing intreclist_head[irq].
  323  */
  324 static int
  325 find_device_id(int irq)
  326 {
  327         char buf[16];
  328         char *cp;
  329         int free_id, id;
  330 
  331         snprintf(buf, sizeof(buf), "pci irq%d", irq);
  332         cp = intrnames;
  333         /* default to 0, which corresponds to clk0 */
  334         free_id = 0;
  335 
  336         for (id = 0; id < NR_DEVICES; id++) {
  337                 if (strcmp(cp, buf) == 0)
  338                         return (id);
  339                 if (free_id == 0 && strcmp(cp, "pci irqnn") == 0)
  340                         free_id = id;
  341                 while (*cp++ != '\0');
  342         }
  343 #if 0
  344         if (free_id == 0) {
  345                 /*
  346                  * All pci irq counters are in use, perhaps because config
  347                  * is old so there aren't any. Abuse the clk0 counter.
  348                  */
  349                 printf("\tcounting shared irq%d as clk0 irq\n", irq);
  350         }
  351 #endif
  352         return (free_id);
  353 }
  354 
  355 void
  356 update_intrname(int intr, int device_id)
  357 {
  358         char    *cp;
  359         int     id;
  360 
  361         if (device_id == -1)
  362                 device_id = find_device_id(intr);
  363 
  364         if ((u_int)device_id >= NR_DEVICES)
  365                 return;
  366 
  367         intr_countp[intr] = &intrcnt[device_id];
  368 
  369         for (cp = intrnames, id = 0; id <= device_id; id++)
  370                 while (*cp++ != '\0')
  371                         ;
  372         if (cp > eintrnames)
  373                 return;
  374         if (intr < 10) {
  375                 cp[-3] = intr + '';
  376                 cp[-2] = ' ';
  377         } else if (intr < 20) {
  378                 cp[-3] = '1';
  379                 cp[-2] = intr - 10 + '';
  380         } else {
  381                 cp[-3] = '2';
  382                 cp[-2] = intr - 20 + '';
  383         }
  384 }
  385 
  386 
  387 int
  388 icu_setup(int intr, inthand2_t *handler, void *arg, u_int *maskptr, int flags)
  389 {
  390 #ifdef FAST_HI
  391         int             select;         /* the select register is 8 bits */
  392         int             vector;
  393         u_int32_t       value;          /* the window register is 32 bits */
  394 #endif /* FAST_HI */
  395         u_long  ef;
  396         u_int   mask = (maskptr ? *maskptr : 0);
  397 
  398 #if defined(APIC_IO)
  399         if ((u_int)intr >= ICU_LEN)     /* no 8259 SLAVE to ignore */
  400 #else
  401         if ((u_int)intr >= ICU_LEN || intr == ICU_SLAVEID)
  402 #endif /* APIC_IO */
  403         if (intr_handler[intr] != isa_strayintr)
  404                 return (EBUSY);
  405 
  406         ef = read_eflags();
  407         disable_intr();
  408         intr_handler[intr] = handler;
  409         intr_mptr[intr] = maskptr;
  410         intr_mask[intr] = mask | SWI_CLOCK_MASK | (1 << intr);
  411         intr_unit[intr] = arg;
  412 #ifdef FAST_HI
  413         if (flags & INTR_FAST) {
  414                 vector = TPR_FAST_INTS + intr;
  415                 setidt(vector, fastintr[intr],
  416                        SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
  417         }
  418         else {
  419                 vector = TPR_SLOW_INTS + intr;
  420 #ifdef APIC_INTR_REORDER
  421 #ifdef APIC_INTR_HIGHPRI_CLOCK
  422                 /* XXX: Hack (kludge?) for more accurate clock. */
  423                 if (intr == apic_8254_intr || intr == 8) {
  424                         vector = TPR_FAST_INTS + intr;
  425                 }
  426 #endif
  427 #endif
  428                 setidt(vector, slowintr[intr],
  429                        SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
  430         }
  431 #ifdef APIC_INTR_REORDER
  432         set_lapic_isrloc(intr, vector);
  433 #endif
  434         /*
  435          * Reprogram the vector in the IO APIC.
  436          */
  437         if (int_to_apicintpin[intr].ioapic >= 0) {
  438                 select = int_to_apicintpin[intr].redirindex;
  439                 value = io_apic_read(int_to_apicintpin[intr].ioapic, 
  440                                      select) & ~IOART_INTVEC;
  441                 io_apic_write(int_to_apicintpin[intr].ioapic, 
  442                               select, value | vector);
  443         }
  444 #else
  445         setidt(ICU_OFFSET + intr,
  446                flags & INTR_FAST ? fastintr[intr] : slowintr[intr],
  447                SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
  448 #endif /* FAST_HI */
  449         INTREN(1 << intr);
  450         MPINTR_UNLOCK();
  451         write_eflags(ef);
  452         return (0);
  453 }
  454 
  455 void
  456 register_imask(dvp, mask)
  457         struct isa_device *dvp;
  458         u_int   mask;
  459 {
  460         if (dvp->id_alive && dvp->id_irq) {
  461                 int     intr;
  462 
  463                 intr = ffs(dvp->id_irq) - 1;
  464                 intr_mask[intr] = mask | (1 <<intr);
  465         }
  466         (void) update_intr_masks();
  467 }
  468 
  469 int
  470 icu_unset(intr, handler)
  471         int     intr;
  472         inthand2_t *handler;
  473 {
  474         u_long  ef;
  475 
  476         if ((u_int)intr >= ICU_LEN || handler != intr_handler[intr])
  477                 return (EINVAL);
  478 
  479         INTRDIS(1 << intr);
  480         ef = read_eflags();
  481         disable_intr();
  482         intr_countp[intr] = &intrcnt[NR_DEVICES + intr];
  483         intr_handler[intr] = isa_strayintr;
  484         intr_mptr[intr] = NULL;
  485         intr_mask[intr] = HWI_MASK | SWI_MASK;
  486         intr_unit[intr] = &intr_unit[intr];
  487 #ifdef FAST_HI_XXX
  488         /* XXX how do I re-create dvp here? */
  489         setidt(flags & INTR_FAST ? TPR_FAST_INTS + intr : TPR_SLOW_INTS + intr,
  490             slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL));
  491 #else /* FAST_HI */
  492 #ifdef APIC_INTR_REORDER
  493         set_lapic_isrloc(intr, ICU_OFFSET + intr);
  494 #endif
  495         setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL,
  496             GSEL(GCODE_SEL, SEL_KPL));
  497 #endif /* FAST_HI */
  498         MPINTR_UNLOCK();
  499         write_eflags(ef);
  500         return (0);
  501 }

Cache object: 4a80e45668ac00a4229c8edca7e94701


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.