summaryrefslogtreecommitdiff
path: root/sys/arch/alpha/pci/sio_pic.c
diff options
context:
space:
mode:
authorTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
committerTheo de Raadt <deraadt@cvs.openbsd.org>1995-10-18 08:53:40 +0000
commitd6583bb2a13f329cf0332ef2570eb8bb8fc0e39c (patch)
treeece253b876159b39c620e62b6c9b1174642e070e /sys/arch/alpha/pci/sio_pic.c
initial import of NetBSD tree
Diffstat (limited to 'sys/arch/alpha/pci/sio_pic.c')
-rw-r--r--sys/arch/alpha/pci/sio_pic.c378
1 files changed, 378 insertions, 0 deletions
diff --git a/sys/arch/alpha/pci/sio_pic.c b/sys/arch/alpha/pci/sio_pic.c
new file mode 100644
index 00000000000..e2f7e8cefdb
--- /dev/null
+++ b/sys/arch/alpha/pci/sio_pic.c
@@ -0,0 +1,378 @@
+/* $NetBSD: sio_pic.c,v 1.1 1995/06/28 01:26:13 cgd Exp $ */
+
+/*
+ * Copyright (c) 1995 Carnegie-Mellon University.
+ * All rights reserved.
+ *
+ * Author: Chris G. Demetriou
+ *
+ * Permission to use, copy, modify and distribute this software and
+ * its documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
+ * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie the
+ * rights to redistribute these changes.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/device.h>
+#include <sys/malloc.h>
+#include <sys/syslog.h>
+
+#include <dev/isa/isavar.h>
+#include <dev/isa/isareg.h>
+#include <alpha/isa/isa_intr.h>
+
+#include <machine/pio.h>
+
+/*
+ * To add to the long history of wonderful PROM console traits,
+ * AlphaStation PROMs don't reset themselves completely on boot!
+ * Therefore, if an interrupt was turned on when the kernel was
+ * started, we're not going to EVER turn it off... I don't know
+ * what will happen if new interrupts (that the PROM console doesn't
+ * want) are turned on. I'll burn that bridge when I come to it.
+ */
+#define BROKEN_PROM_CONSOLE
+
+static void sio_intr_setup __P((void));
+static void *sio_intr_establish __P((int intr, isa_intrtype type,
+ isa_intrlevel level, int (*ih_fun)(void *), void *ih_arg));
+static void sio_intr_disestablish __P((void *handler));
+static void sio_iointr __P((void *framep, int vec));
+
+struct isa_intr_fcns sio_intr_fcns = {
+ sio_intr_setup, sio_intr_establish,
+ sio_intr_disestablish, sio_iointr,
+};
+
+static void sio_strayintr __P((int irq));
+
+/*
+ * Interrupt handler chains. sio_intr_establish() inserts a handler into
+ * the list. The handler is called with its (single) argument.
+ */
+struct intrhand {
+ int (*ih_fun)();
+ void *ih_arg;
+ u_long ih_count;
+ struct intrhand *ih_next;
+ int ih_level;
+ int ih_irq;
+};
+
+#define ICU_LEN 16 /* number of ISA IRQs */
+
+static struct intrhand *sio_intrhand[ICU_LEN];
+static isa_intrtype sio_intrtype[ICU_LEN];
+static u_long sio_strayintrcnt[ICU_LEN];
+
+#ifndef STRAY_MAX
+#ifdef BROKEN_PROM_CONSOLE
+/*
+ * If prom console is broken, because initial interrupt settings
+ * must be kept, there's no way to escape stray interrupts.
+ */
+#define STRAY_MAX 0
+#else
+#define STRAY_MAX 5
+#endif
+#endif
+
+#ifdef BROKEN_PROM_CONSOLE
+/*
+ * If prom console is broken, must remember the initial interrupt
+ * settings and enforce them. WHEE!
+ */
+u_int8_t initial_ocw1[2];
+u_int8_t initial_elcr[2];
+#define INITIALLY_ENABLED(irq) \
+ ((initial_ocw1[(irq) / 8] & (1 << ((irq) % 8))) == 0)
+#define INITIALLY_LEVEL_TRIGGERED(irq) \
+ ((initial_elcr[(irq) / 8] & (1 << ((irq) % 8))) != 0)
+#else
+#define INITIALLY_ENABLED(irq) ((irq) == 2 ? 1 : 0)
+#define INITIALLY_LEVEL_TRIGGERED(irq) 0
+#endif
+
+void
+sio_setirqstat(irq, enabled, type)
+ int irq, enabled;
+ isa_intrtype type;
+{
+ u_int8_t ocw1[2], elcr[2];
+ int icu, bit;
+
+#if 0
+ printf("sio_setirqstat: irq %d, %s, %s\n", irq,
+ enabled ? "enabled" : "disabled", isa_intr_typename(type));
+#endif
+
+ sio_intrtype[irq] = type;
+
+ icu = irq / 8;
+ bit = irq % 8;
+
+ ocw1[0] = inb(IO_ICU1 + 1);
+ ocw1[1] = inb(IO_ICU2 + 1);
+ elcr[0] = inb(0x4d0); /* XXX */
+ elcr[1] = inb(0x4d1); /* XXX */
+
+ /*
+ * interrupt enable: set bit to mask (disable) interrupt.
+ */
+ if (enabled)
+ ocw1[icu] &= ~(1 << bit);
+ else
+ ocw1[icu] |= 1 << bit;
+
+ /*
+ * interrupt type select: set bit to get level-triggered.
+ */
+ if (type == ISA_IST_LEVEL)
+ elcr[icu] |= 1 << bit;
+ else
+ elcr[icu] &= ~(1 << bit);
+
+#ifdef not_here
+ /* see the init function... */
+ ocw1[0] &= ~0x04; /* always enable IRQ2 on first PIC */
+ elcr[0] &= ~0x07; /* IRQ[0-2] must be edge-triggered */
+ elcr[1] &= ~0x21; /* IRQ[13,8] must be edge-triggered */
+#endif
+
+#ifdef BROKEN_PROM_CONSOLE
+ /*
+ * make sure that the initially clear bits (unmasked interrupts)
+ * are never set, and that the initially-level-triggered
+ * intrrupts always remain level-triggered, to keep the prom happy.
+ */
+ if ((ocw1[0] & ~initial_ocw1[0]) != 0 ||
+ (ocw1[1] & ~initial_ocw1[1]) != 0 ||
+ (elcr[0] & initial_elcr[0]) != initial_elcr[0] ||
+ (elcr[1] & initial_elcr[1]) != initial_elcr[1]) {
+ printf("sio_sis: initial: ocw = (%2x,%2x), elcr = (%2x,%2X)\n",
+ initial_ocw1[0], initial_ocw1[1],
+ initial_elcr[0], initial_elcr[1]);
+ printf(" current: ocw = (%2x,%2x), elcr = (%2x,%2X)\n",
+ ocw1[0], ocw1[1], elcr[0], elcr[1]);
+ panic("sio_setirqstat: hosed");
+ }
+#endif
+
+ outb(IO_ICU1 + 1, ocw1[0]);
+ outb(IO_ICU2 + 1, ocw1[1]);
+ outb(0x4d0, elcr[0]); /* XXX */
+ outb(0x4d1, elcr[1]); /* XXX */
+}
+
+void
+sio_intr_setup()
+{
+ int i;
+
+#ifdef BROKEN_PROM_CONSOLE
+ /*
+ * Remember the initial values, because the prom is stupid.
+ */
+ initial_ocw1[0] = inb(IO_ICU1 + 1);
+ initial_ocw1[1] = inb(IO_ICU2 + 1);
+ initial_elcr[0] = inb(0x4d0); /* XXX */
+ initial_elcr[1] = inb(0x4d1); /* XXX */
+#if 0
+ printf("initial_ocw1[0] = 0x%x\n", initial_ocw1[0]);
+ printf("initial_ocw1[1] = 0x%x\n", initial_ocw1[1]);
+ printf("initial_elcr[0] = 0x%x\n", initial_elcr[0]);
+ printf("initial_elcr[1] = 0x%x\n", initial_elcr[1]);
+#endif
+#endif
+
+ /*
+ * set up initial values for interrupt enables.
+ */
+ for (i = 0; i < ICU_LEN; i++) {
+ switch (i) {
+ case 0:
+ case 1:
+ case 8:
+ case 13:
+ /*
+ * IRQs 0, 1, 8, and 13 must always be
+ * edge-triggered.
+ */
+ if (INITIALLY_LEVEL_TRIGGERED(i))
+ printf("sio_intr_setup: %d LT!\n", i);
+ sio_setirqstat(i, INITIALLY_ENABLED(i), ISA_IST_EDGE);
+ break;
+
+ case 2:
+ /*
+ * IRQ 2 must be edge-triggered, and should be
+ * enabled (otherwise IRQs 8-15 are ignored).
+ */
+ if (INITIALLY_LEVEL_TRIGGERED(i))
+ printf("sio_intr_setup: %d LT!\n", i);
+ if (!INITIALLY_ENABLED(i))
+ printf("sio_intr_setup: %d not enabled!\n", i);
+ sio_setirqstat(i, 1, ISA_IST_EDGE);
+ break;
+
+ default:
+ /*
+ * Otherwise, disable the IRQ and set its
+ * type to (effectively) "unknown."
+ */
+ sio_setirqstat(i, INITIALLY_ENABLED(i),
+ INITIALLY_LEVEL_TRIGGERED(i) ? ISA_IST_LEVEL :
+ ISA_IST_NONE);
+ break;
+ }
+ }
+}
+
+void *
+sio_intr_establish(irq, type, level, ih_fun, ih_arg)
+ int irq;
+ isa_intrtype type;
+ isa_intrlevel level;
+ int (*ih_fun)(void *);
+ void *ih_arg;
+{
+ struct intrhand **p, *c, *ih;
+ extern int cold;
+
+ /* no point in sleeping unless someone can free memory. */
+ ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
+ if (ih == NULL)
+ panic("sio_intr_establish: can't malloc handler info");
+
+ if (irq < 0 || irq > ICU_LEN || type == ISA_IST_NONE)
+ panic("sio_intr_establish: bogus irq or type");
+
+ switch (sio_intrtype[irq]) {
+ case ISA_IST_EDGE:
+ case ISA_IST_LEVEL:
+ if (type == sio_intrtype[irq])
+ break;
+ case ISA_IST_PULSE:
+ if (type != ISA_IST_NONE)
+ panic("intr_establish: can't share %s with %s",
+ isa_intr_typename(sio_intrtype[irq]),
+ isa_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 = &sio_intrhand[irq]; (c = *p) != NULL; p = &c->ih_next)
+ ;
+
+ /*
+ * 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 = 0; /* XXX meaningless on alpha */
+ ih->ih_irq = irq;
+ *p = ih;
+
+ sio_setirqstat(irq, 1, type);
+
+ return ih;
+}
+
+void
+sio_intr_disestablish(handler)
+ void *handler;
+{
+
+ printf("sio_intr_disestablish(%lx)\n", handler);
+ /* XXX */
+
+ /* XXX NEVER ALLOW AN INITIALLY-ENABLED INTERRUPT TO BE DISABLED */
+ /* XXX NEVER ALLOW AN INITIALLY-LT INTERRUPT TO BECOME UNTYPED */
+}
+
+/*
+ * caught a stray interrupt; notify if not too many seen already.
+ */
+void
+sio_strayintr(irq)
+ int irq;
+{
+
+ if (++sio_strayintrcnt[irq] <= STRAY_MAX)
+ log(LOG_ERR, "stray interrupt %d%s\n", irq,
+ sio_strayintrcnt[irq] >= STRAY_MAX ?
+ "; stopped logging" : "");
+}
+
+void
+sio_iointr(framep, vec)
+ void *framep;
+ int vec;
+{
+ int irq, handled;
+ struct intrhand *ih;
+
+ irq = (vec - 0x800) >> 4;
+#ifdef DIAGNOSTIC
+ if (irq > ICU_LEN || irq < 0)
+ panic("sio_iointr: irq out of range (%d)", irq);
+#endif
+
+ /*
+ * We cdr down the intrhand chain, calling each handler with
+ * its appropriate argument;
+ *
+ * The handler returns one of three values:
+ * 0 - This interrupt wasn't for me.
+ * 1 - This interrupt was for me.
+ * -1 - This interrupt might have been for me, but I don't know.
+ * If there are no handlers, or they all return 0, we flags it as a
+ * `stray' interrupt. On a system with level-triggered interrupts,
+ * we could terminate immediately when one of them returns 1; but
+ * this is PC-ish!
+ */
+ for (ih = sio_intrhand[irq], handled = 0; ih != NULL;
+ ih = ih->ih_next) {
+ int rv;
+
+ rv = (*ih->ih_fun)(ih->ih_arg);
+
+ ih->ih_count++;
+ handled = handled || (rv != 0);
+ }
+
+ if (!handled)
+ sio_strayintr(irq);
+
+ /*
+ * Some versions of the machines which use the SIO
+ * (or is it some PALcode revisions on those machines?)
+ * require the non-specific EOI to be fed to the PIC(s)
+ * by the interrupt handler.
+ */
+ if (irq > 7)
+ outb(IO_ICU2 + 0, 0x20 | (irq & 0x07)); /* XXX */
+ outb(IO_ICU1 + 0, 0x20 | (irq > 7 ? 2 : irq)); /* XXX */
+}