atpic.c revision 122692
1306196Sjkim/*- 2160819Ssimon * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org> 3160819Ssimon * All rights reserved. 4160819Ssimon * 5160819Ssimon * Redistribution and use in source and binary forms, with or without 6160819Ssimon * modification, are permitted provided that the following conditions 7160819Ssimon * are met: 8160819Ssimon * 1. Redistributions of source code must retain the above copyright 9160819Ssimon * notice, this list of conditions and the following disclaimer. 10160819Ssimon * 2. Redistributions in binary form must reproduce the above copyright 11160819Ssimon * notice, this list of conditions and the following disclaimer in the 12160819Ssimon * documentation and/or other materials provided with the distribution. 13160819Ssimon * 3. Neither the name of the author nor the names of any co-contributors 14160819Ssimon * may be used to endorse or promote products derived from this software 15160819Ssimon * without specific prior written permission. 16160819Ssimon * 17160819Ssimon * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18160819Ssimon * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19160819Ssimon * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20215698Ssimon * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21215698Ssimon * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22215698Ssimon * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23215698Ssimon * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24215698Ssimon * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25160819Ssimon * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26160819Ssimon * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27160819Ssimon * SUCH DAMAGE. 28160819Ssimon */ 29160819Ssimon 30160819Ssimon/* 31160819Ssimon * PIC driver for the 8259A Master and Slave PICs in PC/AT machines. 32160819Ssimon */ 33160819Ssimon 34160819Ssimon#include <sys/cdefs.h> 35160819Ssimon__FBSDID("$FreeBSD: head/sys/i386/isa/atpic.c 122692 2003-11-14 19:13:06Z jhb $"); 36160819Ssimon 37160819Ssimon#include "opt_auto_eoi.h" 38160819Ssimon#include "opt_isa.h" 39160819Ssimon 40160819Ssimon#include <sys/param.h> 41276864Sjkim#include <sys/systm.h> 42276864Sjkim#include <sys/bus.h> 43160819Ssimon#include <sys/interrupt.h> 44160819Ssimon#include <sys/kernel.h> 45215698Ssimon#include <sys/lock.h> 46215698Ssimon#include <sys/mutex.h> 47215698Ssimon 48215698Ssimon#include <machine/cpufunc.h> 49160819Ssimon#include <machine/frame.h> 50215698Ssimon#include <machine/intr_machdep.h> 51160819Ssimon#include <machine/md_var.h> 52160819Ssimon#include <machine/resource.h> 53276864Sjkim#include <machine/segments.h> 54276864Sjkim 55276864Sjkim#include <i386/isa/icu.h> 56160819Ssimon#ifdef PC98 57276864Sjkim#include <pc98/pc98/pc98.h> 58276864Sjkim#else 59276864Sjkim#include <i386/isa/isa.h> 60276864Sjkim#endif 61276864Sjkim#include <isa/isavar.h> 62276864Sjkim 63215698Ssimon#define MASTER 0 64276864Sjkim#define SLAVE 1 65276864Sjkim 66276864Sjkim/* XXX: Magic numbers */ 67276864Sjkim#ifdef PC98 68276864Sjkim#ifdef AUTO_EOI_1 69215698Ssimon#define MASTER_MODE 0x1f /* Master auto EOI, 8086 mode */ 70276864Sjkim#else 71160819Ssimon#define MASTER_MODE 0x1d /* Master 8086 mode */ 72160819Ssimon#endif 73160819Ssimon#define SLAVE_MODE 9 /* 8086 mode */ 74160819Ssimon#else /* IBM-PC */ 75160819Ssimon#ifdef AUTO_EOI_1 76160819Ssimon#define MASTER_MODE (ICW4_8086 | ICW4_AEOI) 77160819Ssimon#else 78160819Ssimon#define MASTER_MODE ICW4_8086 79160819Ssimon#endif 80160819Ssimon#ifdef AUTO_EOI_2 81160819Ssimon#define SLAVE_MODE (ICW4_8086 | ICW4_AEOI) 82160819Ssimon#else 83160819Ssimon#define SLAVE_MODE ICW4_8086 84160819Ssimon#endif 85160819Ssimon#endif /* PC98 */ 86160819Ssimon 87160819Ssimonstatic void atpic_init(void *dummy); 88160819Ssimon 89160819Ssimonunsigned int imen; /* XXX */ 90160819Ssimon 91160819Ssimoninthand_t 92160819Ssimon IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2), 93160819Ssimon IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5), 94160819Ssimon IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8), 95160819Ssimon IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11), 96160819Ssimon IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14), 97160819Ssimon IDTVEC(atpic_intr15); 98160819Ssimon 99160819Ssimon#define IRQ(ap, ai) ((ap)->at_irqbase + (ai)->at_irq) 100160819Ssimon 101160819Ssimon#define ATPIC(io, base, eoi, imenptr) \ 102160819Ssimon { { atpic_enable_source, atpic_disable_source, (eoi), \ 103160819Ssimon atpic_enable_intr, atpic_vector, atpic_source_pending, NULL, \ 104160819Ssimon atpic_resume }, (io), (base), IDT_IO_INTS + (base), (imenptr) } 105160819Ssimon 106160819Ssimon#define INTSRC(irq) \ 107160819Ssimon { { &atpics[(irq) / 8].at_pic }, (irq) % 8, \ 108160819Ssimon IDTVEC(atpic_intr ## irq ) } 109160819Ssimon 110160819Ssimonstruct atpic { 111160819Ssimon struct pic at_pic; 112160819Ssimon int at_ioaddr; 113160819Ssimon int at_irqbase; 114160819Ssimon uint8_t at_intbase; 115160819Ssimon uint8_t *at_imen; 116160819Ssimon}; 117160819Ssimon 118160819Ssimonstruct atpic_intsrc { 119160819Ssimon struct intsrc at_intsrc; 120160819Ssimon int at_irq; /* Relative to PIC base. */ 121160819Ssimon inthand_t *at_intr; 122160819Ssimon}; 123160819Ssimon 124160819Ssimonstatic void atpic_enable_source(struct intsrc *isrc); 125160819Ssimonstatic void atpic_disable_source(struct intsrc *isrc); 126160819Ssimonstatic void atpic_eoi_master(struct intsrc *isrc); 127160819Ssimonstatic void atpic_eoi_slave(struct intsrc *isrc); 128160819Ssimonstatic void atpic_enable_intr(struct intsrc *isrc); 129160819Ssimonstatic int atpic_vector(struct intsrc *isrc); 130160819Ssimonstatic void atpic_resume(struct intsrc *isrc); 131160819Ssimonstatic int atpic_source_pending(struct intsrc *isrc); 132160819Ssimonstatic void i8259_init(struct atpic *pic, int slave); 133160819Ssimon 134160819Ssimonstatic struct atpic atpics[] = { 135160819Ssimon ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen), 136306196Sjkim ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1) 137215698Ssimon}; 138215698Ssimon 139215698Ssimonstatic struct atpic_intsrc atintrs[] = { 140215698Ssimon INTSRC(0), 141160819Ssimon INTSRC(1), 142160819Ssimon INTSRC(2), 143160819Ssimon INTSRC(3), 144160819Ssimon INTSRC(4), 145160819Ssimon INTSRC(5), 146160819Ssimon INTSRC(6), 147160819Ssimon INTSRC(7), 148160819Ssimon INTSRC(8), 149160819Ssimon INTSRC(9), 150160819Ssimon INTSRC(10), 151160819Ssimon INTSRC(11), 152160819Ssimon INTSRC(12), 153160819Ssimon INTSRC(13), 154160819Ssimon INTSRC(14), 155160819Ssimon INTSRC(15), 156160819Ssimon}; 157194208Ssimon 158194208Ssimonstatic void 159194208Ssimonatpic_enable_source(struct intsrc *isrc) 160194208Ssimon{ 161194208Ssimon struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 162194208Ssimon struct atpic *ap = (struct atpic *)isrc->is_pic; 163194208Ssimon 164194208Ssimon mtx_lock_spin(&icu_lock); 165194208Ssimon *ap->at_imen &= ~(1 << ai->at_irq); 166194208Ssimon outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); 167194208Ssimon mtx_unlock_spin(&icu_lock); 168194208Ssimon} 169194208Ssimon 170194208Ssimonstatic void 171276864Sjkimatpic_disable_source(struct intsrc *isrc) 172194208Ssimon{ 173194208Ssimon struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 174194208Ssimon struct atpic *ap = (struct atpic *)isrc->is_pic; 175194208Ssimon 176194208Ssimon mtx_lock_spin(&icu_lock); 177194208Ssimon *ap->at_imen |= (1 << ai->at_irq); 178194208Ssimon 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 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 219 struct atpic *ap = (struct atpic *)isrc->is_pic; 220 register_t eflags; 221 222 mtx_lock_spin(&icu_lock); 223 eflags = intr_disable(); 224 setidt(ap->at_intbase + ai->at_irq, ai->at_intr, SDT_SYS386IGT, 225 SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); 226 intr_restore(eflags); 227 mtx_unlock_spin(&icu_lock); 228} 229 230static int 231atpic_vector(struct intsrc *isrc) 232{ 233 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 234 struct atpic *ap = (struct atpic *)isrc->is_pic; 235 236 return (IRQ(ap, ai)); 237} 238 239static int 240atpic_source_pending(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 (inb(ap->at_ioaddr) & (1 << ai->at_irq)); 246} 247 248static void 249atpic_resume(struct intsrc *isrc) 250{ 251 struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 252 struct atpic *ap = (struct atpic *)isrc->is_pic; 253 254 if (ai->at_irq == 0) 255 i8259_init(ap, ap == &atpics[SLAVE]); 256} 257 258static void 259i8259_init(struct atpic *pic, int slave) 260{ 261 int imr_addr; 262 263 /* Reset the PIC and program with next four bytes. */ 264 mtx_lock_spin(&icu_lock); 265#ifdef DEV_MCA 266 /* MCA uses level triggered interrupts. */ 267 if (MCA_system) 268 outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM); 269 else 270#endif 271 outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4); 272 imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET; 273 274 /* Start vector. */ 275 outb(imr_addr, pic->at_intbase); 276 277 /* 278 * Setup slave links. For the master pic, indicate what line 279 * the slave is configured on. For the slave indicate 280 * which line on the master we are connected to. 281 */ 282 if (slave) 283 outb(imr_addr, ICU_SLAVEID); /* my slave id is 7 */ 284 else 285 outb(imr_addr, IRQ_SLAVE); /* slave on line 7 */ 286 287 /* Set mode. */ 288 if (slave) 289 outb(imr_addr, SLAVE_MODE); 290 else 291 outb(imr_addr, MASTER_MODE); 292 293 /* Set interrupt enable mask. */ 294 outb(imr_addr, *pic->at_imen); 295 296 /* Reset is finished, default to IRR on read. */ 297 outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR); 298 299#ifndef PC98 300 /* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */ 301 if (!slave) 302 outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1); 303#endif 304 mtx_unlock_spin(&icu_lock); 305} 306 307void 308atpic_startup(void) 309{ 310 311 /* Start off with all interrupts disabled. */ 312 imen = 0xffff; 313 i8259_init(&atpics[MASTER], 0); 314 i8259_init(&atpics[SLAVE], 1); 315 atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]); 316} 317 318static void 319atpic_init(void *dummy __unused) 320{ 321 struct atpic_intsrc *ai; 322 int i; 323 324 /* Loop through all interrupt sources and add them. */ 325 for (i = 0; i < sizeof(atintrs) / sizeof(struct atpic_intsrc); i++) { 326 if (i == ICU_SLAVEID) 327 continue; 328 ai = &atintrs[i]; 329 intr_register_source(&ai->at_intsrc); 330 } 331} 332SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL) 333 334void 335atpic_handle_intr(struct intrframe iframe) 336{ 337 struct intsrc *isrc; 338 339 KASSERT((uint)iframe.if_vec < ICU_LEN, 340 ("unknown int %d\n", iframe.if_vec)); 341 isrc = &atintrs[iframe.if_vec].at_intsrc; 342 intr_execute_handlers(isrc, &iframe); 343} 344 345#ifdef DEV_ISA 346/* 347 * Bus attachment for the ISA PIC. 348 */ 349static struct isa_pnp_id atpic_ids[] = { 350 { 0x0000d041 /* PNP0000 */, "AT interrupt controller" }, 351 { 0 } 352}; 353 354static int 355atpic_probe(device_t dev) 356{ 357 int result; 358 359 result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids); 360 if (result <= 0) 361 device_quiet(dev); 362 return (result); 363} 364 365/* 366 * We might be granted IRQ 2, as this is typically consumed by chaining 367 * between the two PIC components. If we're using the APIC, however, 368 * this may not be the case, and as such we should free the resource. 369 * (XXX untested) 370 * 371 * The generic ISA attachment code will handle allocating any other resources 372 * that we don't explicitly claim here. 373 */ 374static int 375atpic_attach(device_t dev) 376{ 377 struct resource *res; 378 int rid; 379 380 /* Try to allocate our IRQ and then free it. */ 381 rid = 0; 382 res = bus_alloc_resource(dev, SYS_RES_IRQ, &rid, 0, ~0, 1, 0); 383 if (res != NULL) 384 bus_release_resource(dev, SYS_RES_IRQ, rid, res); 385 return (0); 386} 387 388static device_method_t atpic_methods[] = { 389 /* Device interface */ 390 DEVMETHOD(device_probe, atpic_probe), 391 DEVMETHOD(device_attach, atpic_attach), 392 DEVMETHOD(device_detach, bus_generic_detach), 393 DEVMETHOD(device_shutdown, bus_generic_shutdown), 394 DEVMETHOD(device_suspend, bus_generic_suspend), 395 DEVMETHOD(device_resume, bus_generic_resume), 396 { 0, 0 } 397}; 398 399static driver_t atpic_driver = { 400 "atpic", 401 atpic_methods, 402 1, /* no softc */ 403}; 404 405static devclass_t atpic_devclass; 406 407DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0); 408#ifndef PC98 409DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0); 410#endif 411 412/* 413 * Return a bitmap of the current interrupt requests. This is 8259-specific 414 * and is only suitable for use at probe time. 415 */ 416intrmask_t 417isa_irq_pending(void) 418{ 419 u_char irr1; 420 u_char irr2; 421 422 irr1 = inb(IO_ICU1); 423 irr2 = inb(IO_ICU2); 424 return ((irr2 << 8) | irr1); 425} 426#endif /* DEV_ISA */ 427