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