atpic.c revision 122897
189857Sobrien/*- 289857Sobrien * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org> 389857Sobrien * All rights reserved. 489857Sobrien * 5218822Sdim * Redistribution and use in source and binary forms, with or without 6218822Sdim * modification, are permitted provided that the following conditions 7218822Sdim * are met: 889857Sobrien * 1. Redistributions of source code must retain the above copyright 989857Sobrien * notice, this list of conditions and the following disclaimer. 1089857Sobrien * 2. Redistributions in binary form must reproduce the above copyright 1189857Sobrien * notice, this list of conditions and the following disclaimer in the 1289857Sobrien * documentation and/or other materials provided with the distribution. 1389857Sobrien * 3. Neither the name of the author nor the names of any co-contributors 1489857Sobrien * may be used to endorse or promote products derived from this software 1589857Sobrien * without specific prior written permission. 1689857Sobrien * 1789857Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1889857Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1989857Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2089857Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2189857Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22218822Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23218822Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2489857Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2589857Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2689857Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2789857Sobrien * SUCH DAMAGE. 2889857Sobrien */ 2989857Sobrien 30130561Sobrien/* 3189857Sobrien * PIC driver for the 8259A Master and Slave PICs in PC/AT machines. 3289857Sobrien */ 3389857Sobrien 3489857Sobrien#include <sys/cdefs.h> 3589857Sobrien__FBSDID("$FreeBSD: head/sys/i386/isa/atpic.c 122897 2003-11-19 15:38:56Z jhb $"); 3689857Sobrien 37218822Sdim#include "opt_auto_eoi.h" 38218822Sdim#include "opt_isa.h" 39218822Sdim 40218822Sdim#include <sys/param.h> 4189857Sobrien#include <sys/systm.h> 4289857Sobrien#include <sys/bus.h> 43130561Sobrien#include <sys/interrupt.h> 44130561Sobrien#include <sys/kernel.h> 4589857Sobrien#include <sys/lock.h> 4689857Sobrien#include <sys/mutex.h> 4789857Sobrien 4889857Sobrien#include <machine/cpufunc.h> 4989857Sobrien#include <machine/frame.h> 5089857Sobrien#include <machine/intr_machdep.h> 5189857Sobrien#include <machine/md_var.h> 5289857Sobrien#include <machine/resource.h> 5389857Sobrien#include <machine/segments.h> 5489857Sobrien 5589857Sobrien#include <i386/isa/icu.h> 5689857Sobrien#ifdef PC98 5789857Sobrien#include <pc98/pc98/pc98.h> 5889857Sobrien#else 5989857Sobrien#include <i386/isa/isa.h> 6089857Sobrien#endif 6189857Sobrien#include <isa/isavar.h> 6289857Sobrien 6389857Sobrien#define MASTER 0 6489857Sobrien#define SLAVE 1 6589857Sobrien 6689857Sobrien/* XXX: Magic numbers */ 6789857Sobrien#ifdef PC98 6889857Sobrien#ifdef AUTO_EOI_1 6989857Sobrien#define MASTER_MODE 0x1f /* Master auto EOI, 8086 mode */ 7089857Sobrien#else 7189857Sobrien#define MASTER_MODE 0x1d /* Master 8086 mode */ 7289857Sobrien#endif 7389857Sobrien#define SLAVE_MODE 9 /* 8086 mode */ 7489857Sobrien#else /* IBM-PC */ 7589857Sobrien#ifdef AUTO_EOI_1 7689857Sobrien#define MASTER_MODE (ICW4_8086 | ICW4_AEOI) 7789857Sobrien#else 7889857Sobrien#define MASTER_MODE ICW4_8086 7989857Sobrien#endif 8089857Sobrien#ifdef AUTO_EOI_2 8189857Sobrien#define SLAVE_MODE (ICW4_8086 | ICW4_AEOI) 8289857Sobrien#else 8389857Sobrien#define SLAVE_MODE ICW4_8086 8489857Sobrien#endif 8589857Sobrien#endif /* PC98 */ 8689857Sobrien 8789857Sobrienstatic void atpic_init(void *dummy); 8889857Sobrien 8989857Sobrienunsigned int imen; /* XXX */ 9089857Sobrien 9189857Sobrieninthand_t 9289857Sobrien IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2), 9389857Sobrien IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5), 9489857Sobrien IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8), 9589857Sobrien IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11), 9689857Sobrien IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14), 9789857Sobrien IDTVEC(atpic_intr15); 9889857Sobrien 9989857Sobrien#define IRQ(ap, ai) ((ap)->at_irqbase + (ai)->at_irq) 10089857Sobrien 10189857Sobrien#define ATPIC(io, base, eoi, imenptr) \ 10289857Sobrien { { atpic_enable_source, atpic_disable_source, (eoi), \ 10389857Sobrien atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \ 10489857Sobrien atpic_resume }, (io), (base), IDT_IO_INTS + (base), (imenptr) } 10589857Sobrien 10689857Sobrien#define INTSRC(irq) \ 10789857Sobrien { { &atpics[(irq) / 8].at_pic }, (irq) % 8, \ 10889857Sobrien IDTVEC(atpic_intr ## irq ) } 10989857Sobrien 11089857Sobrienstruct atpic { 11189857Sobrien struct pic at_pic; 11289857Sobrien int at_ioaddr; 11389857Sobrien int at_irqbase; 11489857Sobrien uint8_t at_intbase; 11589857Sobrien uint8_t *at_imen; 11689857Sobrien}; 11789857Sobrien 11889857Sobrienstruct atpic_intsrc { 11989857Sobrien struct intsrc at_intsrc; 12089857Sobrien int at_irq; /* Relative to PIC base. */ 12189857Sobrien inthand_t *at_intr; 12289857Sobrien u_long at_count; 12389857Sobrien u_long at_straycount; 12489857Sobrien}; 12589857Sobrien 12689857Sobrienstatic void atpic_enable_source(struct intsrc *isrc); 12789857Sobrienstatic void atpic_disable_source(struct intsrc *isrc); 12889857Sobrienstatic void atpic_eoi_master(struct intsrc *isrc); 12989857Sobrienstatic void atpic_eoi_slave(struct intsrc *isrc); 13089857Sobrienstatic void atpic_enable_intr(struct intsrc *isrc); 13189857Sobrienstatic int atpic_vector(struct intsrc *isrc); 13289857Sobrienstatic void atpic_resume(struct intsrc *isrc); 13389857Sobrienstatic int atpic_source_pending(struct intsrc *isrc); 13489857Sobrienstatic void i8259_init(struct atpic *pic, int slave); 13589857Sobrien 13689857Sobrienstatic struct atpic atpics[] = { 13789857Sobrien ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen), 13889857Sobrien ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1) 13989857Sobrien}; 14089857Sobrien 14189857Sobrienstatic struct atpic_intsrc atintrs[] = { 14289857Sobrien INTSRC(0), 14389857Sobrien INTSRC(1), 14489857Sobrien INTSRC(2), 14589857Sobrien INTSRC(3), 14689857Sobrien INTSRC(4), 14789857Sobrien INTSRC(5), 14889857Sobrien INTSRC(6), 14989857Sobrien INTSRC(7), 15089857Sobrien INTSRC(8), 15189857Sobrien INTSRC(9), 15289857Sobrien INTSRC(10), 15389857Sobrien INTSRC(11), 15489857Sobrien INTSRC(12), 15589857Sobrien INTSRC(13), 15689857Sobrien INTSRC(14), 15789857Sobrien INTSRC(15), 15889857Sobrien}; 15989857Sobrien 16089857Sobrienstatic void 16189857Sobrienatpic_enable_source(struct intsrc *isrc) 16289857Sobrien{ 16389857Sobrien struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 16489857Sobrien struct atpic *ap = (struct atpic *)isrc->is_pic; 16589857Sobrien 16689857Sobrien mtx_lock_spin(&icu_lock); 16789857Sobrien *ap->at_imen &= ~(1 << ai->at_irq); 16889857Sobrien outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); 16989857Sobrien mtx_unlock_spin(&icu_lock); 17089857Sobrien} 17189857Sobrien 17289857Sobrienstatic void 17389857Sobrienatpic_disable_source(struct intsrc *isrc) 17489857Sobrien{ 17589857Sobrien struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 17689857Sobrien struct atpic *ap = (struct atpic *)isrc->is_pic; 17789857Sobrien 17889857Sobrien mtx_lock_spin(&icu_lock); 17989857Sobrien *ap->at_imen |= (1 << ai->at_irq); 18089857Sobrien outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); 18189857Sobrien mtx_unlock_spin(&icu_lock); 18289857Sobrien} 18389857Sobrien 18489857Sobrienstatic void 18589857Sobrienatpic_eoi_master(struct intsrc *isrc) 18689857Sobrien{ 18789857Sobrien 18889857Sobrien KASSERT(isrc->is_pic == &atpics[MASTER].at_pic, 18989857Sobrien ("%s: mismatched pic", __func__)); 19089857Sobrien#ifndef AUTO_EOI_1 19189857Sobrien mtx_lock_spin(&icu_lock); 19289857Sobrien outb(atpics[MASTER].at_ioaddr, ICU_EOI); 19389857Sobrien mtx_unlock_spin(&icu_lock); 19489857Sobrien#endif 19589857Sobrien} 19689857Sobrien 19789857Sobrien/* 19889857Sobrien * The data sheet says no auto-EOI on slave, but it sometimes works. 19989857Sobrien * So, if AUTO_EOI_2 is enabled, we use it. 20089857Sobrien */ 20189857Sobrienstatic void 20289857Sobrienatpic_eoi_slave(struct intsrc *isrc) 20389857Sobrien{ 20489857Sobrien 20589857Sobrien KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic, 20689857Sobrien ("%s: mismatched pic", __func__)); 20789857Sobrien#ifndef AUTO_EOI_2 20889857Sobrien mtx_lock_spin(&icu_lock); 20989857Sobrien outb(atpics[SLAVE].at_ioaddr, ICU_EOI); 21089857Sobrien#ifndef AUTO_EOI_1 21189857Sobrien outb(atpics[MASTER].at_ioaddr, ICU_EOI); 21289857Sobrien#endif 21389857Sobrien mtx_unlock_spin(&icu_lock); 21489857Sobrien#endif 21589857Sobrien} 21689857Sobrien 21789857Sobrienstatic void 21889857Sobrienatpic_enable_intr(struct intsrc *isrc) 21989857Sobrien{ 22089857Sobrien} 22189857Sobrien 22289857Sobrienstatic int 22389857Sobrienatpic_vector(struct intsrc *isrc) 22489857Sobrien{ 22589857Sobrien struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 22689857Sobrien struct atpic *ap = (struct atpic *)isrc->is_pic; 22789857Sobrien 22889857Sobrien return (IRQ(ap, ai)); 22989857Sobrien} 23089857Sobrien 23189857Sobrienstatic int 23289857Sobrienatpic_source_pending(struct intsrc *isrc) 23389857Sobrien{ 23489857Sobrien struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 23589857Sobrien struct atpic *ap = (struct atpic *)isrc->is_pic; 23689857Sobrien 23789857Sobrien return (inb(ap->at_ioaddr) & (1 << ai->at_irq)); 23889857Sobrien} 23989857Sobrien 24089857Sobrienstatic void 24189857Sobrienatpic_resume(struct intsrc *isrc) 24289857Sobrien{ 24389857Sobrien struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 24489857Sobrien struct atpic *ap = (struct atpic *)isrc->is_pic; 24589857Sobrien 24689857Sobrien if (ai->at_irq == 0) 24789857Sobrien i8259_init(ap, ap == &atpics[SLAVE]); 24889857Sobrien} 24989857Sobrien 25089857Sobrienstatic void 25189857Sobrieni8259_init(struct atpic *pic, int slave) 25289857Sobrien{ 25389857Sobrien int imr_addr; 25489857Sobrien 25589857Sobrien /* Reset the PIC and program with next four bytes. */ 25689857Sobrien mtx_lock_spin(&icu_lock); 25789857Sobrien#ifdef DEV_MCA 25889857Sobrien /* MCA uses level triggered interrupts. */ 25989857Sobrien if (MCA_system) 26089857Sobrien outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM); 26189857Sobrien else 26289857Sobrien#endif 26389857Sobrien outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4); 26489857Sobrien imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET; 26589857Sobrien 26689857Sobrien /* Start vector. */ 26789857Sobrien outb(imr_addr, pic->at_intbase); 26889857Sobrien 26989857Sobrien /* 27089857Sobrien * Setup slave links. For the master pic, indicate what line 27189857Sobrien * the slave is configured on. For the slave indicate 27289857Sobrien * which line on the master we are connected to. 27389857Sobrien */ 274218822Sdim if (slave) 27589857Sobrien outb(imr_addr, ICU_SLAVEID); /* my slave id is 7 */ 27689857Sobrien else 277218822Sdim outb(imr_addr, IRQ_SLAVE); /* slave on line 7 */ 27889857Sobrien 27989857Sobrien /* Set mode. */ 28089857Sobrien if (slave) 28189857Sobrien outb(imr_addr, SLAVE_MODE); 28289857Sobrien else 28389857Sobrien outb(imr_addr, MASTER_MODE); 28489857Sobrien 28589857Sobrien /* Set interrupt enable mask. */ 28689857Sobrien outb(imr_addr, *pic->at_imen); 28789857Sobrien 28889857Sobrien /* Reset is finished, default to IRR on read. */ 28989857Sobrien outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR); 29089857Sobrien 29189857Sobrien#ifndef PC98 29289857Sobrien /* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */ 29389857Sobrien if (!slave) 29489857Sobrien outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1); 29589857Sobrien#endif 29689857Sobrien mtx_unlock_spin(&icu_lock); 29789857Sobrien} 29889857Sobrien 29989857Sobrienvoid 30089857Sobrienatpic_startup(void) 30189857Sobrien{ 30289857Sobrien struct atpic_intsrc *ai; 30389857Sobrien int i; 30489857Sobrien 30589857Sobrien /* Start off with all interrupts disabled. */ 30689857Sobrien imen = 0xffff; 30789857Sobrien i8259_init(&atpics[MASTER], 0); 30889857Sobrien i8259_init(&atpics[SLAVE], 1); 30989857Sobrien atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]); 31089857Sobrien 31189857Sobrien /* Install low-level interrupt handlers for all of our IRQs. */ 31289857Sobrien for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) { 31389857Sobrien if (i == ICU_SLAVEID) 31489857Sobrien continue; 31589857Sobrien ai = &atintrs[i]; 31689857Sobrien ai->at_intsrc.is_count = &ai->at_count; 31789857Sobrien ai->at_intsrc.is_straycount = &ai->at_straycount; 31889857Sobrien setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase + 31989857Sobrien ai->at_irq, ai->at_intr, SDT_SYS386IGT, SEL_KPL, 32089857Sobrien GSEL(GCODE_SEL, SEL_KPL)); 32189857Sobrien } 32289857Sobrien} 32389857Sobrien 32489857Sobrienstatic void 32589857Sobrienatpic_init(void *dummy __unused) 32689857Sobrien{ 32789857Sobrien int i; 32889857Sobrien 32989857Sobrien /* Loop through all interrupt sources and add them. */ 33089857Sobrien for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) { 33189857Sobrien if (i == ICU_SLAVEID) 33289857Sobrien continue; 33389857Sobrien intr_register_source(&atintrs[i].at_intsrc); 33489857Sobrien } 33589857Sobrien} 33689857SobrienSYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL) 33789857Sobrien 33889857Sobrienvoid 33989857Sobrienatpic_handle_intr(struct intrframe iframe) 34089857Sobrien{ 34189857Sobrien struct intsrc *isrc; 34289857Sobrien 34389857Sobrien KASSERT((uint)iframe.if_vec < ICU_LEN, 34489857Sobrien ("unknown int %d\n", iframe.if_vec)); 34589857Sobrien isrc = &atintrs[iframe.if_vec].at_intsrc; 34689857Sobrien intr_execute_handlers(isrc, &iframe); 34789857Sobrien} 34889857Sobrien 34989857Sobrien#ifdef DEV_ISA 35089857Sobrien/* 35189857Sobrien * Bus attachment for the ISA PIC. 35289857Sobrien */ 35389857Sobrienstatic struct isa_pnp_id atpic_ids[] = { 35489857Sobrien { 0x0000d041 /* PNP0000 */, "AT interrupt controller" }, 35589857Sobrien { 0 } 35689857Sobrien}; 35789857Sobrien 35889857Sobrienstatic int 35989857Sobrienatpic_probe(device_t dev) 36089857Sobrien{ 36189857Sobrien int result; 36289857Sobrien 36389857Sobrien result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids); 36489857Sobrien if (result <= 0) 36589857Sobrien device_quiet(dev); 36689857Sobrien return (result); 36789857Sobrien} 36889857Sobrien 36989857Sobrien/* 37089857Sobrien * We might be granted IRQ 2, as this is typically consumed by chaining 37189857Sobrien * between the two PIC components. If we're using the APIC, however, 37289857Sobrien * this may not be the case, and as such we should free the resource. 37389857Sobrien * (XXX untested) 37489857Sobrien * 37589857Sobrien * The generic ISA attachment code will handle allocating any other resources 37689857Sobrien * that we don't explicitly claim here. 37789857Sobrien */ 37889857Sobrienstatic int 37989857Sobrienatpic_attach(device_t dev) 38089857Sobrien{ 38189857Sobrien struct resource *res; 38289857Sobrien int rid; 38389857Sobrien 38489857Sobrien /* Try to allocate our IRQ and then free it. */ 38589857Sobrien rid = 0; 38689857Sobrien res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 0); 38789857Sobrien if (res != NULL) 38889857Sobrien bus_release_resource(dev, SYS_RES_IRQ, rid, res); 38989857Sobrien return (0); 39089857Sobrien} 39189857Sobrien 39289857Sobrienstatic device_method_t atpic_methods[] = { 39389857Sobrien /* Device interface */ 39489857Sobrien DEVMETHOD(device_probe, atpic_probe), 39589857Sobrien DEVMETHOD(device_attach, atpic_attach), 39689857Sobrien DEVMETHOD(device_detach, bus_generic_detach), 39789857Sobrien DEVMETHOD(device_shutdown, bus_generic_shutdown), 39889857Sobrien DEVMETHOD(device_suspend, bus_generic_suspend), 39989857Sobrien DEVMETHOD(device_resume, bus_generic_resume), 40089857Sobrien { 0, 0 } 40189857Sobrien}; 40289857Sobrien 40389857Sobrienstatic driver_t atpic_driver = { 40489857Sobrien "atpic", 40589857Sobrien atpic_methods, 40689857Sobrien 1, /* no softc */ 40789857Sobrien}; 408218822Sdim 409218822Sdimstatic devclass_t atpic_devclass; 410218822Sdim 41189857SobrienDRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0); 412218822Sdim#ifndef PC98 413218822SdimDRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0); 414218822Sdim#endif 415218822Sdim 416218822Sdim/* 417218822Sdim * Return a bitmap of the current interrupt requests. This is 8259-specific 418218822Sdim * and is only suitable for use at probe time. 419218822Sdim */ 420218822Sdimintrmask_t 421218822Sdimisa_irq_pending(void) 422218822Sdim{ 423218822Sdim u_char irr1; 42489857Sobrien u_char irr2; 42589857Sobrien 426218822Sdim irr1 = inb(IO_ICU1); 427218822Sdim irr2 = inb(IO_ICU2); 428218822Sdim return ((irr2 << 8) | irr1); 42989857Sobrien} 43089857Sobrien#endif /* DEV_ISA */ 431218822Sdim