atpic.c revision 122051
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: head/sys/i386/isa/atpic.c 122051 2003-11-04 13:13:04Z nyan $"); 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 (2 | 1) /* Auto EOI, 8086 mode */ 77#else 78#define MASTER_MODE 1 /* 8086 mode */ 79#endif 80#ifdef AUTO_EOI_2 81#define SLAVE_MODE (2 | 1) /* Auto EOI, 8086 mode */ 82#else 83#define SLAVE_MODE 1 /* 8086 mode */ 84#endif 85#endif /* PC98 */ 86 87static void atpic_init(void *dummy); 88 89unsigned int imen; /* XXX */ 90 91inthand_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 110struct 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 118struct atpic_intsrc { 119 struct intsrc at_intsrc; 120 int at_irq; /* Relative to PIC base. */ 121 inthand_t *at_intr; 122}; 123 124static void atpic_enable_source(struct intsrc *isrc); 125static void atpic_disable_source(struct intsrc *isrc); 126static void atpic_eoi_master(struct intsrc *isrc); 127static void atpic_eoi_slave(struct intsrc *isrc); 128static void atpic_enable_intr(struct intsrc *isrc); 129static int atpic_vector(struct intsrc *isrc); 130static void atpic_resume(struct intsrc *isrc); 131static int atpic_source_pending(struct intsrc *isrc); 132static void i8259_init(struct atpic *pic, int slave); 133 134static struct atpic atpics[] = { 135 ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen), 136 ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1) 137}; 138 139static struct atpic_intsrc atintrs[] = { 140 INTSRC(0), 141 INTSRC(1), 142 INTSRC(2), 143 INTSRC(3), 144 INTSRC(4), 145 INTSRC(5), 146 INTSRC(6), 147 INTSRC(7), 148 INTSRC(8), 149 INTSRC(9), 150 INTSRC(10), 151 INTSRC(11), 152 INTSRC(12), 153 INTSRC(13), 154 INTSRC(14), 155 INTSRC(15), 156}; 157 158static void 159atpic_enable_source(struct intsrc *isrc) 160{ 161 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 162 struct atpic *ap = (struct atpic *)isrc->is_pic; 163 164 mtx_lock_spin(&icu_lock); 165 *ap->at_imen &= ~(1 << ai->at_irq); 166 outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); 167 mtx_unlock_spin(&icu_lock); 168} 169 170static void 171atpic_disable_source(struct intsrc *isrc) 172{ 173 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 174 struct atpic *ap = (struct atpic *)isrc->is_pic; 175 176 mtx_lock_spin(&icu_lock); 177 *ap->at_imen |= (1 << ai->at_irq); 178 outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); 179 mtx_unlock_spin(&icu_lock); 180} 181 182static void 183atpic_eoi_master(struct intsrc *isrc) 184{ 185 186 KASSERT(isrc->is_pic == &atpics[MASTER].at_pic, 187 ("%s: mismatched pic", __func__)); 188#ifndef AUTO_EOI_1 189 mtx_lock_spin(&icu_lock); 190 outb(atpics[MASTER].at_ioaddr, ICU_EOI); 191 mtx_unlock_spin(&icu_lock); 192#endif 193} 194 195static void 196atpic_eoi_slave(struct intsrc *isrc) 197{ 198 199 KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic, 200 ("%s: mismatched pic", __func__)); 201#ifndef AUTO_EOI_2 202 mtx_lock_spin(&icu_lock); 203 outb(atpics[SLAVE].at_ioaddr, ICU_EOI); 204#ifndef AUTO_EOI_1 205 outb(atpics[MASTER].at_ioaddr, ICU_EOI); 206#endif 207 mtx_unlock_spin(&icu_lock); 208#endif 209} 210 211static void 212atpic_enable_intr(struct intsrc *isrc) 213{ 214 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 215 struct atpic *ap = (struct atpic *)isrc->is_pic; 216 register_t eflags; 217 218 mtx_lock_spin(&icu_lock); 219 eflags = intr_disable(); 220 setidt(ap->at_intbase + ai->at_irq, ai->at_intr, SDT_SYS386IGT, 221 SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 222 intr_restore(eflags); 223 mtx_unlock_spin(&icu_lock); 224} 225 226static int 227atpic_vector(struct intsrc *isrc) 228{ 229 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 230 struct atpic *ap = (struct atpic *)isrc->is_pic; 231 232 return (IRQ(ap, ai)); 233} 234 235static int 236atpic_source_pending(struct intsrc *isrc) 237{ 238 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 239 struct atpic *ap = (struct atpic *)isrc->is_pic; 240 241 return (inb(ap->at_ioaddr) & (1 << ai->at_irq)); 242} 243 244static void 245atpic_resume(struct intsrc *isrc) 246{ 247 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 248 struct atpic *ap = (struct atpic *)isrc->is_pic; 249 250 if (ai->at_irq == 0) 251 i8259_init(ap, ap == &atpics[SLAVE]); 252} 253 254static void 255i8259_init(struct atpic *pic, int slave) 256{ 257 int imr_addr; 258 259 /* Reset the PIC and program with next four bytes. */ 260 mtx_lock_spin(&icu_lock); 261#ifdef DEV_MCA 262 if (MCA_system) 263 outb(pic->at_ioaddr, 0x19); 264 else 265#endif 266 outb(pic->at_ioaddr, 0x11); 267 imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET; 268 269 /* Start vector. */ 270 outb(imr_addr, pic->at_intbase); 271 272 /* 273 * Setup slave links. For the master pic, indicate what line 274 * the slave is configured on. For the slave indicate 275 * which line on the master we are connected to. 276 */ 277 if (slave) 278 outb(imr_addr, ICU_SLAVEID); /* my slave id is 7 */ 279 else 280 outb(imr_addr, IRQ_SLAVE); /* slave on line 7 */ 281 282 /* Set mode. */ 283 if (slave) 284 outb(imr_addr, SLAVE_MODE); 285 else 286 outb(imr_addr, MASTER_MODE); 287 288 /* Set interrupt enable mask. */ 289 outb(imr_addr, *pic->at_imen); 290 291 /* Reset is finished, default to IRR on read. */ 292 outb(pic->at_ioaddr, 0x0a); 293 294#ifndef PC98 295 /* Set priority order to 3-7, 0-2 (com2 first). */ 296 if (!slave) 297 outb(pic->at_ioaddr, 0xc0 | (3 - 1)); 298#endif 299 mtx_unlock_spin(&icu_lock); 300} 301 302void 303atpic_startup(void) 304{ 305 306 /* Start off with all interrupts disabled. */ 307 imen = 0xffff; 308 i8259_init(&atpics[MASTER], 0); 309 i8259_init(&atpics[SLAVE], 1); 310 atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]); 311} 312 313static void 314atpic_init(void *dummy __unused) 315{ 316 struct atpic_intsrc *ai; 317 int i; 318 319 /* Loop through all interrupt sources and add them. */ 320 for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) { 321 if (i == ICU_SLAVEID) 322 continue; 323 ai = &atintrs[i]; 324 intr_register_source(&ai->at_intsrc); 325 } 326} 327SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL) 328 329void 330atpic_sched_ithd(struct intrframe iframe) 331{ 332 struct intsrc *isrc; 333 334 KASSERT((uint)iframe.if_vec < ICU_LEN, 335 ("unknown int %d\n", iframe.if_vec)); 336 isrc = &atintrs[iframe.if_vec].at_intsrc; 337 intr_execute_handlers(isrc, &iframe); 338} 339 340#ifdef DEV_ISA 341/* 342 * Bus attachment for the ISA PIC. 343 */ 344static struct isa_pnp_id atpic_ids[] = { 345 { 0x0000d041 /* PNP0000 */, "AT interrupt controller" }, 346 { 0 } 347}; 348 349static int 350atpic_probe(device_t dev) 351{ 352 int result; 353 354 result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids); 355 if (result <= 0) 356 device_quiet(dev); 357 return (result); 358} 359 360/* 361 * We might be granted IRQ 2, as this is typically consumed by chaining 362 * between the two PIC components. If we're using the APIC, however, 363 * this may not be the case, and as such we should free the resource. 364 * (XXX untested) 365 * 366 * The generic ISA attachment code will handle allocating any other resources 367 * that we don't explicitly claim here. 368 */ 369static int 370atpic_attach(device_t dev) 371{ 372 struct resource *res; 373 int rid; 374 375 /* Try to allocate our IRQ and then free it. */ 376 rid = 0; 377 res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 0); 378 if (res != NULL) 379 bus_release_resource(dev, SYS_RES_IRQ, rid, res); 380 return (0); 381} 382 383static device_method_t atpic_methods[] = { 384 /* Device interface */ 385 DEVMETHOD(device_probe, atpic_probe), 386 DEVMETHOD(device_attach, atpic_attach), 387 DEVMETHOD(device_detach, bus_generic_detach), 388 DEVMETHOD(device_shutdown, bus_generic_shutdown), 389 DEVMETHOD(device_suspend, bus_generic_suspend), 390 DEVMETHOD(device_resume, bus_generic_resume), 391 { 0, 0 } 392}; 393 394static driver_t atpic_driver = { 395 "atpic", 396 atpic_methods, 397 1, /* no softc */ 398}; 399 400static devclass_t atpic_devclass; 401 402DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0); 403#ifndef PC98 404DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0); 405#endif 406 407/* 408 * Return a bitmap of the current interrupt requests. This is 8259-specific 409 * and is only suitable for use at probe time. 410 */ 411intrmask_t 412isa_irq_pending(void) 413{ 414 u_char irr1; 415 u_char irr2; 416 417 irr1 = inb(IO_ICU1); 418 irr2 = inb(IO_ICU2); 419 return ((irr2 << 8) | irr1); 420} 421#endif /* DEV_ISA */ 422