summaryrefslogtreecommitdiff
path: root/sys/src/9/pc/irq.c
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@felloff.net>2020-11-22 17:44:21 +0100
committercinap_lenrek <cinap_lenrek@felloff.net>2020-11-22 17:44:21 +0100
commita041c90431c5987496ac1d5de4f330f70fd2966f (patch)
tree3907820cd93bd325697dfc5eae1a1bbd8d8d89e9 /sys/src/9/pc/irq.c
parent97008caa4174cee3dd849d9962c529897717e333 (diff)
pc, pc64: move common irq handling code out of trap.c
Move the common irq handling code out of trap.c into pc/irq.c so that it can be shared between 386 and amd64 ports.
Diffstat (limited to 'sys/src/9/pc/irq.c')
-rw-r--r--sys/src/9/pc/irq.c313
1 files changed, 313 insertions, 0 deletions
diff --git a/sys/src/9/pc/irq.c b/sys/src/9/pc/irq.c
new file mode 100644
index 000000000..3ad717849
--- /dev/null
+++ b/sys/src/9/pc/irq.c
@@ -0,0 +1,313 @@
+#include "u.h"
+#include "tos.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "ureg.h"
+#include "../port/error.h"
+
+static Lock vctllock;
+static Vctl *vctl[256];
+
+enum
+{
+ Ntimevec = 20 /* number of time buckets for each intr */
+};
+ulong intrtimes[256][Ntimevec];
+
+/*
+ * keep histogram of interrupt service times
+ */
+static void
+intrtime(Mach*, int vno)
+{
+ ulong diff;
+ ulong x;
+
+ x = perfticks();
+ diff = x - m->perf.intrts;
+ m->perf.intrts = x;
+
+ m->perf.inintr += diff;
+ if(up == nil && m->perf.inidle > diff)
+ m->perf.inidle -= diff;
+
+ diff /= m->cpumhz*100; /* quantum = 100µsec */
+ if(diff >= Ntimevec)
+ diff = Ntimevec-1;
+ intrtimes[vno][diff]++;
+}
+
+int
+irqhandled(Ureg *ureg, int vno)
+{
+ Vctl *ctl, *v;
+ int i;
+
+ if(ctl = vctl[vno]){
+ if(ctl->isintr){
+ m->perf.intrts = perfticks();
+ m->intr++;
+ if(vno >= VectorPIC)
+ m->lastintr = ctl->irq;
+ }
+ if(ctl->isr)
+ ctl->isr(vno);
+ for(v = ctl; v != nil; v = v->next){
+ if(v->f)
+ v->f(ureg, v->a);
+ }
+ if(ctl->eoi)
+ ctl->eoi(vno);
+
+ if(ctl->isintr){
+ intrtime(m, vno);
+
+ if(up){
+ if(ctl->irq == IrqCLOCK || ctl->irq == IrqTIMER){
+ /* delaysched set because we held a lock or because our quantum ended */
+ if(up->delaysched)
+ sched();
+ } else {
+ preempted();
+ }
+ }
+ }
+ return 1;
+ }
+
+ if(vno < VectorPIC)
+ return 0;
+
+ /*
+ * An unknown interrupt.
+ * Check for a default IRQ7. This can happen when
+ * the IRQ input goes away before the acknowledge.
+ * In this case, a 'default IRQ7' is generated, but
+ * the corresponding bit in the ISR isn't set.
+ * In fact, just ignore all such interrupts.
+ */
+
+ /* call all interrupt routines, just in case */
+ for(i = VectorPIC; i <= MaxIrqLAPIC; i++){
+ ctl = vctl[i];
+ if(ctl == nil)
+ continue;
+ if(!ctl->isintr)
+ continue;
+ for(v = ctl; v != nil; v = v->next){
+ if(v->f)
+ v->f(ureg, v->a);
+ }
+ /* should we do this? */
+ if(ctl->eoi)
+ ctl->eoi(i);
+ }
+
+ /* clear the interrupt */
+ i8259isr(vno);
+
+ if(0)print("cpu%d: spurious interrupt %d, last %d\n",
+ m->machno, vno, m->lastintr);
+ if(0)if(conf.nmach > 1){
+ for(i = 0; i < MAXMACH; i++){
+ Mach *mach;
+
+ if(active.machs[i] == 0)
+ continue;
+ mach = MACHP(i);
+ if(m->machno == mach->machno)
+ continue;
+ print(" cpu%d: last %d",
+ mach->machno, mach->lastintr);
+ }
+ print("\n");
+ }
+ m->spuriousintr++;
+ return -1;
+}
+
+void
+trapenable(int vno, void (*f)(Ureg*, void*), void* a, char *name)
+{
+ Vctl *v;
+
+ if(vno < 0 || vno >= VectorPIC)
+ panic("trapenable: vno %d", vno);
+ if((v = xalloc(sizeof(Vctl))) == nil)
+ panic("trapenable: out of memory");
+ v->tbdf = BUSUNKNOWN;
+ v->f = f;
+ v->a = a;
+ strncpy(v->name, name, KNAMELEN-1);
+ v->name[KNAMELEN-1] = 0;
+
+ ilock(&vctllock);
+ if(vctl[vno])
+ v->next = vctl[vno]->next;
+ vctl[vno] = v;
+ iunlock(&vctllock);
+}
+
+void
+intrenable(int irq, void (*f)(Ureg*, void*), void* a, int tbdf, char *name)
+{
+ int vno;
+ Vctl *v;
+
+ if(f == nil){
+ print("intrenable: nil handler for %d, tbdf 0x%uX for %s\n",
+ irq, tbdf, name);
+ return;
+ }
+
+ if(tbdf != BUSUNKNOWN && (irq == 0xff || irq == 0))
+ irq = -1;
+
+
+ /*
+ * IRQ2 doesn't really exist, it's used to gang the interrupt
+ * controllers together. A device set to IRQ2 will appear on
+ * the second interrupt controller as IRQ9.
+ */
+ if(irq == 2)
+ irq = 9;
+
+ if((v = xalloc(sizeof(Vctl))) == nil)
+ panic("intrenable: out of memory");
+ v->isintr = 1;
+ v->irq = irq;
+ v->tbdf = tbdf;
+ v->f = f;
+ v->a = a;
+ strncpy(v->name, name, KNAMELEN-1);
+ v->name[KNAMELEN-1] = 0;
+
+ ilock(&vctllock);
+ vno = arch->intrenable(v);
+ if(vno == -1){
+ iunlock(&vctllock);
+ print("intrenable: couldn't enable irq %d, tbdf 0x%uX for %s\n",
+ irq, tbdf, v->name);
+ xfree(v);
+ return;
+ }
+ if(vctl[vno]){
+ if(vctl[vno]->isr != v->isr || vctl[vno]->eoi != v->eoi)
+ panic("intrenable: handler: %s %s %#p %#p %#p %#p",
+ vctl[vno]->name, v->name,
+ vctl[vno]->isr, v->isr, vctl[vno]->eoi, v->eoi);
+ v->next = vctl[vno];
+ }
+ vctl[vno] = v;
+ iunlock(&vctllock);
+}
+
+void
+intrdisable(int irq, void (*f)(Ureg *, void *), void *a, int tbdf, char *name)
+{
+ Vctl **pv, *v;
+ int vno;
+
+ if(irq == 2)
+ irq = 9;
+ if(arch->intrvecno == nil || (tbdf != BUSUNKNOWN && (irq == 0xff || irq == 0))){
+ /*
+ * on APIC machine, irq is pretty meaningless
+ * and disabling a the vector is not implemented.
+ * however, we still want to remove the matching
+ * Vctl entry to prevent calling Vctl.f() with a
+ * stale Vctl.a pointer.
+ */
+ irq = -1;
+ vno = VectorPIC;
+ } else {
+ vno = arch->intrvecno(irq);
+ }
+ ilock(&vctllock);
+ do {
+ for(pv = &vctl[vno]; (v = *pv) != nil; pv = &v->next){
+ if(v->isintr && (v->irq == irq || irq == -1)
+ && v->tbdf == tbdf && v->f == f && v->a == a
+ && strcmp(v->name, name) == 0)
+ break;
+ }
+ if(v != nil){
+ if(v->disable != nil)
+ (*v->disable)(v);
+ *pv = v->next;
+ xfree(v);
+
+ if(irq != -1 && vctl[vno] == nil && arch->intrdisable != nil)
+ arch->intrdisable(irq);
+ break;
+ }
+ } while(irq == -1 && ++vno <= MaxVectorAPIC);
+ iunlock(&vctllock);
+}
+
+static long
+irqallocread(Chan*, void *a, long n, vlong offset)
+{
+ char buf[2*(11+1)+KNAMELEN+1+1];
+ int vno, m;
+ Vctl *v;
+
+ if(n < 0 || offset < 0)
+ error(Ebadarg);
+
+ for(vno=0; vno<nelem(vctl); vno++){
+ for(v=vctl[vno]; v; v=v->next){
+ m = snprint(buf, sizeof(buf), "%11d %11d %.*s\n", vno, v->irq, KNAMELEN, v->name);
+ offset -= m;
+ if(offset >= 0)
+ continue;
+ if(n > -offset)
+ n = -offset;
+ offset += m;
+ memmove(a, buf+offset, n);
+ return n;
+ }
+ }
+ return 0;
+}
+
+static void
+nmienable(void)
+{
+ int x;
+
+ /*
+ * Hack: should be locked with NVRAM access.
+ */
+ outb(0x70, 0x80); /* NMI latch clear */
+ outb(0x70, 0);
+
+ x = inb(0x61) & 0x07; /* Enable NMI */
+ outb(0x61, 0x0C|x);
+ outb(0x61, x);
+}
+
+static void
+nmihandler(Ureg *ureg, void*)
+{
+ /*
+ * Don't re-enable, it confuses the crash dumps.
+ nmienable();
+ */
+ iprint("cpu%d: nmi PC %#p, status %ux\n",
+ m->machno, ureg->pc, inb(0x61));
+ while(m->machno != 0)
+ ;
+}
+
+void
+irqinit(void)
+{
+ addarchfile("irqalloc", 0444, irqallocread, nil);
+
+ trapenable(VectorNMI, nmihandler, nil, "nmi");
+ nmienable();
+}