diff options
Diffstat (limited to 'sys/i386/isa/atpic.c')
-rw-r--r-- | sys/i386/isa/atpic.c | 679 |
1 files changed, 0 insertions, 679 deletions
diff --git a/sys/i386/isa/atpic.c b/sys/i386/isa/atpic.c deleted file mode 100644 index 37a1285..0000000 --- a/sys/i386/isa/atpic.c +++ /dev/null @@ -1,679 +0,0 @@ -/*- - * Copyright (c) 2003 John Baldwin <jhb@FreeBSD.org> - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the author nor the names of any co-contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -/* - * PIC driver for the 8259A Master and Slave PICs in PC/AT machines. - */ - -#include <sys/cdefs.h> -__FBSDID("$FreeBSD$"); - -#include "opt_auto_eoi.h" -#include "opt_isa.h" - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/bus.h> -#include <sys/interrupt.h> -#include <sys/kernel.h> -#include <sys/lock.h> -#include <sys/module.h> - -#include <machine/cpufunc.h> -#include <machine/frame.h> -#include <machine/intr_machdep.h> -#include <machine/md_var.h> -#include <machine/resource.h> -#include <machine/segments.h> - -#include <dev/ic/i8259.h> -#include <i386/isa/icu.h> -#ifdef PC98 -#include <pc98/cbus/cbus.h> -#else -#include <i386/isa/isa.h> -#endif -#include <isa/isavar.h> - -#define MASTER 0 -#define SLAVE 1 - -/* - * PC-98 machines wire the slave 8259A to pin 7 on the master PIC, and - * PC-AT machines wire the slave PIC to pin 2 on the master PIC. - */ -#ifdef PC98 -#define ICU_SLAVEID 7 -#else -#define ICU_SLAVEID 2 -#endif - -/* - * Determine the base master and slave modes not including auto EOI support. - * All machines that FreeBSD supports use 8086 mode. - */ -#ifdef PC98 -/* - * PC-98 machines do not support auto EOI on the second PIC. Also, it - * seems that PC-98 machine PICs use buffered mode, and the master PIC - * uses special fully nested mode. - */ -#define BASE_MASTER_MODE (ICW4_SFNM | ICW4_BUF | ICW4_MS | ICW4_8086) -#define BASE_SLAVE_MODE (ICW4_BUF | ICW4_8086) -#else -#define BASE_MASTER_MODE ICW4_8086 -#define BASE_SLAVE_MODE ICW4_8086 -#endif - -/* Enable automatic EOI if requested. */ -#ifdef AUTO_EOI_1 -#define MASTER_MODE (BASE_MASTER_MODE | ICW4_AEOI) -#else -#define MASTER_MODE BASE_MASTER_MODE -#endif -#ifdef AUTO_EOI_2 -#define SLAVE_MODE (BASE_SLAVE_MODE | ICW4_AEOI) -#else -#define SLAVE_MODE BASE_SLAVE_MODE -#endif - -#define IRQ_MASK(irq) (1 << (irq)) -#define IMEN_MASK(ai) (IRQ_MASK((ai)->at_irq)) - -#define NUM_ISA_IRQS 16 - -static void atpic_init(void *dummy); - -unsigned int imen; /* XXX */ - -inthand_t - IDTVEC(atpic_intr0), IDTVEC(atpic_intr1), IDTVEC(atpic_intr2), - IDTVEC(atpic_intr3), IDTVEC(atpic_intr4), IDTVEC(atpic_intr5), - IDTVEC(atpic_intr6), IDTVEC(atpic_intr7), IDTVEC(atpic_intr8), - IDTVEC(atpic_intr9), IDTVEC(atpic_intr10), IDTVEC(atpic_intr11), - IDTVEC(atpic_intr12), IDTVEC(atpic_intr13), IDTVEC(atpic_intr14), - IDTVEC(atpic_intr15); - -#define IRQ(ap, ai) ((ap)->at_irqbase + (ai)->at_irq) - -#define ATPIC(io, base, eoi, imenptr) \ - { { atpic_enable_source, atpic_disable_source, (eoi), \ - atpic_enable_intr, atpic_disable_intr, atpic_vector, \ - atpic_source_pending, NULL, atpic_resume, atpic_config_intr,\ - atpic_assign_cpu }, (io), (base), IDT_IO_INTS + (base), \ - (imenptr) } - -#define INTSRC(irq) \ - { { &atpics[(irq) / 8].at_pic }, IDTVEC(atpic_intr ## irq ), \ - (irq) % 8 } - -struct atpic { - struct pic at_pic; - int at_ioaddr; - int at_irqbase; - uint8_t at_intbase; - uint8_t *at_imen; -}; - -struct atpic_intsrc { - struct intsrc at_intsrc; - inthand_t *at_intr; - int at_irq; /* Relative to PIC base. */ - enum intr_trigger at_trigger; - u_long at_count; - u_long at_straycount; -}; - -static void atpic_enable_source(struct intsrc *isrc); -static void atpic_disable_source(struct intsrc *isrc, int eoi); -static void atpic_eoi_master(struct intsrc *isrc); -static void atpic_eoi_slave(struct intsrc *isrc); -static void atpic_enable_intr(struct intsrc *isrc); -static void atpic_disable_intr(struct intsrc *isrc); -static int atpic_vector(struct intsrc *isrc); -static void atpic_resume(struct pic *pic); -static int atpic_source_pending(struct intsrc *isrc); -static int atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig, - enum intr_polarity pol); -static int atpic_assign_cpu(struct intsrc *isrc, u_int apic_id); -static void i8259_init(struct atpic *pic, int slave); - -static struct atpic atpics[] = { - ATPIC(IO_ICU1, 0, atpic_eoi_master, (uint8_t *)&imen), - ATPIC(IO_ICU2, 8, atpic_eoi_slave, ((uint8_t *)&imen) + 1) -}; - -static struct atpic_intsrc atintrs[] = { - INTSRC(0), - INTSRC(1), - INTSRC(2), - INTSRC(3), - INTSRC(4), - INTSRC(5), - INTSRC(6), - INTSRC(7), - INTSRC(8), - INTSRC(9), - INTSRC(10), - INTSRC(11), - INTSRC(12), - INTSRC(13), - INTSRC(14), - INTSRC(15), -}; - -CTASSERT(sizeof(atintrs) / sizeof(atintrs[0]) == NUM_ISA_IRQS); - -static __inline void -_atpic_eoi_master(struct intsrc *isrc) -{ - - KASSERT(isrc->is_pic == &atpics[MASTER].at_pic, - ("%s: mismatched pic", __func__)); -#ifndef AUTO_EOI_1 - outb(atpics[MASTER].at_ioaddr, OCW2_EOI); -#endif -} - -/* - * The data sheet says no auto-EOI on slave, but it sometimes works. - * So, if AUTO_EOI_2 is enabled, we use it. - */ -static __inline void -_atpic_eoi_slave(struct intsrc *isrc) -{ - - KASSERT(isrc->is_pic == &atpics[SLAVE].at_pic, - ("%s: mismatched pic", __func__)); -#ifndef AUTO_EOI_2 - outb(atpics[SLAVE].at_ioaddr, OCW2_EOI); -#ifndef AUTO_EOI_1 - outb(atpics[MASTER].at_ioaddr, OCW2_EOI); -#endif -#endif -} - -static void -atpic_enable_source(struct intsrc *isrc) -{ - struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; - struct atpic *ap = (struct atpic *)isrc->is_pic; - - spinlock_enter(); - if (*ap->at_imen & IMEN_MASK(ai)) { - *ap->at_imen &= ~IMEN_MASK(ai); - outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); - } - spinlock_exit(); -} - -static void -atpic_disable_source(struct intsrc *isrc, int eoi) -{ - struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; - struct atpic *ap = (struct atpic *)isrc->is_pic; - - spinlock_enter(); - if (ai->at_trigger != INTR_TRIGGER_EDGE) { - *ap->at_imen |= IMEN_MASK(ai); - outb(ap->at_ioaddr + ICU_IMR_OFFSET, *ap->at_imen); - } - - /* - * Take care to call these functions directly instead of through - * a function pointer. All of the referenced variables should - * still be hot in the cache. - */ - if (eoi == PIC_EOI) { - if (isrc->is_pic == &atpics[MASTER].at_pic) - _atpic_eoi_master(isrc); - else - _atpic_eoi_slave(isrc); - } - - spinlock_exit(); -} - -static void -atpic_eoi_master(struct intsrc *isrc) -{ -#ifndef AUTO_EOI_1 - spinlock_enter(); - _atpic_eoi_master(isrc); - spinlock_exit(); -#endif -} - -static void -atpic_eoi_slave(struct intsrc *isrc) -{ -#ifndef AUTO_EOI_2 - spinlock_enter(); - _atpic_eoi_slave(isrc); - spinlock_exit(); -#endif -} - -static void -atpic_enable_intr(struct intsrc *isrc) -{ -} - -static void -atpic_disable_intr(struct intsrc *isrc) -{ -} - - -static int -atpic_vector(struct intsrc *isrc) -{ - struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; - struct atpic *ap = (struct atpic *)isrc->is_pic; - - return (IRQ(ap, ai)); -} - -static int -atpic_source_pending(struct intsrc *isrc) -{ - struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; - struct atpic *ap = (struct atpic *)isrc->is_pic; - - return (inb(ap->at_ioaddr) & IMEN_MASK(ai)); -} - -static void -atpic_resume(struct pic *pic) -{ - struct atpic *ap = (struct atpic *)pic; - - i8259_init(ap, ap == &atpics[SLAVE]); -#ifndef PC98 - if (ap == &atpics[SLAVE] && elcr_found) - elcr_resume(); -#endif -} - -static int -atpic_config_intr(struct intsrc *isrc, enum intr_trigger trig, - enum intr_polarity pol) -{ - struct atpic_intsrc *ai = (struct atpic_intsrc *)isrc; - u_int vector; - - /* Map conforming values to edge/hi and sanity check the values. */ - if (trig == INTR_TRIGGER_CONFORM) - trig = INTR_TRIGGER_EDGE; - if (pol == INTR_POLARITY_CONFORM) - pol = INTR_POLARITY_HIGH; - vector = atpic_vector(isrc); - if ((trig == INTR_TRIGGER_EDGE && pol == INTR_POLARITY_LOW) || - (trig == INTR_TRIGGER_LEVEL && pol == INTR_POLARITY_HIGH)) { - printf( - "atpic: Mismatched config for IRQ%u: trigger %s, polarity %s\n", - vector, trig == INTR_TRIGGER_EDGE ? "edge" : "level", - pol == INTR_POLARITY_HIGH ? "high" : "low"); - return (EINVAL); - } - - /* If there is no change, just return. */ - if (ai->at_trigger == trig) - return (0); - -#ifdef PC98 - if ((vector == 0 || vector == 1 || vector == 7 || vector == 8) && - trig == INTR_TRIGGER_LEVEL) { - if (bootverbose) - printf( - "atpic: Ignoring invalid level/low configuration for IRQ%u\n", - vector); - return (EINVAL); - } - return (ENXIO); -#else - /* - * Certain IRQs can never be level/lo, so don't try to set them - * that way if asked. At least some ELCR registers ignore setting - * these bits as well. - */ - if ((vector == 0 || vector == 1 || vector == 2 || vector == 13) && - trig == INTR_TRIGGER_LEVEL) { - if (bootverbose) - printf( - "atpic: Ignoring invalid level/low configuration for IRQ%u\n", - vector); - return (EINVAL); - } - if (!elcr_found) { - if (bootverbose) - printf("atpic: No ELCR to configure IRQ%u as %s\n", - vector, trig == INTR_TRIGGER_EDGE ? "edge/high" : - "level/low"); - return (ENXIO); - } - if (bootverbose) - printf("atpic: Programming IRQ%u as %s\n", vector, - trig == INTR_TRIGGER_EDGE ? "edge/high" : "level/low"); - spinlock_enter(); - elcr_write_trigger(atpic_vector(isrc), trig); - ai->at_trigger = trig; - spinlock_exit(); - return (0); -#endif /* PC98 */ -} - -static int -atpic_assign_cpu(struct intsrc *isrc, u_int apic_id) -{ - - /* - * 8259A's are only used in UP in which case all interrupts always - * go to the sole CPU and this function shouldn't even be called. - */ - panic("%s: bad cookie", __func__); -} - -static void -i8259_init(struct atpic *pic, int slave) -{ - int imr_addr; - - /* Reset the PIC and program with next four bytes. */ - spinlock_enter(); -#ifdef DEV_MCA - /* MCA uses level triggered interrupts. */ - if (MCA_system) - outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4 | ICW1_LTIM); - else -#endif - outb(pic->at_ioaddr, ICW1_RESET | ICW1_IC4); - imr_addr = pic->at_ioaddr + ICU_IMR_OFFSET; - - /* Start vector. */ - outb(imr_addr, pic->at_intbase); - - /* - * Setup slave links. For the master pic, indicate what line - * the slave is configured on. For the slave indicate - * which line on the master we are connected to. - */ - if (slave) - outb(imr_addr, ICU_SLAVEID); - else - outb(imr_addr, IRQ_MASK(ICU_SLAVEID)); - - /* Set mode. */ - if (slave) - outb(imr_addr, SLAVE_MODE); - else - outb(imr_addr, MASTER_MODE); - - /* Set interrupt enable mask. */ - outb(imr_addr, *pic->at_imen); - - /* Reset is finished, default to IRR on read. */ - outb(pic->at_ioaddr, OCW3_SEL | OCW3_RR); - -#ifndef PC98 - /* OCW2_L1 sets priority order to 3-7, 0-2 (com2 first). */ - if (!slave) - outb(pic->at_ioaddr, OCW2_R | OCW2_SL | OCW2_L1); -#endif - spinlock_exit(); -} - -void -atpic_startup(void) -{ - struct atpic_intsrc *ai; - int i; - - /* Start off with all interrupts disabled. */ - imen = 0xffff; - i8259_init(&atpics[MASTER], 0); - i8259_init(&atpics[SLAVE], 1); - atpic_enable_source((struct intsrc *)&atintrs[ICU_SLAVEID]); - - /* Install low-level interrupt handlers for all of our IRQs. */ - for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) { - if (i == ICU_SLAVEID) - continue; - ai->at_intsrc.is_count = &ai->at_count; - ai->at_intsrc.is_straycount = &ai->at_straycount; - setidt(((struct atpic *)ai->at_intsrc.is_pic)->at_intbase + - ai->at_irq, ai->at_intr, SDT_SYS386IGT, SEL_KPL, - GSEL(GCODE_SEL, SEL_KPL)); - } - -#ifdef DEV_MCA - /* For MCA systems, all interrupts are level triggered. */ - if (MCA_system) - for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) - ai->at_trigger = INTR_TRIGGER_LEVEL; - else -#endif - -#ifdef PC98 - for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) - switch (i) { - case 0: - case 1: - case 7: - case 8: - ai->at_trigger = INTR_TRIGGER_EDGE; - break; - default: - ai->at_trigger = INTR_TRIGGER_LEVEL; - break; - } -#else - /* - * Look for an ELCR. If we find one, update the trigger modes. - * If we don't find one, assume that IRQs 0, 1, 2, and 13 are - * edge triggered and that everything else is level triggered. - * We only use the trigger information to reprogram the ELCR if - * we have one and as an optimization to avoid masking edge - * triggered interrupts. For the case that we don't have an ELCR, - * it doesn't hurt to mask an edge triggered interrupt, so we - * assume level trigger for any interrupt that we aren't sure is - * edge triggered. - */ - if (elcr_found) { - for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) - ai->at_trigger = elcr_read_trigger(i); - } else { - for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) - switch (i) { - case 0: - case 1: - case 2: - case 8: - case 13: - ai->at_trigger = INTR_TRIGGER_EDGE; - break; - default: - ai->at_trigger = INTR_TRIGGER_LEVEL; - break; - } - } -#endif /* PC98 */ -} - -static void -atpic_init(void *dummy __unused) -{ - struct atpic_intsrc *ai; - int i; - - /* - * Register our PICs, even if we aren't going to use any of their - * pins so that they are suspended and resumed. - */ - if (intr_register_pic(&atpics[0].at_pic) != 0 || - intr_register_pic(&atpics[1].at_pic) != 0) - panic("Unable to register ATPICs"); - - /* - * If any of the ISA IRQs have an interrupt source already, then - * assume that the APICs are being used and don't register any - * of our interrupt sources. This makes sure we don't accidentally - * use mixed mode. The "accidental" use could otherwise occur on - * machines that route the ACPI SCI interrupt to a different ISA - * IRQ (at least one machines routes it to IRQ 13) thus disabling - * that APIC ISA routing and allowing the ATPIC source for that IRQ - * to leak through. We used to depend on this feature for routing - * IRQ0 via mixed mode, but now we don't use mixed mode at all. - */ - for (i = 0; i < NUM_ISA_IRQS; i++) - if (intr_lookup_source(i) != NULL) - return; - - /* Loop through all interrupt sources and add them. */ - for (i = 0, ai = atintrs; i < NUM_ISA_IRQS; i++, ai++) { - if (i == ICU_SLAVEID) - continue; - intr_register_source(&ai->at_intsrc); - } -} -SYSINIT(atpic_init, SI_SUB_INTR, SI_ORDER_SECOND + 1, atpic_init, NULL); - -void -atpic_handle_intr(u_int vector, struct trapframe *frame) -{ - struct intsrc *isrc; - - KASSERT(vector < NUM_ISA_IRQS, ("unknown int %u\n", vector)); - isrc = &atintrs[vector].at_intsrc; - - /* - * If we don't have an event, see if this is a spurious - * interrupt. - */ - if (isrc->is_event == NULL && (vector == 7 || vector == 15)) { - int port, isr; - - /* - * Read the ISR register to see if IRQ 7/15 is really - * pending. Reset read register back to IRR when done. - */ - port = ((struct atpic *)isrc->is_pic)->at_ioaddr; - spinlock_enter(); - outb(port, OCW3_SEL | OCW3_RR | OCW3_RIS); - isr = inb(port); - outb(port, OCW3_SEL | OCW3_RR); - spinlock_exit(); - if ((isr & IRQ_MASK(7)) == 0) - return; - } - intr_execute_handlers(isrc, frame); -} - -#ifdef DEV_ISA -/* - * Bus attachment for the ISA PIC. - */ -static struct isa_pnp_id atpic_ids[] = { - { 0x0000d041 /* PNP0000 */, "AT interrupt controller" }, - { 0 } -}; - -static int -atpic_probe(device_t dev) -{ - int result; - - result = ISA_PNP_PROBE(device_get_parent(dev), dev, atpic_ids); - if (result <= 0) - device_quiet(dev); - return (result); -} - -/* - * We might be granted IRQ 2, as this is typically consumed by chaining - * between the two PIC components. If we're using the APIC, however, - * this may not be the case, and as such we should free the resource. - * (XXX untested) - * - * The generic ISA attachment code will handle allocating any other resources - * that we don't explicitly claim here. - */ -static int -atpic_attach(device_t dev) -{ - struct resource *res; - int rid; - - /* Try to allocate our IRQ and then free it. */ - rid = 0; - res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 0); - if (res != NULL) - bus_release_resource(dev, SYS_RES_IRQ, rid, res); - return (0); -} - -static device_method_t atpic_methods[] = { - /* Device interface */ - DEVMETHOD(device_probe, atpic_probe), - DEVMETHOD(device_attach, atpic_attach), - DEVMETHOD(device_detach, bus_generic_detach), - DEVMETHOD(device_shutdown, bus_generic_shutdown), - DEVMETHOD(device_suspend, bus_generic_suspend), - DEVMETHOD(device_resume, bus_generic_resume), - { 0, 0 } -}; - -static driver_t atpic_driver = { - "atpic", - atpic_methods, - 1, /* no softc */ -}; - -static devclass_t atpic_devclass; - -DRIVER_MODULE(atpic, isa, atpic_driver, atpic_devclass, 0, 0); -#ifndef PC98 -DRIVER_MODULE(atpic, acpi, atpic_driver, atpic_devclass, 0, 0); -#endif - -/* - * Return a bitmap of the current interrupt requests. This is 8259-specific - * and is only suitable for use at probe time. - */ -intrmask_t -isa_irq_pending(void) -{ - u_char irr1; - u_char irr2; - - irr1 = inb(IO_ICU1); - irr2 = inb(IO_ICU2); - return ((irr2 << 8) | irr1); -} -#endif /* DEV_ISA */ |