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