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/atpic.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) 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: releng/5.2/sys/i386/isa/atpic.c 122898 2003-11-19 15:40:23Z 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/mutex.h>
   47 
   48 #include <machine/cpufunc.h>
   49 #include <machine/frame.h>
   50 #include <machine/intr_machdep.h>
   51 #include <machine/md_var.h>
   52 #include <machine/resource.h>
   53 #include <machine/segments.h>
   54 
   55 #include <i386/isa/icu.h>
   56 #ifdef PC98
   57 #include <pc98/pc98/pc98.h>
   58 #else
   59 #include <i386/isa/isa.h>
   60 #endif
   61 #include <isa/isavar.h>
   62 
   63 #define MASTER  0
   64 #define SLAVE   1
   65 
   66 /* XXX: Magic numbers */
   67 #ifdef PC98
   68 #ifdef AUTO_EOI_1
   69 #define MASTER_MODE     0x1f    /* Master auto EOI, 8086 mode */
   70 #else
   71 #define MASTER_MODE     0x1d    /* Master 8086 mode */
   72 #endif
   73 #define SLAVE_MODE      9       /* 8086 mode */
   74 #else /* IBM-PC */
   75 #ifdef AUTO_EOI_1
   76 #define MASTER_MODE     (ICW4_8086 | ICW4_AEOI)
   77 #else
   78 #define MASTER_MODE     ICW4_8086
   79 #endif
   80 #ifdef AUTO_EOI_2
   81 #define SLAVE_MODE      (ICW4_8086 | ICW4_AEOI)
   82 #else
   83 #define SLAVE_MODE      ICW4_8086
   84 #endif
   85 #endif /* PC98 */
   86 
   87 static void     atpic_init(void *dummy);
   88 
   89 unsigned int imen;      /* XXX */
   90 
   91 inthand_t
   92         IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2),
   93         IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5),
   94         IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8),
   95         IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11),
   96         IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14),
   97         IDTVEC(atpic_intr15);
   98 
   99 #define IRQ(ap, ai)     ((ap)->at_irqbase + (ai)->at_irq)
  100 
  101 #define ATPIC(io, base, eoi, imenptr)                           \
  102         { { atpic_enable_source, atpic_disable_source, (eoi),           \
  103             atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \
  104             atpic_resume }, (io), (base), IDT_IO_INTS + (base), (imenptr) }
  105 
  106 #define INTSRC(irq)                                                     \
  107         { { &atpics[(irq) / 8].at_pic }, (irq) % 8,                     \
  108             IDTVEC(atpic_intr ## irq ) }
  109 
  110 struct atpic {
  111         struct pic at_pic;
  112         int     at_ioaddr;
  113         int     at_irqbase;
  114         uint8_t at_intbase;
  115         uint8_t *at_imen;
  116 };
  117 
  118 struct atpic_intsrc {
  119         struct intsrc at_intsrc;
  120         int     at_irq;         /* Relative to PIC base. */
  121         inthand_t *at_intr;
  122         u_long  at_count;
  123         u_long  at_straycount;
  124 };
  125 
  126 static void atpic_enable_source(struct intsrc *isrc);
  127 static void atpic_disable_source(struct intsrc *isrc);
  128 static void atpic_eoi_master(struct intsrc *isrc);
  129 static void atpic_eoi_slave(struct intsrc *isrc);
  130 static void atpic_enable_intr(struct intsrc *isrc);
  131 static int atpic_vector(struct intsrc *isrc);
  132 static void atpic_resume(struct intsrc *isrc);
  133 static int atpic_source_pending(struct intsrc *isrc);
  134 static void i8259_init(struct atpic *pic, int slave);
  135 
  136 static struct atpic atpics[] = {
  137         ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen),
  138         ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1)
  139 };
  140 
  141 static struct atpic_intsrc atintrs[] = {
  142         INTSRC(0),
  143         INTSRC(1),
  144         INTSRC(2),
  145         INTSRC(3),
  146         INTSRC(4),
  147         INTSRC(5),
  148         INTSRC(6),
  149         INTSRC(7),
  150         INTSRC(8),
  151         INTSRC(9),
  152         INTSRC(10),
  153         INTSRC(11),
  154         INTSRC(12),
  155         INTSRC(13),
  156         INTSRC(14),
  157         INTSRC(15),
  158 };
  159 
  160 static void
  161 atpic_enable_source(struct intsrc *isrc)
  162 {
  163         struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
  164         struct atpic *ap = (struct atpic *)isrc->is_pic;
  165 
  166         mtx_lock_spin(&icu_lock);
  167         *ap->at_imen &= ~(1 << ai->at_irq);
  168         outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
  169         mtx_unlock_spin(&icu_lock);
  170 }
  171 
  172 static void
  173 atpic_disable_source(struct intsrc *isrc)
  174 {
  175         struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
  176         struct atpic *ap = (struct atpic *)isrc->is_pic;
  177 
  178         mtx_lock_spin(&icu_lock);
  179         *ap->at_imen |= (1 << ai->at_irq);
  180         outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen);
  181         mtx_unlock_spin(&icu_lock);
  182 }
  183 
  184 static void
  185 atpic_eoi_master(struct intsrc *isrc)
  186 {
  187 
  188         KASSERT(isrc->is_pic == &atpics[MASTER].at_pic,
  189             ("%s: mismatched pic", __func__));
  190 #ifndef AUTO_EOI_1
  191         mtx_lock_spin(&icu_lock);
  192         outb(atpics[MASTER].at_ioaddr, ICU_EOI);
  193         mtx_unlock_spin(&icu_lock);
  194 #endif
  195 }
  196 
  197 /*
  198  * The data sheet says no auto-EOI on slave, but it sometimes works.
  199  * So, if AUTO_EOI_2 is enabled, we use it.
  200  */
  201 static void
  202 atpic_eoi_slave(struct intsrc *isrc)
  203 {
  204 
  205         KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic,
  206             ("%s: mismatched pic", __func__));
  207 #ifndef AUTO_EOI_2
  208         mtx_lock_spin(&icu_lock);
  209         outb(atpics[SLAVE].at_ioaddr, ICU_EOI);
  210 #ifndef AUTO_EOI_1
  211         outb(atpics[MASTER].at_ioaddr, ICU_EOI);
  212 #endif
  213         mtx_unlock_spin(&icu_lock);
  214 #endif
  215 }
  216 
  217 static void
  218 atpic_enable_intr(struct intsrc *isrc)
  219 {
  220 }
  221 
  222 static int
  223 atpic_vector(struct intsrc *isrc)
  224 {
  225         struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
  226         struct atpic *ap = (struct atpic *)isrc->is_pic;
  227 
  228         return (IRQ(ap, ai));
  229 }
  230 
  231 static int
  232 atpic_source_pending(struct intsrc *isrc)
  233 {
  234         struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
  235         struct atpic *ap = (struct atpic *)isrc->is_pic;
  236 
  237         return (inb(ap->at_ioaddr) & (1 << ai->at_irq));
  238 }
  239 
  240 static void
  241 atpic_resume(struct intsrc *isrc)
  242 {
  243         struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc;
  244         struct atpic *ap = (struct atpic *)isrc->is_pic;
  245 
  246         if (ai->at_irq == 0)
  247                 i8259_init(ap, ap == &atpics[SLAVE]);
  248 }
  249 
  250 static void
  251 i8259_init(struct atpic *pic, int slave)
  252 {
  253         int imr_addr;
  254 
  255         /* Reset the PIC and program with next four bytes. */
  256         mtx_lock_spin(&icu_lock);
  257 #ifdef DEV_MCA
  258         /* MCA uses level triggered interrupts. */
  259         if (MCA_system)
  260                 outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM);
  261         else
  262 #endif
  263                 outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4);
  264         imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET;
  265 
  266         /* Start vector. */
  267         outb(imr_addr, pic->at_intbase);
  268 
  269         /*
  270          * Setup slave links.  For the master pic, indicate what line
  271          * the slave is configured on.  For the slave indicate
  272          * which line on the master we are connected to.
  273          */
  274         if (slave)
  275                 outb(imr_addr, ICU_SLAVEID);    /* my slave id is 7 */
  276         else
  277                 outb(imr_addr, IRQ_SLAVE);      /* slave on line 7 */
  278 
  279         /* Set mode. */
  280         if (slave)
  281                 outb(imr_addr, SLAVE_MODE);
  282         else
  283                 outb(imr_addr, MASTER_MODE);
  284 
  285         /* Set interrupt enable mask. */
  286         outb(imr_addr, *pic->at_imen);
  287 
  288         /* Reset is finished, default to IRR on read. */
  289         outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR);
  290 
  291 #ifndef PC98
  292         /* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */
  293         if (!slave)
  294                 outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1);
  295 #endif
  296         mtx_unlock_spin(&icu_lock);
  297 }
  298 
  299 void
  300 atpic_startup(void)
  301 {
  302         struct atpic_intsrc *ai;
  303         int i;
  304 
  305         /* Start off with all interrupts disabled. */
  306         imen = 0xffff;
  307         i8259_init(&atpics[MASTER], 0);
  308         i8259_init(&atpics[SLAVE], 1);
  309         atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]);
  310 
  311         /* Install low-level interrupt handlers for all of our IRQs. */
  312         for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) {
  313                 if (i == ICU_SLAVEID)
  314                         continue;
  315                 ai = &atintrs[i];
  316                 ai->at_intsrc.is_count = &ai->at_count;
  317                 ai->at_intsrc.is_straycount = &ai->at_straycount;
  318                 setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase +
  319                     ai->at_irq, ai->at_intr, SDT_SYS386IGT, SEL_KPL,
  320                     GSEL(GCODE_SEL, SEL_KPL));
  321         }
  322 }
  323 
  324 static void
  325 atpic_init(void *dummy __unused)
  326 {
  327         int i;
  328 
  329         /* Loop through all interrupt sources and add them. */
  330         for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) {
  331                 if (i == ICU_SLAVEID)
  332                         continue;
  333                 intr_register_source(&atintrs[i].at_intsrc);
  334         }
  335 }
  336 SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL)
  337 
  338 void
  339 atpic_handle_intr(struct intrframe iframe)
  340 {
  341         struct intsrc *isrc;
  342 
  343         KASSERT((uint)iframe.if_vec < ICU_LEN,
  344             ("unknown int %d\n", iframe.if_vec));
  345         isrc = &atintrs[iframe.if_vec].at_intsrc;
  346 
  347         /*
  348          * If we don't have an ithread, see if this is a spurious
  349          * interrupt.
  350          */
  351         if (isrc->is_ithread == NULL &&
  352             (iframe.if_vec == 7 || iframe.if_vec == 15)) {
  353                 int port, isr;
  354 
  355                 /*
  356                  * Read the ISR register to see if IRQ 7/15 is really
  357                  * pending.  Reset read register back to IRR when done.
  358                  */
  359                 port = ((struct atpic *)isrc->is_pic)->at_ioaddr;
  360                 mtx_lock_spin(&icu_lock);
  361                 outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS);
  362                 isr = inb(port);
  363                 outb(port, OCW3_SEL | OCW3_RR);
  364                 mtx_unlock_spin(&icu_lock);
  365                 if ((isr & IRQ7) == 0)
  366                         return;
  367         }
  368         intr_execute_handlers(isrc, &iframe);
  369 }
  370 
  371 #ifdef DEV_ISA
  372 /*
  373  * Bus attachment for the ISA PIC.
  374  */
  375 static struct isa_pnp_id atpic_ids[] = {
  376         { 0x0000d041 /* PNP0000 */, "AT interrupt controller" },
  377         { 0 }
  378 };
  379 
  380 static int
  381 atpic_probe(device_t dev)
  382 {
  383         int result;
  384         
  385         result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids);
  386         if (result <= 0)
  387                 device_quiet(dev);
  388         return (result);
  389 }
  390 
  391 /*
  392  * We might be granted IRQ 2, as this is typically consumed by chaining
  393  * between the two PIC components.  If we're using the APIC, however,
  394  * this may not be the case, and as such we should free the resource.
  395  * (XXX untested)
  396  *
  397  * The generic ISA attachment code will handle allocating any other resources
  398  * that we don't explicitly claim here.
  399  */
  400 static int
  401 atpic_attach(device_t dev)
  402 {
  403         struct resource *res;
  404         int rid;
  405 
  406         /* Try to allocate our IRQ and then free it. */
  407         rid = 0;
  408         res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 0);
  409         if (res != NULL)
  410                 bus_release_resource(dev, SYS_RES_IRQ, rid, res);
  411         return (0);
  412 }
  413 
  414 static device_method_t atpic_methods[] = {
  415         /* Device interface */
  416         DEVMETHOD(device_probe,         atpic_probe),
  417         DEVMETHOD(device_attach,        atpic_attach),
  418         DEVMETHOD(device_detach,        bus_generic_detach),
  419         DEVMETHOD(device_shutdown,      bus_generic_shutdown),
  420         DEVMETHOD(device_suspend,       bus_generic_suspend),
  421         DEVMETHOD(device_resume,        bus_generic_resume),
  422         { 0, 0 }
  423 };
  424 
  425 static driver_t atpic_driver = {
  426         "atpic",
  427         atpic_methods,
  428         1,              /* no softc */
  429 };
  430 
  431 static devclass_t atpic_devclass;
  432 
  433 DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0);
  434 #ifndef PC98
  435 DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0);
  436 #endif
  437 
  438 /*
  439  * Return a bitmap of the current interrupt requests.  This is 8259-specific
  440  * and is only suitable for use at probe time.
  441  */
  442 intrmask_t
  443 isa_irq_pending(void)
  444 {
  445         u_char irr1;
  446         u_char irr2;
  447 
  448         irr1 = inb(IO_ICU1);
  449         irr2 = inb(IO_ICU2);
  450         return ((irr2 << 8) | irr1);
  451 }
  452 #endif /* DEV_ISA */

Cache object: f8a11be0bb087353b0c3fc14da0697b1


[ 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.