atpic.c revision 122897
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 122897 2003-11-19 15:38:56Z 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 intr_execute_handlers(isrc, &iframe); 347} 348 349#ifdef DEV_ISA 350/* 351 * Bus attachment for the ISA PIC. 352 */ 353static struct isa_pnp_id atpic_ids[] = { 354 { 0x0000d041 /* PNP0000 */, "AT interrupt controller" }, 355 { 0 } 356}; 357 358static int 359atpic_probe(device_t dev) 360{ 361 int result; 362 363 result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids); 364 if (result <= 0) 365 device_quiet(dev); 366 return (result); 367} 368 369/* 370 * We might be granted IRQ 2, as this is typically consumed by chaining 371 * between the two PIC components. If we're using the APIC, however, 372 * this may not be the case, and as such we should free the resource. 373 * (XXX untested) 374 * 375 * The generic ISA attachment code will handle allocating any other resources 376 * that we don't explicitly claim here. 377 */ 378static int 379atpic_attach(device_t dev) 380{ 381 struct resource *res; 382 int rid; 383 384 /* Try to allocate our IRQ and then free it. */ 385 rid = 0; 386 res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 0); 387 if (res != NULL) 388 bus_release_resource(dev, SYS_RES_IRQ, rid, res); 389 return (0); 390} 391 392static device_method_t atpic_methods[] = { 393 /* Device interface */ 394 DEVMETHOD(device_probe, atpic_probe), 395 DEVMETHOD(device_attach, atpic_attach), 396 DEVMETHOD(device_detach, bus_generic_detach), 397 DEVMETHOD(device_shutdown, bus_generic_shutdown), 398 DEVMETHOD(device_suspend, bus_generic_suspend), 399 DEVMETHOD(device_resume, bus_generic_resume), 400 { 0, 0 } 401}; 402 403static driver_t atpic_driver = { 404 "atpic", 405 atpic_methods, 406 1, /* no softc */ 407}; 408 409static devclass_t atpic_devclass; 410 411DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0); 412#ifndef PC98 413DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0); 414#endif 415 416/* 417 * Return a bitmap of the current interrupt requests. This is 8259-specific 418 * and is only suitable for use at probe time. 419 */ 420intrmask_t 421isa_irq_pending(void) 422{ 423 u_char irr1; 424 u_char irr2; 425 426 irr1 = inb(IO_ICU1); 427 irr2 = inb(IO_ICU2); 428 return ((irr2 << 8) | irr1); 429} 430#endif /* DEV_ISA */ 431