diff options
Diffstat (limited to 'sys/arch/mvmeppc/dev/openpic.c')
-rw-r--r-- | sys/arch/mvmeppc/dev/openpic.c | 1035 |
1 files changed, 1035 insertions, 0 deletions
diff --git a/sys/arch/mvmeppc/dev/openpic.c b/sys/arch/mvmeppc/dev/openpic.c new file mode 100644 index 00000000000..f4e2a391e2c --- /dev/null +++ b/sys/arch/mvmeppc/dev/openpic.c @@ -0,0 +1,1035 @@ +/* $OpenBSD: openpic.c,v 1.1 2001/06/26 21:57:41 smurph Exp $ */ + +/*- + * Copyright (c) 1995 Per Fogelstrom + * Copyright (c) 1993, 1994 Charles M. Hannum. + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * William Jolitz and Don Ahn. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + * + * @(#)isa.c 7.2 (Berkeley) 5/12/91 + */ + +#if 0 +#define OP_DEBUG +#endif + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/ioctl.h> +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/systm.h> +#ifdef UVM +#include <vm/vm.h> +#include <vm/vm_kern.h> +#include <uvm/uvm.h> +#endif + +#include <machine/autoconf.h> +#include <machine/intr.h> +#include <machine/psl.h> +#include <machine/pio.h> +#include <mvmeppc/dev/openpicreg.h> +#include <mvmeppc/dev/ravenvar.h> +#include <mvmeppc/dev/ravenreg.h> + +#define ICU_LEN 32 +#define LEGAL_IRQ(x) ((x >= 0) && (x < ICU_LEN)) +#define IO_ICU1 (RAVEN_P_ISA_IO_SPACE + 0x20) +#define IO_ICU2 (RAVEN_P_ISA_IO_SPACE + 0xA0) +#define IRQ_SLAVE 2 +#define ICU_OFFSET 16 +unsigned char icu1_val = 0xff; +unsigned char icu2_val = 0xff; + +#define SET_ICUS() (outb(IO_ICU1 + 1, imen), outb(IO_ICU2 + 1, imen >> 8)) + +static int intrtype[ICU_LEN], intrmask[ICU_LEN], intrlevel[ICU_LEN]; +static struct intrhand *intrhand[ICU_LEN] = { 0 }; +static int hwirq[ICU_LEN], virq[ICU_LEN]; +unsigned int imen /* = 0xffffffff */; /* XXX */ +static int virq_max = 0; + +struct evcnt evirq[ICU_LEN]; + +static int fakeintr __P((void *)); +static char *intr_typename(int type); +static void intr_calculatemasks(); +static __inline int cntlzw(int x); +static int mapirq(int irq); +static int read_irq(); +void openpic_enable_irq_mask(int irq_mask); + +static struct raven_reg *ravenp = (struct raven_reg *)NULL; + +#define HWIRQ_MAX 27 +#define HWIRQ_MASK 0x0fffffff + + +static __inline u_int openpic_read __P((int)); +static __inline void openpic_write __P((int, u_int)); +void openpic_enable_irq __P((int, int)); +void openpic_disable_irq __P((int)); +void openpic_init(); +void openpic_set_priority __P((int, int)); +void openpic_set_vec_pri __P((int, int)); +static __inline int openpic_read_irq __P((int)); +static __inline void openpic_eoi __P((int)); + +void i8259_init __P((void)); +int i8259_intr __P((void)); +void i8259_enable_irq __P((int)); +void i8259_disable_irq __P((int)); +void *i8259_intr_establish( void * lcv, int irq, int type, int level, + int (*ih_fun) __P((void *)), void *ih_arg, char *name); + +struct openpic_softc { + struct device sc_dev; +}; + +int openpic_match __P((struct device *parent, void *cf, void *aux)); +void openpic_attach __P((struct device *, struct device *, void *)); +void openpic_do_pending_int(); +void ext_intr_openpic(); + +struct cfattach openpic_ca = { + sizeof(struct openpic_softc), + openpic_match, + openpic_attach +}; + +struct cfdriver openpic_cd = { + NULL, "openpic", DV_DULL +}; + +struct pci_route { + int pci; + int openpic; +} pci_routes[] = { + 10, 2, + 11, 4, + 14, 3, + 15, 5, + 0, 0, +}; + +static int isaintrs = 0; + +int +openpic_match(parent, cf, aux) + struct device *parent; + void *cf; + void *aux; +{ + struct confargs *ca = aux; + + /* We must be a child of the raven device */ + if (strcmp(parent->dv_cfdata->cf_driver->cd_name, "raven") != 0) + return (0); + /* don't attach more than once. */ + if (ravenp != (struct raven_reg *)NULL){ +#ifdef DIAGNOSTIC + printf("openpic: trying to attach more than once!"); +#endif + return(0); + } + /* If there is a raven, then there is a mpic! */ + return 1; +} + +u_int8_t *interrupt_reg; +typedef void (void_f) (void); +extern void_f *pending_int_f; +static int abort_switch (void *arg); +static int i8259_dummy (void *arg); + +typedef int mac_intr_handle_t; + +typedef void *(intr_establish_t) __P((void *, mac_intr_handle_t, + int, int, int (*func)(void *), void *, char *)); +typedef void (intr_disestablish_t) __P((void *, void *)); + +vaddr_t openpic_base; +void * openpic_intr_establish( void * lcv, int irq, int type, int level, + int (*ih_fun) __P((void *)), void *ih_arg, char *name); +void openpic_intr_disestablish( void *lcp, void *arg); +void openpic_collect_preconf_intr(); + +void +openpic_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct confargs *ca = aux; + struct openpic_softc *sc = (void *)self; + extern intr_establish_t *intr_establish_func; + extern intr_disestablish_t *intr_disestablish_func; +#if 0 + extern intr_establish_t *mac_intr_establish_func; + extern intr_disestablish_t *mac_intr_disestablish_func; +#endif + openpic_base = (vaddr_t)MPCIC_REG/* mapiodev (MPCIC_REG, 0x22000)*/; + + printf(": version 0x%x", openpic_read(OPENPIC_FEATURE) & 0xFF); + + i8259_init(); + openpic_init(); + + pending_int_f = openpic_do_pending_int; + intr_establish_func = openpic_intr_establish; + intr_disestablish_func = openpic_intr_disestablish; +#if 0 + mac_intr_establish_func = openpic_intr_establish; + mac_intr_disestablish_func = openpic_intr_disestablish; +#endif + install_extint(ext_intr_openpic); + +#if 1 + openpic_collect_preconf_intr(); +#endif + +#if 1 + + intr_establish_func(parent, 0x00, IST_LEVEL, IPL_HIGH, + i8259_dummy, (void *)0x00, "8259 Interrupt"); + i8259_intr_establish(parent, 0x08, IST_EDGE, IPL_HIGH, + abort_switch, (void *)0x08, "abort button"); +#endif +/* + ppc_intr_enable(1); +*/ + printf("\n"); +} + +void +openpic_collect_preconf_intr() +{ + int i; + for (i = 0; i < ppc_configed_intr_cnt; i++) { +#ifdef DEBUG + printf("\n\t%s irq %d level %d fun %x arg %x", + ppc_configed_intr[i].ih_what, ppc_configed_intr[i].ih_irq, + ppc_configed_intr[i].ih_level, ppc_configed_intr[i].ih_fun, + ppc_configed_intr[i].ih_arg); +#endif + openpic_intr_establish(NULL, ppc_configed_intr[i].ih_irq, + IST_LEVEL, ppc_configed_intr[i].ih_level, + ppc_configed_intr[i].ih_fun, ppc_configed_intr[i].ih_arg, + ppc_configed_intr[i].ih_what); + } +} + +static int +abort_switch (void *arg) +{ +#ifdef DDB + printf("Abort button pressed, entering debugger.\n"); + Debugger(); +#else + printf("Abort button pressed, debugger not available.\n"); +#endif + return 1; +} + +static int +i8259_dummy (void *arg) +{ + return 1; +} + +static int +fakeintr(arg) + void *arg; +{ + + return 0; +} + +/* + * Register an interrupt handler. + */ +void * +i8259_intr_establish(lcv, irq, type, level, ih_fun, ih_arg, name) + void * lcv; + int irq; + int type; + int level; + int (*ih_fun) __P((void *)); + void *ih_arg; + char *name; +{ + struct intrhand **p, *q, *ih; + static struct intrhand fakehand; + extern int cold; + + fakehand.ih_next = NULL; + fakehand.ih_fun = fakeintr; + +#if 0 +printf("i8259_intr_establish, hI %d L %d ", irq, type); +#endif + isaintrs++; + irq = mapirq(irq + ICU_OFFSET); +#if 0 +printf("vI %d ", irq); +#endif + + /* no point in sleeping unless someone can free memory. */ + ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); + if (ih == NULL) + panic("i8259_intr_establish: can't malloc handler info"); + + if (!LEGAL_IRQ(irq) || type == IST_NONE) + panic("i8259_intr_establish: bogus irq or type"); + + switch (intrtype[irq]) { + case IST_NONE: + intrtype[irq] = type; + break; + case IST_EDGE: + case IST_LEVEL: + if (type == intrtype[irq]) + break; + case IST_PULSE: + if (type != IST_NONE) + panic("intr_establish: can't share %s with %s", + intr_typename(intrtype[irq]), + intr_typename(type)); + break; + } + + /* + * Figure out where to put the handler. + * This is O(N^2), but we want to preserve the order, and N is + * generally small. + */ + for (p = &intrhand[irq]; (q = *p) != NULL; p = &q->ih_next) + ; + + /* + * Actually install a fake handler momentarily, since we might be doing + * this with interrupts enabled and DON'T WANt the real routine called + * until masking is set up. + */ + fakehand.ih_level = level; + *p = &fakehand; + + intr_calculatemasks(); + + /* + * Poke the real handler in now. + */ + ih->ih_fun = ih_fun; + ih->ih_arg = ih_arg; + ih->ih_count = 0; + ih->ih_next = NULL; + ih->ih_level = level; + ih->ih_irq = irq; + *p = ih; + + return (ih); +} + + +/* + * Register an interrupt handler. + */ +void * +openpic_intr_establish(lcv, irq, type, level, ih_fun, ih_arg, name) + void * lcv; + int irq; + int type; + int level; + int (*ih_fun) __P((void *)); + void *ih_arg; + char *name; +{ + struct intrhand **p, *q, *ih; + static struct intrhand fakehand; + struct pci_route *pr; + extern int cold; + + fakehand.ih_next = NULL; + fakehand.ih_fun = fakeintr; + +#if 0 +printf("mac_intr_establish, hI %d L %d ", irq, type); +#endif + + pr = pci_routes; + while (pr->pci !=0) { + irq = (pr->pci == irq) ? pr->openpic : irq; + pr++; + } + + irq = mapirq(irq); +#if 0 +printf("vI %d ", irq); +#endif + + /* no point in sleeping unless someone can free memory. */ + ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); + if (ih == NULL) + panic("intr_establish: can't malloc handler info"); + + if (!LEGAL_IRQ(irq) || type == IST_NONE) + panic("intr_establish: bogus irq or type"); + + switch (intrtype[irq]) { + case IST_NONE: + intrtype[irq] = type; + break; + case IST_EDGE: + case IST_LEVEL: + if (type == intrtype[irq]) + break; + case IST_PULSE: + if (type != IST_NONE) + panic("intr_establish: can't share %s with %s", + intr_typename(intrtype[irq]), + intr_typename(type)); + break; + } + + /* + * Figure out where to put the handler. + * This is O(N^2), but we want to preserve the order, and N is + * generally small. + */ + for (p = &intrhand[irq]; (q = *p) != NULL; p = &q->ih_next) + ; + + /* + * Actually install a fake handler momentarily, since we might be doing + * this with interrupts enabled and DON'T WANt the real routine called + * until masking is set up. + */ + fakehand.ih_level = level; + *p = &fakehand; + + intr_calculatemasks(); + + /* + * Poke the real handler in now. + */ + ih->ih_fun = ih_fun; + ih->ih_arg = ih_arg; + ih->ih_count = 0; + ih->ih_next = NULL; + ih->ih_level = level; + ih->ih_irq = irq; + *p = ih; + + return (ih); +} + +/* + * Deregister an interrupt handler. + */ +void +openpic_intr_disestablish(lcp, arg) + void *lcp; + void *arg; +{ + struct intrhand *ih = arg; + int irq = ih->ih_irq; + struct intrhand **p, *q; + + if (!LEGAL_IRQ(irq)) + panic("intr_disestablish: bogus irq"); + + /* + * Remove the handler from the chain. + * This is O(n^2), too. + */ + for (p = &intrhand[irq]; (q = *p) != NULL && q != ih; p = &q->ih_next) + ; + if (q) + *p = q->ih_next; + else + panic("intr_disestablish: handler not registered"); + free((void *)ih, M_DEVBUF); + + intr_calculatemasks(); + + if (intrhand[irq] == NULL) + intrtype[irq] = IST_NONE; +} + + +static char * +intr_typename(type) + int type; +{ + + switch (type) { + case IST_NONE : + return ("none"); + case IST_PULSE: + return ("pulsed"); + case IST_EDGE: + return ("edge-triggered"); + case IST_LEVEL: + return ("level-triggered"); + default: + panic("intr_typename: invalid type %d", type); +#if 1 /* XXX */ + return ("unknown"); +#endif + } +} + +/* + * Recalculate the interrupt masks from scratch. + * We could code special registry and deregistry versions of this function that + * would be faster, but the code would be nastier, and we don't expect this to + * happen very much anyway. + */ +static void +intr_calculatemasks() +{ + int irq, level; + struct intrhand *q; +#ifdef OP_DEBUG + printf("intr_calculatemasks() "); +#endif + /* First, figure out which levels each IRQ uses. */ + for (irq = 0; irq < ICU_LEN; irq++) { + register int levels = 0; + for (q = intrhand[irq]; q; q = q->ih_next) + levels |= 1 << q->ih_level; + intrlevel[irq] = levels; + } + + /* Then figure out which IRQs use each level. */ + for (level = 0; level < 5; level++) { + register int irqs = 0; + for (irq = 0; irq < ICU_LEN; irq++) + if (intrlevel[irq] & (1 << level)) + irqs |= 1 << irq; + imask[level] = irqs | SINT_MASK; + } + + /* + * There are tty, network and disk drivers that use free() at interrupt + * time, so imp > (tty | net | bio). + */ + imask[IPL_IMP] |= imask[IPL_TTY] | imask[IPL_NET] | imask[IPL_BIO]; + + /* + * Enforce a hierarchy that gives slow devices a better chance at not + * dropping data. + */ + imask[IPL_TTY] |= imask[IPL_NET] | imask[IPL_BIO]; + imask[IPL_NET] |= imask[IPL_BIO]; + + /* + * These are pseudo-levels. + */ + imask[IPL_NONE] = 0x00000000; + imask[IPL_HIGH] = 0xffffffff; + + /* And eventually calculate the complete masks. */ + for (irq = 0; irq < ICU_LEN; irq++) { + register int irqs = 1 << irq; + for (q = intrhand[irq]; q; q = q->ih_next) + irqs |= imask[q->ih_level]; + intrmask[irq] = irqs | SINT_MASK; + } + + /* Lastly, determine which IRQs are actually in use. */ + { + register int irqs = 0; + for (irq = 0; irq < ICU_LEN; irq++) { + if (intrhand[irq]) { + irqs |= 1 << irq; + if (hwirq[irq] >= ICU_OFFSET) + i8259_enable_irq(hwirq[irq]); + else + openpic_enable_irq(hwirq[irq], intrtype[irq]); + } else { + if (hwirq[irq] >= ICU_OFFSET) + i8259_disable_irq(hwirq[irq]); + else + openpic_disable_irq(hwirq[irq]); + } + } + } +#if 0 + i8259_enable_irq(2); +#endif +} +/* + * Map 64 irqs into 32 (bits). + */ +static int +mapirq(irq) + int irq; +{ + int v; + + if (irq < 0 || irq >= ICU_LEN) + panic("invalid irq"); + virq_max++; + v = virq_max; + if (v > HWIRQ_MAX) + panic("virq overflow"); + + hwirq[v] = irq; + virq[irq] = v; +#if 0 +printf("\nmapirq %x to %x\n", irq, v); +#endif + + return v; +} + +/* + * Count leading zeros. + */ +static __inline int +cntlzw(x) + int x; +{ + int a; + + __asm __volatile ("cntlzw %0,%1" : "=r"(a) : "r"(x)); + + return a; +} + + +void +openpic_do_pending_int() +{ + struct intrhand *ih; + int irq; + int pcpl; + int hwpend; + int emsr, dmsr; + static int processing; + + if (processing) + return; + +#ifdef OP_DEBUG + printf("openpic_do_pending_int()\n"); +#endif + processing = 1; + pcpl = splhigh(); /* Turn off all */ + asm volatile("mfmsr %0" : "=r"(emsr)); + dmsr = emsr & ~PSL_EE; + asm volatile("mtmsr %0" :: "r"(dmsr)); + + hwpend = ipending & ~pcpl; /* Do now unmasked pendings */ + imen &= ~hwpend; + openpic_enable_irq_mask(~imen); + + hwpend &= HWIRQ_MASK; + while (hwpend) { + irq = 31 - cntlzw(hwpend); + hwpend &= ~(1L << irq); + ih = intrhand[irq]; + while(ih) { + (*ih->ih_fun)(ih->ih_arg); + ih = ih->ih_next; + } + + evirq[hwirq[irq]].ev_count++; + } + + /*out32rb(INT_ENABLE_REG, ~imen);*/ + + do { + if((ipending & SINT_CLOCK) & ~pcpl) { + ipending &= ~SINT_CLOCK; + softclock(); + } + if((ipending & SINT_NET) & ~pcpl) { + extern int netisr; + int pisr = netisr; + netisr = 0; + ipending &= ~SINT_NET; + softnet(pisr); + } + } while (ipending & (SINT_NET|SINT_CLOCK) & ~cpl); + ipending &= pcpl; + cpl = pcpl; /* Don't use splx... we are here already! */ + asm volatile("mtmsr %0" :: "r"(emsr)); + processing = 0; +} + +u_int +openpic_read(reg) + int reg; +{ + char *addr = (void *)(openpic_base + reg); + + return in32rb(addr); +} + +void +openpic_write(reg, val) + int reg; + u_int val; +{ + char *addr = (void *)(openpic_base + reg); + + out32rb(addr, val); +} + +void +openpic_enable_irq_mask(irq_mask) +int irq_mask; +{ + int irq; +#ifdef OP_DEBUG + printf("openpic_enable_irq_mask()\n"); +#endif + for ( irq = 0; irq <= virq_max; irq++) { + if (irq_mask & (1 << irq)) { + if (hwirq[irq] >= ICU_OFFSET) + i8259_enable_irq(hwirq[irq]); + else + openpic_enable_irq(hwirq[irq], intrtype[irq]); + } else { + if (hwirq[irq] >= ICU_OFFSET) + i8259_disable_irq(hwirq[irq]); + else + openpic_disable_irq(hwirq[irq]); + } + } +} + +void +openpic_enable_irq(irq, type) + int irq; + int type; +{ + u_int x; + /* skip invalid irqs */ + if (irq == -1) + return; + + x = openpic_read(OPENPIC_SRC_VECTOR(irq)); + x &= ~(OPENPIC_IMASK|OPENPIC_SENSE_LEVEL|OPENPIC_SENSE_EDGE); + if (type == IST_LEVEL) { + x |= OPENPIC_SENSE_LEVEL; + } else { + x |= OPENPIC_SENSE_EDGE; + } +#ifdef OP_DEBUG + printf("enabeling irq %d, type %d, val 0x%08x\n", irq, type, x); +#endif + + openpic_write(OPENPIC_SRC_VECTOR(irq), x); +} + +void +openpic_disable_irq(irq) + int irq; +{ + u_int x; + /* skip invalid irqs */ + if (irq == -1) + return; + + x = openpic_read(OPENPIC_SRC_VECTOR(irq)); + x |= OPENPIC_IMASK; +#ifdef OP_DEBUG + printf("disabeling irq %d, val 0x%08x\n", irq, x); +#endif + + openpic_write(OPENPIC_SRC_VECTOR(irq), x); +} + +void +i8259_set_irq_mask(void) +{ + if (icu2_val != 0xFF) { + /* Turn on the second IC */ + icu1_val &= ~(1 << 2); + } else { + icu1_val |= (1 << 2); + } + + outb(IO_ICU1 + 1, icu1_val); + outb(IO_ICU2 + 1, icu2_val); +} + +void +i8259_disable_irq(irq) +int irq; +{ + if (irq >= ICU_OFFSET) + irq -= ICU_OFFSET; + if (irq < 8) + icu1_val |= 1 << irq; + else + icu2_val |= 1 << (irq - 8); + i8259_set_irq_mask(); +#ifdef OP_DEBUG + printf("disabeling isa irq %d\n", irq); +#endif +} + +void +i8259_enable_irq(irq) +int irq; +{ + + if (irq >= ICU_OFFSET) + irq -= ICU_OFFSET; + if ( irq < 8 ) + icu1_val &= ~(1 << irq); + else + icu2_val &= ~(1 << (irq - 8)); + i8259_set_irq_mask(); +#ifdef OP_DEBUG + printf("enabeling isa irq %d\n", irq); +#endif + +} + +void +openpic_set_priority(cpu, pri) + int cpu, pri; +{ + u_int x; + + x = openpic_read(OPENPIC_CPU_PRIORITY(cpu)); + x &= ~OPENPIC_CPU_PRIORITY_MASK; + x |= pri; + openpic_write(OPENPIC_CPU_PRIORITY(cpu), x); +} + +int +openpic_read_irq(cpu) + int cpu; +{ + return openpic_read(OPENPIC_IACK(cpu)) & OPENPIC_VECTOR_MASK; +} + +void +openpic_eoi(cpu) + int cpu; +{ + openpic_write(OPENPIC_EOI(cpu), 0); + openpic_read(OPENPIC_EOI(cpu)); +} + +void i8259_init(void) +{ + /* initialize 8259's */ + outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ + outb(IO_ICU1+1, ICU_OFFSET); /* starting at this vector index */ + outb(IO_ICU1+1, 1 << IRQ_SLAVE); /* slave on line 2 */ + outb(IO_ICU1+1, 1); /* 8086 mode */ + outb(IO_ICU1+1, 0xff); /* leave interrupts masked */ + /* init interrupt controller 2 */ + outb(IO_ICU2, 0x11); /* reset; program device, four bytes */ + outb(IO_ICU2+1, ICU_OFFSET+8); /* staring at this vector index */ + outb(IO_ICU2+1, IRQ_SLAVE); + outb(IO_ICU2+1, 1); /* 8086 mode */ + outb(IO_ICU2+1, 0xff); /* leave interrupts masked */ +} + +int i8259_intr(void) +{ + int irq; + + /* + * Perform an interrupt acknowledge cycle on controller 1 + */ + outb(IO_ICU1, 0x0C); + irq = inb(IO_ICU1) & 7; +#ifdef OP_DEBUG + printf("isa intr = %d\n", irq); +#endif + if (irq == 2) + { + /* + * Interrupt is cascaded so perform interrupt + * acknowledge on controller 2 + */ + outb(IO_ICU2, 0x0C); + irq = (inb(IO_ICU2) & 7) + 8; + } + else if (irq==7) + { + /* + * This may be a spurious interrupt + * + * Read the interrupt status register. If the most + * significant bit is not set then there is no valid + * interrupt + */ + outb(IO_ICU1, 0x0B); + if(~inb(IO_ICU1)&0x80) + return 0xFF; + } + return (ICU_OFFSET + irq); +} + +void +ext_intr_openpic() +{ + int irq, realirq; + int r_imen; + int pcpl; + struct intrhand *ih; + +#ifdef OP_DEBUG + printf("Interrupt!\n"); +#endif + pcpl = splhigh(); /* Turn off all */ + + realirq = openpic_read_irq(0); +#ifdef OP_DEBUG + printf("irq %d\n", realirq); +#endif + + while ((realirq != 0xFF) && (realirq != 0xAA)) { + if (realirq == 0x00){ + realirq = i8259_intr(); +#ifdef OP_DEBUG + printf("irq2 %d\n", realirq); +#endif + openpic_eoi(0); + if (realirq == 0xFF) + continue; + } + + irq = virq[realirq]; + intrcnt[realirq]++; + + /* XXX check range */ + + r_imen = 1 << irq; + + if ((pcpl & r_imen) != 0) { + ipending |= r_imen; /* Masked! Mark this as pending */ + if (realirq >= ICU_OFFSET) + i8259_disable_irq(realirq); + else + openpic_disable_irq(realirq); + } else { + ih = intrhand[irq]; + while (ih) { + (*ih->ih_fun)(ih->ih_arg); + ih = ih->ih_next; + } +#ifdef UVM + uvmexp.intrs++; +#else +#endif + evirq[realirq].ev_count++; + } + + openpic_eoi(0); + + realirq = openpic_read_irq(0); + } + + splx(pcpl); /* Process pendings. */ +} + +void openpic_set_vec_pri(int irq, int pri) +{ + u_int x; + x = openpic_read(OPENPIC_SRC_VECTOR(irq)); + x &= ~OPENPIC_PRIORITY_MASK; + x |= pri << OPENPIC_PRIORITY_SHIFT; + openpic_write(OPENPIC_SRC_VECTOR(irq), x); +} + +void openpic_initirq(int irq, int pri, int vec, int pol, int sense) +{ + u_int x; + x = (vec & OPENPIC_VECTOR_MASK); + x |= OPENPIC_IMASK; + x |= (pol ? OPENPIC_POLARITY_POSITIVE : OPENPIC_POLARITY_NEGATIVE); + x |= (sense ? OPENPIC_SENSE_LEVEL : OPENPIC_SENSE_EDGE); + x |= pri << OPENPIC_PRIORITY_SHIFT; + openpic_write(OPENPIC_SRC_VECTOR(irq), x); +} + +void +openpic_init() +{ + int irq; + u_int x; + + /* disable all interrupts and init hwirq[] */ + for (irq = 0; irq < ICU_LEN; irq++){ + hwirq[irq] = -1; + openpic_write(OPENPIC_SRC_VECTOR(irq), OPENPIC_IMASK); + } + openpic_set_priority(0, 15); + + /* we don't need 8259 pass through mode */ + x = openpic_read(OPENPIC_CONFIG); + x |= OPENPIC_CONFIG_8259_PASSTHRU_DISABLE; + openpic_write(OPENPIC_CONFIG, x); + + /* send all interrupts to cpu 0 */ + for (irq = 0; irq < ICU_LEN; irq++) + openpic_write(OPENPIC_SRC_DEST(irq), CPU(0)); + + /* special case for intr src 0 */ + openpic_initirq(0, 8, 0, 1, 0); + + for (irq = 1; irq < ICU_LEN; irq++) { + openpic_initirq(irq, 8, irq, 0, 1); + } + + /* XXX set spurious intr vector */ + openpic_write(OPENPIC_SPURIOUS_VECTOR, 0xFF); + + /* unmask interrupts for cpu 0 */ + openpic_set_priority(0, 0); + + /* clear all pending interrunts */ + for (irq = 0; irq < ICU_OFFSET; irq++) { + openpic_read_irq(0); + openpic_eoi(0); + } + + for (irq = 0; irq < ICU_OFFSET; irq++){ + i8259_disable_irq(irq); + openpic_disable_irq(irq); + } + + install_extint(ext_intr_openpic); +} |