atpic.c revision 195249
1227006Smarius/*- 2227006Smarius * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org> 3227006Smarius * All rights reserved. 4227006Smarius * 5227006Smarius * Redistribution and use in source and binary forms, with or without 6227006Smarius * modification, are permitted provided that the following conditions 7227006Smarius * are met: 8227006Smarius * 1. Redistributions of source code must retain the above copyright 9227006Smarius * notice, this list of conditions and the following disclaimer. 10227006Smarius * 2. Redistributions in binary form must reproduce the above copyright 11227006Smarius * notice, this list of conditions and the following disclaimer in the 12227006Smarius * documentation and/or other materials provided with the distribution. 13227006Smarius * 3. Neither the name of the author nor the names of any co-contributors 14227006Smarius * may be used to endorse or promote products derived from this software 15227006Smarius * without specific prior written permission. 16227006Smarius * 17227006Smarius * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18227006Smarius * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19227006Smarius * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20227006Smarius * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21227006Smarius * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22227006Smarius * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23227006Smarius * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24227006Smarius * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25227006Smarius * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26227006Smarius * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27227006Smarius * SUCH DAMAGE. 28227006Smarius */ 29227006Smarius 30227006Smarius/* 31227006Smarius * PIC driver for the 8259A Master and Slave PICs in PC/AT machines. 32227006Smarius */ 33227006Smarius 34227006Smarius#include <sys/cdefs.h> 35227006Smarius__FBSDID("$FreeBSD: head/sys/i386/isa/atpic.c 195249 2009-07-01 17:20:07Z jhb $"); 36227006Smarius 37227006Smarius#include "opt_auto_eoi.h" 38227006Smarius#include "opt_isa.h" 39227006Smarius 40227006Smarius#include <sys/param.h> 41227006Smarius#include <sys/systm.h> 42227006Smarius#include <sys/bus.h> 43227006Smarius#include <sys/interrupt.h> 44227006Smarius#include <sys/kernel.h> 45227006Smarius#include <sys/lock.h> 46227006Smarius#include <sys/module.h> 47227006Smarius 48227006Smarius#include <machine/cpufunc.h> 49227006Smarius#include <machine/frame.h> 50227006Smarius#include <machine/intr_machdep.h> 51227006Smarius#include <machine/md_var.h> 52227006Smarius#include <machine/resource.h> 53227006Smarius#include <machine/segments.h> 54227006Smarius 55227006Smarius#include <dev/ic/i8259.h> 56227006Smarius#include <i386/isa/icu.h> 57227006Smarius#ifdef PC98 58227006Smarius#include <pc98/cbus/cbus.h> 59227006Smarius#else 60227006Smarius#include <i386/isa/isa.h> 61227006Smarius#endif 62227006Smarius#include <isa/isavar.h> 63227006Smarius 64227006Smarius#define MASTER 0 65227006Smarius#define SLAVE 1 66227006Smarius 67227006Smarius/* 68227006Smarius * PC-98 machines wire the slave 8259A to pin 7 on the master PIC, and 69227006Smarius * PC-AT machines wire the slave PIC to pin 2 on the master PIC. 70227006Smarius */ 71227006Smarius#ifdef PC98 72227006Smarius#define ICU_SLAVEID 7 73227006Smarius#else 74227006Smarius#define ICU_SLAVEID 2 75227006Smarius#endif 76227006Smarius 77227006Smarius/* 78227006Smarius * Determine the base master and slave modes not including auto EOI support. 79227006Smarius * All machines that FreeBSD supports use 8086 mode. 80227006Smarius */ 81227006Smarius#ifdef PC98 82227006Smarius/* 83227006Smarius * PC-98 machines do not support auto EOI on the second PIC. Also, it 84227006Smarius * seems that PC-98 machine PICs use buffered mode, and the master PIC 85227006Smarius * uses special fully nested mode. 86227006Smarius */ 87227006Smarius#define BASE_MASTER_MODE (ICW4_SFNM | ICW4_BUF | ICW4_MS | ICW4_8086) 88227006Smarius#define BASE_SLAVE_MODE (ICW4_BUF | ICW4_8086) 89227006Smarius#else 90227006Smarius#define BASE_MASTER_MODE ICW4_8086 91227006Smarius#define BASE_SLAVE_MODE ICW4_8086 92227006Smarius#endif 93227006Smarius 94227006Smarius/* Enable automatic EOI if requested. */ 95227006Smarius#ifdef AUTO_EOI_1 96227006Smarius#define MASTER_MODE (BASE_MASTER_MODE | ICW4_AEOI) 97227006Smarius#else 98227006Smarius#define MASTER_MODE BASE_MASTER_MODE 99227006Smarius#endif 100227006Smarius#ifdef AUTO_EOI_2 101227006Smarius#define SLAVE_MODE (BASE_SLAVE_MODE | ICW4_AEOI) 102227006Smarius#else 103227006Smarius#define SLAVE_MODE BASE_SLAVE_MODE 104227006Smarius#endif 105227006Smarius 106227006Smarius#define IRQ_MASK(irq) (1 << (irq)) 107227006Smarius#define IMEN_MASK(ai) (IRQ_MASK((ai)->at_irq)) 108227006Smarius 109227006Smarius#define NUM_ISA_IRQS 16 110227006Smarius 111227006Smariusstatic void atpic_init(void *dummy); 112227006Smarius 113227006Smariusunsigned int imen; /* XXX */ 114227006Smarius 115227006Smariusinthand_t 116227006Smarius IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2), 117227006Smarius IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5), 118227006Smarius IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8), 119227006Smarius IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11), 120227006Smarius IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14), 121227006Smarius IDTVEC(atpic_intr15); 122227006Smarius 123227006Smarius#define IRQ(ap, ai) ((ap)->at_irqbase + (ai)->at_irq) 124227006Smarius 125227006Smarius#define ATPIC(io, base, eoi, imenptr) \ 126227006Smarius { { atpic_enable_source, atpic_disable_source, (eoi), \ 127227006Smarius atpic_enable_intr, atpic_disable_intr, atpic_vector, \ 128227006Smarius atpic_source_pending, NULL, atpic_resume, atpic_config_intr,\ 129227006Smarius atpic_assign_cpu }, (io), (base), IDT_IO_INTS + (base), \ 130227006Smarius (imenptr) } 131227006Smarius 132227006Smarius#define INTSRC(irq) \ 133227006Smarius { { &atpics[(irq) / 8].at_pic }, IDTVEC(atpic_intr ## irq ), \ 134227006Smarius (irq) % 8 } 135227006Smarius 136227006Smariusstruct atpic { 137227006Smarius struct pic at_pic; 138227006Smarius int at_ioaddr; 139227006Smarius int at_irqbase; 140227006Smarius uint8_t at_intbase; 141227006Smarius uint8_t *at_imen; 142227006Smarius}; 143227006Smarius 144227006Smariusstruct atpic_intsrc { 145227006Smarius struct intsrc at_intsrc; 146227006Smarius inthand_t *at_intr; 147227006Smarius int at_irq; /* Relative to PIC base. */ 148227006Smarius enum intr_trigger at_trigger; 149227848Smarius u_long at_count; 150227006Smarius u_long at_straycount; 151227006Smarius}; 152227006Smarius 153227006Smariusstatic void atpic_enable_source(struct intsrc *isrc); 154227006Smariusstatic void atpic_disable_source(struct intsrc *isrc, int eoi); 155227006Smariusstatic void atpic_eoi_master(struct intsrc *isrc); 156227006Smariusstatic void atpic_eoi_slave(struct intsrc *isrc); 157227006Smariusstatic void atpic_enable_intr(struct intsrc *isrc); 158227006Smariusstatic void atpic_disable_intr(struct intsrc *isrc); 159227006Smariusstatic int atpic_vector(struct intsrc *isrc); 160227006Smariusstatic void atpic_resume(struct pic *pic); 161227006Smariusstatic int atpic_source_pending(struct intsrc *isrc); 162227006Smariusstatic int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig, 163227006Smarius enum intr_polarity pol); 164227006Smariusstatic int atpic_assign_cpu(struct intsrc *isrc, u_int apic_id); 165227006Smariusstatic void i8259_init(struct atpic *pic, int slave); 166227006Smarius 167227006Smariusstatic struct atpic atpics[] = { 168227006Smarius ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen), 169227006Smarius ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1) 170227006Smarius}; 171227006Smarius 172227006Smariusstatic struct atpic_intsrc atintrs[] = { 173227006Smarius INTSRC(0), 174227006Smarius INTSRC(1), 175227006Smarius INTSRC(2), 176227006Smarius INTSRC(3), 177227006Smarius INTSRC(4), 178227006Smarius INTSRC(5), 179227006Smarius INTSRC(6), 180227006Smarius INTSRC(7), 181227006Smarius INTSRC(8), 182227006Smarius INTSRC(9), 183227006Smarius INTSRC(10), 184227006Smarius INTSRC(11), 185227006Smarius INTSRC(12), 186227006Smarius INTSRC(13), 187227006Smarius INTSRC(14), 188227006Smarius INTSRC(15), 189227006Smarius}; 190227006Smarius 191227006SmariusCTASSERT(sizeof(atintrs) / sizeof(atintrs[0]) == NUM_ISA_IRQS); 192227006Smarius 193227006Smariusstatic __inline void 194227006Smarius_atpic_eoi_master(struct intsrc *isrc) 195227006Smarius{ 196227006Smarius 197227006Smarius KASSERT(isrc->is_pic == &atpics[MASTER].at_pic, 198227006Smarius ("%s: mismatched pic", __func__)); 199227006Smarius#ifndef AUTO_EOI_1 200227006Smarius outb(atpics[MASTER].at_ioaddr, OCW2_EOI); 201227006Smarius#endif 202227006Smarius} 203227006Smarius 204227006Smarius/* 205227006Smarius * The data sheet says no auto-EOI on slave, but it sometimes works. 206227006Smarius * So, if AUTO_EOI_2 is enabled, we use it. 207227006Smarius */ 208227006Smariusstatic __inline void 209227006Smarius_atpic_eoi_slave(struct intsrc *isrc) 210227006Smarius{ 211227006Smarius 212227006Smarius KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic, 213227006Smarius ("%s: mismatched pic", __func__)); 214227006Smarius#ifndef AUTO_EOI_2 215227006Smarius outb(atpics[SLAVE].at_ioaddr, OCW2_EOI); 216227006Smarius#ifndef AUTO_EOI_1 217227006Smarius outb(atpics[MASTER].at_ioaddr, OCW2_EOI); 218227006Smarius#endif 219227006Smarius#endif 220227006Smarius} 221227006Smarius 222227006Smariusstatic void 223227006Smariusatpic_enable_source(struct intsrc *isrc) 224227006Smarius{ 225227006Smarius struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 226227006Smarius struct atpic *ap = (struct atpic *)isrc->is_pic; 227227006Smarius 228227006Smarius spinlock_enter(); 229227006Smarius if (*ap->at_imen & IMEN_MASK(ai)) { 230227006Smarius *ap->at_imen &= ~IMEN_MASK(ai); 231227006Smarius outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); 232227006Smarius } 233227006Smarius spinlock_exit(); 234227006Smarius} 235227006Smarius 236227006Smariusstatic void 237227006Smariusatpic_disable_source(struct intsrc *isrc, int eoi) 238227006Smarius{ 239227006Smarius struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 240227006Smarius struct atpic *ap = (struct atpic *)isrc->is_pic; 241227006Smarius 242227006Smarius spinlock_enter(); 243227006Smarius if (ai->at_trigger != INTR_TRIGGER_EDGE) { 244227006Smarius *ap->at_imen |= IMEN_MASK(ai); 245227006Smarius outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); 246227006Smarius } 247227006Smarius 248227006Smarius /* 249227006Smarius * Take care to call these functions directly instead of through 250227006Smarius * a function pointer. All of the referenced variables should 251227006Smarius * still be hot in the cache. 252227006Smarius */ 253227006Smarius if (eoi == PIC_EOI) { 254227006Smarius if (isrc->is_pic == &atpics[MASTER].at_pic) 255227006Smarius _atpic_eoi_master(isrc); 256227006Smarius else 257227006Smarius _atpic_eoi_slave(isrc); 258227006Smarius } 259227006Smarius 260227006Smarius spinlock_exit(); 261227006Smarius} 262227006Smarius 263227006Smariusstatic void 264227006Smariusatpic_eoi_master(struct intsrc *isrc) 265227006Smarius{ 266227006Smarius#ifndef AUTO_EOI_1 267227006Smarius spinlock_enter(); 268227006Smarius _atpic_eoi_master(isrc); 269227006Smarius spinlock_exit(); 270227006Smarius#endif 271227006Smarius} 272227006Smarius 273227006Smariusstatic void 274227006Smariusatpic_eoi_slave(struct intsrc *isrc) 275227006Smarius{ 276227006Smarius#ifndef AUTO_EOI_2 277227006Smarius spinlock_enter(); 278227006Smarius _atpic_eoi_slave(isrc); 279227006Smarius spinlock_exit(); 280227006Smarius#endif 281227006Smarius} 282227006Smarius 283227006Smariusstatic void 284227006Smariusatpic_enable_intr(struct intsrc *isrc) 285227006Smarius{ 286227006Smarius} 287227006Smarius 288227006Smariusstatic void 289227006Smariusatpic_disable_intr(struct intsrc *isrc) 290227006Smarius{ 291227006Smarius} 292227006Smarius 293227006Smarius 294227006Smariusstatic int 295298955Spfgatpic_vector(struct intsrc *isrc) 296227006Smarius{ 297227006Smarius struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 298227006Smarius struct atpic *ap = (struct atpic *)isrc->is_pic; 299227006Smarius 300227006Smarius return (IRQ(ap, ai)); 301227006Smarius} 302227006Smarius 303227006Smariusstatic int 304227006Smariusatpic_source_pending(struct intsrc *isrc) 305227006Smarius{ 306227006Smarius struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 307227006Smarius struct atpic *ap = (struct atpic *)isrc->is_pic; 308227006Smarius 309227006Smarius return (inb(ap->at_ioaddr) & IMEN_MASK(ai)); 310227006Smarius} 311227006Smarius 312227006Smariusstatic void 313227006Smariusatpic_resume(struct pic *pic) 314227006Smarius{ 315227006Smarius struct atpic *ap = (struct atpic *)pic; 316227006Smarius 317227006Smarius i8259_init(ap, ap == &atpics[SLAVE]); 318227006Smarius#ifndef PC98 319227006Smarius if (ap == &atpics[SLAVE] && elcr_found) 320227006Smarius elcr_resume(); 321227006Smarius#endif 322227006Smarius} 323227006Smarius 324227006Smariusstatic int 325227006Smariusatpic_config_intr(struct intsrc *isrc, enum intr_trigger trig, 326227006Smarius enum intr_polarity pol) 327227006Smarius{ 328227006Smarius struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; 329227006Smarius u_int vector; 330227006Smarius 331227006Smarius /* Map conforming values to edge/hi and sanity check the values. */ 332227006Smarius if (trig == INTR_TRIGGER_CONFORM) 333227006Smarius trig = INTR_TRIGGER_EDGE; 334227006Smarius if (pol == INTR_POLARITY_CONFORM) 335227006Smarius pol = INTR_POLARITY_HIGH; 336227006Smarius vector = atpic_vector(isrc); 337227006Smarius if ((trig == INTR_TRIGGER_EDGE && pol == INTR_POLARITY_LOW) || 338227006Smarius (trig == INTR_TRIGGER_LEVEL && pol == INTR_POLARITY_HIGH)) { 339227006Smarius printf( 340227006Smarius "atpic: Mismatched config for IRQ%u: trigger %s, polarity %s\n", 341227006Smarius vector, trig == INTR_TRIGGER_EDGE ? "edge" : "level", 342227006Smarius pol == INTR_POLARITY_HIGH ? "high" : "low"); 343227006Smarius return (EINVAL); 344227006Smarius } 345227006Smarius 346227006Smarius /* If there is no change, just return. */ 347227006Smarius if (ai->at_trigger == trig) 348227006Smarius return (0); 349227006Smarius 350227006Smarius#ifdef PC98 351227006Smarius if ((vector == 0 || vector == 1 || vector == 7 || vector == 8) && 352227006Smarius trig == INTR_TRIGGER_LEVEL) { 353227006Smarius if (bootverbose) 354227006Smarius printf( 355227006Smarius "atpic: Ignoring invalid level/low configuration for IRQ%u\n", 356227006Smarius vector); 357227006Smarius return (EINVAL); 358227006Smarius } 359227006Smarius return (ENXIO); 360227006Smarius#else 361227006Smarius /* 362227006Smarius * Certain IRQs can never be level/lo, so don't try to set them 363227006Smarius * that way if asked. At least some ELCR registers ignore setting 364227006Smarius * these bits as well. 365227006Smarius */ 366227006Smarius if ((vector == 0 || vector == 1 || vector == 2 || vector == 13) && 367227006Smarius trig == INTR_TRIGGER_LEVEL) { 368227006Smarius if (bootverbose) 369227006Smarius printf( 370227006Smarius "atpic: Ignoring invalid level/low configuration for IRQ%u\n", 371227006Smarius vector); 372227006Smarius return (EINVAL); 373227006Smarius } 374227006Smarius if (!elcr_found) { 375227006Smarius if (bootverbose) 376227006Smarius printf("atpic: No ELCR to configure IRQ%u as %s\n", 377227006Smarius vector, trig == INTR_TRIGGER_EDGE ? "edge/high" : 378227006Smarius "level/low"); 379227006Smarius return (ENXIO); 380227006Smarius } 381227006Smarius if (bootverbose) 382227006Smarius printf("atpic: Programming IRQ%u as %s\n", vector, 383227006Smarius trig == INTR_TRIGGER_EDGE ? "edge/high" : "level/low"); 384227006Smarius spinlock_enter(); 385227006Smarius elcr_write_trigger(atpic_vector(isrc), trig); 386227006Smarius ai->at_trigger = trig; 387227006Smarius spinlock_exit(); 388227006Smarius return (0); 389227006Smarius#endif /* PC98 */ 390227006Smarius} 391227006Smarius 392227006Smariusstatic int 393227006Smariusatpic_assign_cpu(struct intsrc *isrc, u_int apic_id) 394227006Smarius{ 395227006Smarius 396227006Smarius /* 397227006Smarius * 8259A's are only used in UP in which case all interrupts always 398227006Smarius * go to the sole CPU and this function shouldn't even be called. 399227006Smarius */ 400227006Smarius panic("%s: bad cookie", __func__); 401227006Smarius} 402227006Smarius 403227006Smariusstatic void 404227006Smariusi8259_init(struct atpic *pic, int slave) 405227006Smarius{ 406227006Smarius int imr_addr; 407227006Smarius 408227006Smarius /* Reset the PIC and program with next four bytes. */ 409227006Smarius spinlock_enter(); 410227006Smarius#ifdef DEV_MCA 411227006Smarius /* MCA uses level triggered interrupts. */ 412227006Smarius if (MCA_system) 413227006Smarius outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM); 414227006Smarius else 415227006Smarius#endif 416227006Smarius outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4); 417227006Smarius imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET; 418227006Smarius 419227006Smarius /* Start vector. */ 420227006Smarius outb(imr_addr, pic->at_intbase); 421227006Smarius 422227006Smarius /* 423227006Smarius * Setup slave links. For the master pic, indicate what line 424227006Smarius * the slave is configured on. For the slave indicate 425227006Smarius * which line on the master we are connected to. 426227006Smarius */ 427227006Smarius if (slave) 428227006Smarius outb(imr_addr, ICU_SLAVEID); 429227006Smarius else 430227006Smarius outb(imr_addr, IRQ_MASK(ICU_SLAVEID)); 431227006Smarius 432227006Smarius /* Set mode. */ 433227006Smarius if (slave) 434227006Smarius outb(imr_addr, SLAVE_MODE); 435227006Smarius else 436227006Smarius outb(imr_addr, MASTER_MODE); 437227006Smarius 438227006Smarius /* Set interrupt enable mask. */ 439227006Smarius outb(imr_addr, *pic->at_imen); 440227006Smarius 441227006Smarius /* Reset is finished, default to IRR on read. */ 442227006Smarius outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR); 443227006Smarius 444227006Smarius#ifndef PC98 445227006Smarius /* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */ 446227006Smarius if (!slave) 447227006Smarius outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1); 448227006Smarius#endif 449227006Smarius spinlock_exit(); 450227006Smarius} 451227006Smarius 452227006Smariusvoid 453227006Smariusatpic_startup(void) 454227006Smarius{ 455227006Smarius struct atpic_intsrc *ai; 456227006Smarius int i; 457227006Smarius 458227006Smarius /* Start off with all interrupts disabled. */ 459227006Smarius imen = 0xffff; 460227006Smarius i8259_init(&atpics[MASTER], 0); 461227006Smarius i8259_init(&atpics[SLAVE], 1); 462227006Smarius atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]); 463227006Smarius 464227006Smarius /* Install low-level interrupt handlers for all of our IRQs. */ 465227006Smarius for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) { 466227006Smarius if (i == ICU_SLAVEID) 467227006Smarius continue; 468227006Smarius ai->at_intsrc.is_count = &ai->at_count; 469227006Smarius ai->at_intsrc.is_straycount = &ai->at_straycount; 470227006Smarius setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase + 471227006Smarius ai->at_irq, ai->at_intr, SDT_SYS386IGT, SEL_KPL, 472227006Smarius GSEL(GCODE_SEL, SEL_KPL)); 473227006Smarius } 474227006Smarius 475227006Smarius#ifdef DEV_MCA 476227006Smarius /* For MCA systems, all interrupts are level triggered. */ 477227006Smarius if (MCA_system) 478227006Smarius for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) 479227006Smarius ai->at_trigger = INTR_TRIGGER_LEVEL; 480227006Smarius else 481227006Smarius#endif 482227006Smarius 483227006Smarius#ifdef PC98 484227006Smarius for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) 485227006Smarius switch (i) { 486227006Smarius case 0: 487227006Smarius case 1: 488227006Smarius case 7: 489227006Smarius case 8: 490227006Smarius ai->at_trigger = INTR_TRIGGER_EDGE; 491227006Smarius break; 492227006Smarius default: 493227006Smarius ai->at_trigger = INTR_TRIGGER_LEVEL; 494227006Smarius break; 495227006Smarius } 496227006Smarius#else 497227006Smarius /* 498227006Smarius * Look for an ELCR. If we find one, update the trigger modes. 499227006Smarius * If we don't find one, assume that IRQs 0, 1, 2, and 13 are 500227006Smarius * edge triggered and that everything else is level triggered. 501227006Smarius * We only use the trigger information to reprogram the ELCR if 502227006Smarius * we have one and as an optimization to avoid masking edge 503227006Smarius * triggered interrupts. For the case that we don't have an ELCR, 504227006Smarius * it doesn't hurt to mask an edge triggered interrupt, so we 505227006Smarius * assume level trigger for any interrupt that we aren't sure is 506227006Smarius * edge triggered. 507227006Smarius */ 508227006Smarius if (elcr_found) { 509227006Smarius for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) 510227006Smarius ai->at_trigger = elcr_read_trigger(i); 511227006Smarius } else { 512227006Smarius for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) 513227006Smarius switch (i) { 514227006Smarius case 0: 515227006Smarius case 1: 516227006Smarius case 2: 517227006Smarius case 8: 518227006Smarius case 13: 519227006Smarius ai->at_trigger = INTR_TRIGGER_EDGE; 520227006Smarius break; 521227006Smarius default: 522227006Smarius ai->at_trigger = INTR_TRIGGER_LEVEL; 523227006Smarius break; 524227006Smarius } 525227006Smarius } 526227006Smarius#endif /* PC98 */ 527227006Smarius} 528227006Smarius 529227006Smariusstatic void 530227006Smariusatpic_init(void *dummy __unused) 531227006Smarius{ 532227006Smarius struct atpic_intsrc *ai; 533227006Smarius int i; 534227006Smarius 535227006Smarius /* 536227006Smarius * Register our PICs, even if we aren't going to use any of their 537227006Smarius * pins so that they are suspended and resumed. 538227006Smarius */ 539227006Smarius if (intr_register_pic(&atpics[0].at_pic) != 0 || 540227006Smarius intr_register_pic(&atpics[1].at_pic) != 0) 541227006Smarius panic("Unable to register ATPICs"); 542227006Smarius 543227006Smarius /* 544227006Smarius * If any of the ISA IRQs have an interrupt source already, then 545227006Smarius * assume that the APICs are being used and don't register any 546227006Smarius * of our interrupt sources. This makes sure we don't accidentally 547227006Smarius * use mixed mode. The "accidental" use could otherwise occur on 548227006Smarius * machines that route the ACPI SCI interrupt to a different ISA 549227006Smarius * IRQ (at least one machines routes it to IRQ 13) thus disabling 550227006Smarius * that APIC ISA routing and allowing the ATPIC source for that IRQ 551227006Smarius * to leak through. We used to depend on this feature for routing 552227006Smarius * IRQ0 via mixed mode, but now we don't use mixed mode at all. 553227006Smarius */ 554227006Smarius for (i = 0; i < NUM_ISA_IRQS; i++) 555227006Smarius if (intr_lookup_source(i) != NULL) 556227006Smarius return; 557227006Smarius 558227006Smarius /* Loop through all interrupt sources and add them. */ 559227006Smarius for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) { 560227006Smarius if (i == ICU_SLAVEID) 561227006Smarius continue; 562227006Smarius intr_register_source(&ai->at_intsrc); 563227006Smarius } 564227006Smarius} 565227006SmariusSYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL); 566227006Smarius 567227006Smariusvoid 568227006Smariusatpic_handle_intr(u_int vector, struct trapframe *frame) 569227006Smarius{ 570227006Smarius struct intsrc *isrc; 571227006Smarius 572227006Smarius KASSERT(vector < NUM_ISA_IRQS, ("unknown int %u\n", vector)); 573227006Smarius isrc = &atintrs[vector].at_intsrc; 574227006Smarius 575227006Smarius /* 576227006Smarius * If we don't have an event, see if this is a spurious 577227006Smarius * interrupt. 578227006Smarius */ 579227006Smarius if (isrc->is_event == NULL && (vector == 7 || vector == 15)) { 580227006Smarius int port, isr; 581227006Smarius 582227006Smarius /* 583227006Smarius * Read the ISR register to see if IRQ 7/15 is really 584227006Smarius * pending. Reset read register back to IRR when done. 585227006Smarius */ 586227006Smarius port = ((struct atpic *)isrc->is_pic)->at_ioaddr; 587227006Smarius spinlock_enter(); 588227006Smarius outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS); 589227006Smarius isr = inb(port); 590227006Smarius outb(port, OCW3_SEL | OCW3_RR); 591227006Smarius spinlock_exit(); 592227006Smarius if ((isr & IRQ_MASK(7)) == 0) 593227006Smarius return; 594227006Smarius } 595227006Smarius intr_execute_handlers(isrc, frame); 596227006Smarius} 597227006Smarius 598227006Smarius#ifdef DEV_ISA 599227006Smarius/* 600227006Smarius * Bus attachment for the ISA PIC. 601227006Smarius */ 602227006Smariusstatic struct isa_pnp_id atpic_ids[] = { 603227006Smarius { 0x0000d041 /* PNP0000 */, "AT interrupt controller" }, 604227006Smarius { 0 } 605227006Smarius}; 606227006Smarius 607227006Smariusstatic int 608227006Smariusatpic_probe(device_t dev) 609227006Smarius{ 610227006Smarius int result; 611227006Smarius 612227006Smarius result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids); 613227006Smarius if (result <= 0) 614227006Smarius device_quiet(dev); 615227006Smarius return (result); 616227006Smarius} 617227006Smarius 618227006Smarius/* 619227006Smarius * We might be granted IRQ 2, as this is typically consumed by chaining 620227006Smarius * between the two PIC components. If we're using the APIC, however, 621227006Smarius * this may not be the case, and as such we should free the resource. 622227006Smarius * (XXX untested) 623227006Smarius * 624227006Smarius * The generic ISA attachment code will handle allocating any other resources 625227006Smarius * that we don't explicitly claim here. 626227006Smarius */ 627227006Smariusstatic int 628227006Smariusatpic_attach(device_t dev) 629227006Smarius{ 630227006Smarius struct resource *res; 631227006Smarius int rid; 632227006Smarius 633227006Smarius /* Try to allocate our IRQ and then free it. */ 634227006Smarius rid = 0; 635227006Smarius res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 0); 636227006Smarius if (res != NULL) 637227006Smarius bus_release_resource(dev, SYS_RES_IRQ, rid, res); 638227006Smarius return (0); 639227006Smarius} 640227006Smarius 641227006Smariusstatic device_method_t atpic_methods[] = { 642227006Smarius /* Device interface */ 643227006Smarius DEVMETHOD(device_probe, atpic_probe), 644227006Smarius DEVMETHOD(device_attach, atpic_attach), 645227006Smarius DEVMETHOD(device_detach, bus_generic_detach), 646227006Smarius DEVMETHOD(device_shutdown, bus_generic_shutdown), 647227006Smarius DEVMETHOD(device_suspend, bus_generic_suspend), 648227006Smarius DEVMETHOD(device_resume, bus_generic_resume), 649227006Smarius { 0, 0 } 650227006Smarius}; 651227006Smarius 652227006Smariusstatic driver_t atpic_driver = { 653227006Smarius "atpic", 654227006Smarius atpic_methods, 655 1, /* no softc */ 656}; 657 658static devclass_t atpic_devclass; 659 660DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0); 661#ifndef PC98 662DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0); 663#endif 664 665/* 666 * Return a bitmap of the current interrupt requests. This is 8259-specific 667 * and is only suitable for use at probe time. 668 */ 669intrmask_t 670isa_irq_pending(void) 671{ 672 u_char irr1; 673 u_char irr2; 674 675 irr1 = inb(IO_ICU1); 676 irr2 = inb(IO_ICU2); 677 return ((irr2 << 8) | irr1); 678} 679#endif /* DEV_ISA */ 680