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