#include "u.h"
#include <ureg.h>
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/error.h"

enum {
	NINTR = 212,
	NPRIVATE = 32
};

static struct irq {
	void (*f)(Ureg *, void *);
	void *arg;
	char *name;
} irqs[NINTR];

enum {
	ICCICR = 0x100/4,
	ICCPMR,
	ICCBPR,
	ICCIAR,
	ICCEOIR,
	ICDDCR = 0x1000/4,
	ICDISR = 0x1080/4,
	ICDISER = 0x1100/4,
	ICDICER = 0x1180/4,
	ICDICPR = 0x1280/4,
	ICDABR  = 0x1300/4,
	ICDIPRI = 0x1400/4,
	ICDIPTR = 0x1800/4,
	ICDICFR = 0x1C00/4,
};

void
intrinit(void)
{
	int i;

	mpcore[ICDDCR] = 3;
	mpcore[ICCICR] = 7;
	mpcore[ICCBPR] = 3;
	mpcore[ICCPMR] = 255;

	if(m->machno != 0)
		return;
	
	/* disable all irqs and clear any pending interrupts */
	for(i = 0; i < NINTR/32; i++){
		mpcore[ICDISR + i] = -1;
		mpcore[ICDICER + i] = -1;
		mpcore[ICDICPR + i] = -1;
		mpcore[ICDABR + i] = 0;
	}
}

void
intrenable(int irq, void (*f)(Ureg *, void *), void *arg, int type, char *name)
{
	ulong *e, s;
	struct irq *i;

	if(f == nil)
		panic("intrenable: f == nil");
	if(irq < 0 || irq >= NINTR)
		panic("intrenable: invalid irq %d", irq);
	if(type != LEVEL && type != EDGE)
		panic("intrenable: invalid type %d", type);
	if(irqs[irq].f != nil && irqs[irq].f != f)
		panic("intrenable: handler already assigned");
	if(irq >= NPRIVATE){
		e = &mpcore[ICDIPTR + (irq >> 2)];
		s = irq << 3 & 24;
		*e = *e & ~(3 << s) | 1 << s;
		e = &mpcore[ICDICFR + (irq >> 4)];
		s = irq << 1 & 30 | 1;
		*e = *e & ~(1 << s) | type << s;
	}
	((uchar*)&mpcore[ICDIPRI])[irq] = 0;
	i = &irqs[irq];
	i->f = f;
	i->arg = arg;
	i->name = name;
	mpcore[ICDISER + (irq >> 5)] = 1 << (irq & 31);
	mpcore[ICDABR + (irq >> 5)] |= 1 << (irq & 31);
}

void
intr(Ureg *ureg)
{
	ulong v;
	int irq;
	struct irq *i;

	v = mpcore[ICCIAR];
	irq = v & 0x3ff;
	if(irq == 0x3ff)
		return;
		
	m->intr++;
	m->lastintr = irq;
	i = &irqs[irq];
	if(i->f == nil)
		print("irq without handler %d\n", irq);
	else
		i->f(ureg, i->arg);
	mpcore[ICCEOIR] = v;

	preempted(irq == TIMERIRQ);
}
