atpic.c revision 121985
164562Sgshapiro/*- 273188Sgshapiro * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org> 364562Sgshapiro * All rights reserved. 464562Sgshapiro * 564562Sgshapiro * Redistribution and use in source and binary forms, with or without 664562Sgshapiro * modification, are permitted provided that the following conditions 764562Sgshapiro * are met: 864562Sgshapiro * 1. Redistributions of source code must retain the above copyright 964562Sgshapiro * notice, this list of conditions and the following disclaimer. 1064562Sgshapiro * 2. Redistributions in binary form must reproduce the above copyright 1164562Sgshapiro * notice, this list of conditions and the following disclaimer in the 1277349Sgshapiro * documentation and/or other materials provided with the distribution. 1364562Sgshapiro * 3. Neither the name of the author nor the names of any co-contributors 1464562Sgshapiro * may be used to endorse or promote products derived from this software 1564562Sgshapiro * without specific prior written permission. 1664562Sgshapiro * 1764562Sgshapiro * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1864562Sgshapiro * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1964562Sgshapiro * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2064562Sgshapiro * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2164562Sgshapiro * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2264562Sgshapiro * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2364562Sgshapiro * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2464562Sgshapiro * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2564562Sgshapiro * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2664562Sgshapiro * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2764562Sgshapiro * SUCH DAMAGE. 2864562Sgshapiro */ 2964562Sgshapiro 3064562Sgshapiro/* 3164562Sgshapiro * PIC driver for the 8259A Master and Slave PICs in PC/AT machines. 3264562Sgshapiro */ 3373188Sgshapiro 3473188Sgshapiro#include <sys/cdefs.h> 3564562Sgshapiro__FBSDID("$FreeBSD: head/sys/i386/isa/atpic.c 121985 2003-11-03 21:34:45Z jhb $"); 3664562Sgshapiro 3764562Sgshapiro#include "opt_auto_eoi.h" 3864562Sgshapiro#include "opt_isa.h" 3964562Sgshapiro 4066494Sgshapiro#include <sys/param.h> 4173188Sgshapiro#include <sys/systm.h> 4264562Sgshapiro#include <sys/bus.h> 4364562Sgshapiro#include <sys/interrupt.h> 4464562Sgshapiro#include <sys/kernel.h> 4573188Sgshapiro#include <sys/lock.h> 4664562Sgshapiro#include <sys/mutex.h> 4764562Sgshapiro 4866494Sgshapiro#include <machine/cpufunc.h> 4964562Sgshapiro#include <machine/frame.h> 5064562Sgshapiro#include <machine/intr_machdep.h> 5164562Sgshapiro#include <machine/md_var.h> 5264562Sgshapiro#include <machine/resource.h> 5364562Sgshapiro#include <machine/segments.h> 5464562Sgshapiro 5564562Sgshapiro#include <i386/isa/icu.h> 5664562Sgshapiro#include <i386/isa/isa.h> 5764562Sgshapiro#include <isa/isavar.h> 5864562Sgshapiro 5966494Sgshapiro#define MASTER 0 6064562Sgshapiro#define SLAVE 1 6164562Sgshapiro 6264562Sgshapiro/* XXX: Magic numbers */ 6364562Sgshapiro#ifdef PC98 6464562Sgshapiro#ifdef AUTO_EOI_1 6564562Sgshapiro#define MASTER_MODE 0x1f /* Master auto EOI, 8086 mode */ 6664562Sgshapiro#else 6764562Sgshapiro#define MASTER_MODE 0x1d /* Master 8086 mode */ 6864562Sgshapiro#endif 6964562Sgshapiro#define SLAVE_MODE 9 /* 8086 mode */ 7064562Sgshapiro#else /* IBM-PC */ 7164562Sgshapiro#ifdef AUTO_EOI_1 7264562Sgshapiro#define MASTER_MODE (2 | 1) /* Auto EOI, 8086 mode */ 7364562Sgshapiro#else 7464562Sgshapiro#define MASTER_MODE 1 /* 8086 mode */ 7564562Sgshapiro#endif 7664562Sgshapiro#ifdef AUTO_EOI_2 7764562Sgshapiro#define SLAVE_MODE (2 | 1) /* Auto EOI, 8086 mode */ 7864562Sgshapiro#else 7964562Sgshapiro#define SLAVE_MODE 1 /* 8086 mode */ 8064562Sgshapiro#endif 8164562Sgshapiro#endif /* PC98 */ 8264562Sgshapiro 8364562Sgshapirostatic void atpic_init(void *dummy); 8464562Sgshapiro 8564562Sgshapirounsigned int imen; /* XXX */ 8664562Sgshapiro 8764562Sgshapirointhand_t 8864562Sgshapiro IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2), 8964562Sgshapiro IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5), 9064562Sgshapiro IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8), 9166494Sgshapiro IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11), 9264562Sgshapiro IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14), 9364562Sgshapiro IDTVEC(atpic_intr15); 9464562Sgshapiro 9564562Sgshapiro#define IRQ(ap, ai) ((ap)->at_irqbase + (ai)->at_irq) 9664562Sgshapiro 9764562Sgshapiro#define ATPIC(io, base, eoi, imenptr) \ 9864562Sgshapiro { { atpic_enable_source, atpic_disable_source, (eoi), \ 9964562Sgshapiro atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \ 10064562Sgshapiro atpic_resume }, (io), (base), IDT_IO_INTS + (base), (imenptr) } 10164562Sgshapiro 10264562Sgshapiro#define INTSRC(irq) \ 10364562Sgshapiro { { &atpics[(irq) / 8].at_pic }, (irq) % 8, \ 10464562Sgshapiro IDTVEC(atpic_intr ## irq ) } 10564562Sgshapiro 10664562Sgshapirostruct atpic { 10764562Sgshapiro struct pic at_pic; 10864562Sgshapiro int at_ioaddr; 10964562Sgshapiro int at_irqbase; 11064562Sgshapiro uint8_t at_intbase; 11164562Sgshapiro uint8_t *at_imen; 11264562Sgshapiro}; 11364562Sgshapiro 11464562Sgshapirostruct atpic_intsrc { 11564562Sgshapiro struct intsrc at_intsrc; 11664562Sgshapiro int at_irq; /* Relative to PIC base. */ 11764562Sgshapiro inthand_t *at_intr; 11864562Sgshapiro}; 11964562Sgshapiro 12064562Sgshapirostatic void atpic_enable_source(struct intsrc *isrc); 12164562Sgshapirostatic void atpic_disable_source(struct intsrc *isrc); 12266494Sgshapirostatic void atpic_eoi_master(struct intsrc *isrc); 12364562Sgshapirostatic void atpic_eoi_slave(struct intsrc *isrc); 12464562Sgshapirostatic void atpic_enable_intr(struct intsrc *isrc); 12564562Sgshapirostatic int atpic_vector(struct intsrc *isrc); 12664562Sgshapirostatic void atpic_resume(struct intsrc *isrc); 12764562Sgshapirostatic int atpic_source_pending(struct intsrc *isrc); 12864562Sgshapirostatic void i8259_init(struct atpic *pic, int slave); 12964562Sgshapiro 13064562Sgshapirostatic struct atpic atpics[] = { 13164562Sgshapiro ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen), 13264562Sgshapiro ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1) 13364562Sgshapiro}; 13464562Sgshapiro 13564562Sgshapirostatic struct atpic_intsrc atintrs[] = { 13664562Sgshapiro INTSRC(0), 13764562Sgshapiro INTSRC(1), 13864562Sgshapiro INTSRC(2), 13964562Sgshapiro INTSRC(3), 14064562Sgshapiro INTSRC(4), 14164562Sgshapiro INTSRC(5), 14264562Sgshapiro INTSRC(6), 14364562Sgshapiro INTSRC(7), 14464562Sgshapiro INTSRC(8), 14564562Sgshapiro INTSRC(9), 14666494Sgshapiro INTSRC(10), 14764562Sgshapiro INTSRC(11), 14864562Sgshapiro INTSRC(12), 14964562Sgshapiro INTSRC(13), 15064562Sgshapiro INTSRC(14), 15164562Sgshapiro INTSRC(15), 15264562Sgshapiro}; 15364562Sgshapiro 15464562Sgshapirostatic void 15564562Sgshapiroatpic_enable_source(struct intsrc *isrc) 15664562Sgshapiro{ 15764562Sgshapiro struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 15864562Sgshapiro struct atpic *ap = (struct atpic *)isrc->is_pic; 15964562Sgshapiro 16064562Sgshapiro mtx_lock_spin(&icu_lock); 16164562Sgshapiro *ap->at_imen &= ~(1 << ai->at_irq); 16264562Sgshapiro outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); 16364562Sgshapiro mtx_unlock_spin(&icu_lock); 16464562Sgshapiro} 16564562Sgshapiro 16664562Sgshapirostatic void 16766494Sgshapiroatpic_disable_source(struct intsrc *isrc) 16864562Sgshapiro{ 16964562Sgshapiro struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 17064562Sgshapiro struct atpic *ap = (struct atpic *)isrc->is_pic; 17164562Sgshapiro 17264562Sgshapiro mtx_lock_spin(&icu_lock); 17364562Sgshapiro *ap->at_imen |= (1 << ai->at_irq); 17464562Sgshapiro outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); 17564562Sgshapiro mtx_unlock_spin(&icu_lock); 17664562Sgshapiro} 17764562Sgshapiro 17864562Sgshapirostatic void 17966494Sgshapiroatpic_eoi_master(struct intsrc *isrc) 18064562Sgshapiro{ 18164562Sgshapiro 18264562Sgshapiro KASSERT(isrc->is_pic == &atpics[MASTER].at_pic, 18364562Sgshapiro ("%s: mismatched pic", __func__)); 18464562Sgshapiro#ifndef AUTO_EOI_1 18564562Sgshapiro mtx_lock_spin(&icu_lock); 18664562Sgshapiro outb(atpics[MASTER].at_ioaddr, ICU_EOI); 18764562Sgshapiro mtx_unlock_spin(&icu_lock); 18864562Sgshapiro#endif 18964562Sgshapiro} 19064562Sgshapiro 19164562Sgshapirostatic void 19264562Sgshapiroatpic_eoi_slave(struct intsrc *isrc) 19364562Sgshapiro{ 19464562Sgshapiro 19564562Sgshapiro KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic, 19664562Sgshapiro ("%s: mismatched pic", __func__)); 19764562Sgshapiro#ifndef AUTO_EOI_2 19864562Sgshapiro mtx_lock_spin(&icu_lock); 19964562Sgshapiro outb(atpics[SLAVE].at_ioaddr, ICU_EOI); 20064562Sgshapiro#ifndef AUTO_EOI_1 20164562Sgshapiro outb(atpics[MASTER].at_ioaddr, ICU_EOI); 20264562Sgshapiro#endif 20364562Sgshapiro mtx_unlock_spin(&icu_lock); 20464562Sgshapiro#endif 20564562Sgshapiro} 20664562Sgshapiro 20764562Sgshapirostatic void 20864562Sgshapiroatpic_enable_intr(struct intsrc *isrc) 20964562Sgshapiro{ 21064562Sgshapiro struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 21164562Sgshapiro struct atpic *ap = (struct atpic *)isrc->is_pic; 21264562Sgshapiro register_t eflags; 21364562Sgshapiro 21464562Sgshapiro mtx_lock_spin(&icu_lock); 21564562Sgshapiro eflags = intr_disable(); 21664562Sgshapiro setidt(ap->at_intbase + ai->at_irq, ai->at_intr, SDT_SYS386IGT, 21764562Sgshapiro SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 21864562Sgshapiro intr_restore(eflags); 21964562Sgshapiro mtx_unlock_spin(&icu_lock); 22064562Sgshapiro} 22164562Sgshapiro 22264562Sgshapirostatic int 22364562Sgshapiroatpic_vector(struct intsrc *isrc) 22464562Sgshapiro{ 22564562Sgshapiro struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 22664562Sgshapiro struct atpic *ap = (struct atpic *)isrc->is_pic; 22764562Sgshapiro 22864562Sgshapiro return (IRQ(ap, ai)); 22964562Sgshapiro} 23066494Sgshapiro 23164562Sgshapirostatic int 23264562Sgshapiroatpic_source_pending(struct intsrc *isrc) 23364562Sgshapiro{ 23464562Sgshapiro struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 23564562Sgshapiro struct atpic *ap = (struct atpic *)isrc->is_pic; 23664562Sgshapiro 23764562Sgshapiro return (inb(ap->at_ioaddr) & (1 << ai->at_irq)); 23864562Sgshapiro} 23964562Sgshapiro 24066494Sgshapirostatic void 24164562Sgshapiroatpic_resume(struct intsrc *isrc) 24264562Sgshapiro{ 24364562Sgshapiro struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 24464562Sgshapiro struct atpic *ap = (struct atpic *)isrc->is_pic; 24564562Sgshapiro 24664562Sgshapiro if (ai->at_irq == 0) 24764562Sgshapiro i8259_init(ap, ap == &atpics[SLAVE]); 24864562Sgshapiro} 24964562Sgshapiro 25064562Sgshapirostatic void 25164562Sgshapiroi8259_init(struct atpic *pic, int slave) 25264562Sgshapiro{ 25364562Sgshapiro int imr_addr; 25464562Sgshapiro 25564562Sgshapiro /* Reset the PIC and program with next four bytes. */ 25664562Sgshapiro mtx_lock_spin(&icu_lock); 25764562Sgshapiro#ifdef DEV_MCA 25864562Sgshapiro if (MCA_system) 25964562Sgshapiro outb(pic->at_ioaddr, 0x19); 26064562Sgshapiro else 26164562Sgshapiro#endif 26264562Sgshapiro outb(pic->at_ioaddr, 0x11); 26364562Sgshapiro imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET; 26464562Sgshapiro 26564562Sgshapiro /* Start vector. */ 26664562Sgshapiro outb(imr_addr, pic->at_intbase); 26764562Sgshapiro 26864562Sgshapiro /* 26964562Sgshapiro * Setup slave links. For the master pic, indicate what line 27064562Sgshapiro * the slave is configured on. For the slave indicate 27164562Sgshapiro * which line on the master we are connected to. 27264562Sgshapiro */ 27364562Sgshapiro if (slave) 27464562Sgshapiro outb(imr_addr, ICU_SLAVEID); /* my slave id is 7 */ 27564562Sgshapiro else 27664562Sgshapiro outb(imr_addr, IRQ_SLAVE); /* slave on line 7 */ 27764562Sgshapiro 27864562Sgshapiro /* Set mode. */ 27964562Sgshapiro if (slave) 28064562Sgshapiro outb(imr_addr, SLAVE_MODE); 28164562Sgshapiro else 28264562Sgshapiro outb(imr_addr, MASTER_MODE); 28364562Sgshapiro 28464562Sgshapiro /* Set interrupt enable mask. */ 28564562Sgshapiro outb(imr_addr, *pic->at_imen); 28664562Sgshapiro 28764562Sgshapiro /* Reset is finished, default to IRR on read. */ 28864562Sgshapiro outb(pic->at_ioaddr, 0x0a); 28964562Sgshapiro 29064562Sgshapiro#ifndef PC98 29166494Sgshapiro /* Set priority order to 3-7, 0-2 (com2 first). */ 29264562Sgshapiro if (!slave) 29364562Sgshapiro outb(pic->at_ioaddr, 0xc0 | (3 - 1)); 29464562Sgshapiro#endif 29564562Sgshapiro mtx_unlock_spin(&icu_lock); 29664562Sgshapiro} 29764562Sgshapiro 29864562Sgshapirovoid 29966494Sgshapiroatpic_startup(void) 30064562Sgshapiro{ 30164562Sgshapiro 30264562Sgshapiro /* Start off with all interrupts disabled. */ 30364562Sgshapiro imen = 0xffff; 30471345Sgshapiro i8259_init(&atpics[MASTER], 0); 30571345Sgshapiro i8259_init(&atpics[SLAVE], 1); 30664562Sgshapiro atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]); 30764562Sgshapiro} 30864562Sgshapiro 30964562Sgshapirostatic void 31064562Sgshapiroatpic_init(void *dummy __unused) 31164562Sgshapiro{ 31266494Sgshapiro struct atpic_intsrc *ai; 31364562Sgshapiro int i; 31464562Sgshapiro 31564562Sgshapiro /* Loop through all interrupt sources and add them. */ 31664562Sgshapiro for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) { 31764562Sgshapiro if (i == ICU_SLAVEID) 31864562Sgshapiro continue; 31964562Sgshapiro ai = &atintrs[i]; 32064562Sgshapiro intr_register_source(&ai->at_intsrc); 32164562Sgshapiro } 32264562Sgshapiro} 32364562SgshapiroSYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL) 32464562Sgshapiro 32564562Sgshapirovoid 32664562Sgshapiroatpic_sched_ithd(struct intrframe iframe) 32764562Sgshapiro{ 32864562Sgshapiro struct intsrc *isrc; 32964562Sgshapiro 33064562Sgshapiro KASSERT((uint)iframe.if_vec < ICU_LEN, 33164562Sgshapiro ("unknown int %d\n", iframe.if_vec)); 33264562Sgshapiro isrc = &atintrs[iframe.if_vec].at_intsrc; 33364562Sgshapiro intr_execute_handlers(isrc, &iframe); 33464562Sgshapiro} 33564562Sgshapiro 33664562Sgshapiro#ifdef DEV_ISA 33764562Sgshapiro/* 33864562Sgshapiro * Bus attachment for the ISA PIC. 33966494Sgshapiro */ 34064562Sgshapirostatic struct isa_pnp_id atpic_ids[] = { 34171345Sgshapiro { 0x0000d041 /* PNP0000 */, "AT interrupt controller" }, 34271345Sgshapiro { 0 } 34371345Sgshapiro}; 34464562Sgshapiro 34564562Sgshapirostatic int 34664562Sgshapiroatpic_probe(device_t dev) 34764562Sgshapiro{ 34864562Sgshapiro int result; 34964562Sgshapiro 35064562Sgshapiro result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids); 35164562Sgshapiro if (result <= 0) 35264562Sgshapiro device_quiet(dev); 35364562Sgshapiro return (result); 35464562Sgshapiro} 35564562Sgshapiro 35664562Sgshapiro/* 35764562Sgshapiro * We might be granted IRQ 2, as this is typically consumed by chaining 35864562Sgshapiro * between the two PIC components. If we're using the APIC, however, 35964562Sgshapiro * this may not be the case, and as such we should free the resource. 36064562Sgshapiro * (XXX untested) 36164562Sgshapiro * 36264562Sgshapiro * The generic ISA attachment code will handle allocating any other resources 36364562Sgshapiro * that we don't explicitly claim here. 36464562Sgshapiro */ 36564562Sgshapirostatic int 36664562Sgshapiroatpic_attach(device_t dev) 36764562Sgshapiro{ 36864562Sgshapiro struct resource *res; 36964562Sgshapiro int rid; 37064562Sgshapiro 37166494Sgshapiro /* Try to allocate our IRQ and then free it. */ 37264562Sgshapiro rid = 0; 37364562Sgshapiro res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 0); 37464562Sgshapiro if (res != NULL) 37564562Sgshapiro bus_release_resource(dev, SYS_RES_IRQ, rid, res); 37664562Sgshapiro return (0); 37764562Sgshapiro} 37864562Sgshapiro 37964562Sgshapirostatic device_method_t atpic_methods[] = { 38066494Sgshapiro /* Device interface */ 38164562Sgshapiro DEVMETHOD(device_probe, atpic_probe), 38264562Sgshapiro DEVMETHOD(device_attach, atpic_attach), 38364562Sgshapiro DEVMETHOD(device_detach, bus_generic_detach), 38464562Sgshapiro DEVMETHOD(device_shutdown, bus_generic_shutdown), 38564562Sgshapiro DEVMETHOD(device_suspend, bus_generic_suspend), 38664562Sgshapiro DEVMETHOD(device_resume, bus_generic_resume), 38764562Sgshapiro { 0, 0 } 38864562Sgshapiro}; 38966494Sgshapiro 39064562Sgshapirostatic driver_t atpic_driver = { 39164562Sgshapiro "atpic", 39264562Sgshapiro atpic_methods, 39364562Sgshapiro 1, /* no softc */ 39464562Sgshapiro}; 39564562Sgshapiro 39664562Sgshapirostatic devclass_t atpic_devclass; 39766494Sgshapiro 39864562SgshapiroDRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0); 39973188SgshapiroDRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0); 40064562Sgshapiro 40164562Sgshapiro/* 40264562Sgshapiro * Return a bitmap of the current interrupt requests. This is 8259-specific 40364562Sgshapiro * and is only suitable for use at probe time. 40464562Sgshapiro */ 40564562Sgshapirointrmask_t 40664562Sgshapiroisa_irq_pending(void) 40764562Sgshapiro{ 40864562Sgshapiro u_char irr1; 40964562Sgshapiro u_char irr2; 41064562Sgshapiro 41164562Sgshapiro irr1 = inb(IO_ICU1); 41264562Sgshapiro irr2 = inb(IO_ICU2); 41364562Sgshapiro return ((irr2 << 8) | irr1); 41464562Sgshapiro} 41564562Sgshapiro#endif /* DEV_ISA */ 41664562Sgshapiro