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