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