summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa/atpic.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/i386/isa/atpic.c')
-rw-r--r--sys/i386/isa/atpic.c679
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 */
OpenPOWER on IntegriCloud