shithub: riscv

ref: 781a8f8d9f50221dc34c0dab5baf48e197444269
dir: /sys/src/9/pc/irq.c/

View raw version
#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();
}