#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: tr2a_intr.c,v 1.15 2011/03/10 17:05:41 tsutsui Exp $");
#define __INTR_PRIVATE
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/evcnt.h>
#include <sys/lwp.h>
#include <sys/cpu.h>
#include <sys/intr.h>
#include <machine/locore.h>
#include <machine/sbdvar.h>
#define _SBD_TR2A_PRIVATE
#include <machine/sbd_tr2a.h>
SBD_DECL(tr2a);
const struct ipl_sr_map tr2a_ipl_sr_map = {
.sr_bits = {
[IPL_NONE] = 0,
[IPL_SOFTCLOCK] = MIPS_SOFT_INT_MASK_0,
[IPL_SOFTNET] = MIPS_SOFT_INT_MASK,
[IPL_VM] = MIPS_INT_MASK & ~MIPS_INT_MASK_5,
[IPL_SCHED] = MIPS_INT_MASK,
[IPL_DDB] = MIPS_INT_MASK,
[IPL_HIGH] = MIPS_INT_MASK,
},
};
#define NIRQ 16
enum bustype {
ASOBUS,
APBUS,
};
struct tr2a_intr_handler {
int (*func)(void *);
void *arg;
enum bustype bustype;
uint32_t cpu_int;
uint32_t aso_mask;
struct evcnt evcnt;
char evname[32];
} tr2a_intr_handler[NIRQ] = {
[0] = { NULL, NULL, ASOBUS, 2, 0x00000001 },
[4] = { NULL, NULL, ASOBUS, 4, 0x00300010 },
[6] = { NULL, NULL, ASOBUS, 2, 0x00000200 },
[9] = { NULL, NULL, ASOBUS, 4, 0x00000040 },
[10] = { NULL, NULL, ASOBUS, 2, 0x00000100 },
};
struct tr2a_intc_handler {
int ref_cnt;
uint32_t mask;
} tr2a_intc_handler[6] = {
[0] = { 0, 0x00000020 },
[1] = { 0, 0x00000800 },
[2] = { 0, 0x00010000 },
[3] = { 0, 0x00200000 },
[4] = { 0, 0x04000000 },
[5] = { 0, 0x80000000 }
};
struct evcnt timer_tr2a_ev =
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "intc", "timer");
void
tr2a_intr_init(void)
{
#if 1
*ASO_INT_MASK_REG &= ~TR2A_ASO_INTMASK_ALL;
#else
*ASO_INT_MASK_REG |= TR2A_ASO_INTMASK_ALL;
#endif
tr2a_wbflush();
*INTC_MASK_REG = 0;
*CLOCK_REG = 0x80;
tr2a_wbflush();
if ((*CLOCK_REG & 0x80) != 0)
*ASO_INT_MASK_REG |= 0x8000;
tr2a_wbflush();
evcnt_attach_static(&timer_tr2a_ev);
}
void *
tr2a_intr_establish(int irq, int (*func)(void *), void *arg)
{
struct tr2a_intr_handler *ih = &tr2a_intr_handler[irq];
struct tr2a_intc_handler *ic = &tr2a_intc_handler[ih->cpu_int];
int s;
s = splhigh();
ih->func = func;
ih->arg = arg;
snprintf(ih->evname, sizeof(ih->evname), "irq %d", irq);
evcnt_attach_dynamic(&ih->evcnt, EVCNT_TYPE_INTR,
NULL, "intc", ih->evname);
if (ih->bustype == ASOBUS)
*ASO_INT_MASK_REG |= ih->aso_mask;
if (ic->ref_cnt++ == 0)
*INTC_MASK_REG |= ic->mask;
tr2a_wbflush();
splx(s);
return (void *)irq;
}
void
tr2a_intr_disestablish(void *arg)
{
int s, irq = (int)arg;
struct tr2a_intr_handler *ih = &tr2a_intr_handler[irq];
struct tr2a_intc_handler *ic = &tr2a_intc_handler[ih->cpu_int];
s = splhigh();
if (ih->bustype == ASOBUS)
*ASO_INT_MASK_REG &= ~ih->aso_mask;
if (--ic->ref_cnt == 0)
*INTC_MASK_REG &= ~ic->mask;
ih->func = NULL;
ih->arg = NULL;
evcnt_detach(&ih->evcnt);
splx(s);
}
void
tr2a_intr(int ppl, vaddr_t pc, uint32_t status)
{
struct tr2a_intr_handler *ih;
struct clockframe cf;
uint32_t r, intc_cause, ipending;
int ipl;
intc_cause = *INTC_STATUS_REG & *INTC_MASK_REG;
while (ppl < (ipl = splintr(&ipending))) {
if ((ipending & MIPS_INT_MASK_5) && (intc_cause & INTC_INT5)) {
cf.pc = pc;
cf.sr = status;
cf.intr = (curcpu()->ci_idepth > 1);
tr2a_wbflush();
*INTC_CLEAR_REG = 0x7c;
*INTC_STATUS_REG;
hardclock(&cf);
timer_tr2a_ev.ev_count++;
}
if ((ipending & MIPS_INT_MASK_4) && (intc_cause & INTC_INT4)) {
r = *ASO_INT_STATUS_REG;
if (r & 0x300010) {
ih = &tr2a_intr_handler[4];
if (ih->func) {
ih->func(ih->arg);
ih->evcnt.ev_count++;
}
} else if (r & 0x40) {
ih = &tr2a_intr_handler[9];
if (ih->func) {
ih->func(ih->arg);
ih->evcnt.ev_count++;
}
} else if (r & 0x20) {
printf("INT4 (1)\n");
} else if (r & 0x00800000) {
printf("INT4 (2)\n");
} else if (r & 0x00400000) {
printf("INT4 (3)\n");
} else if (r != 0) {
printf("not for INT4 %x\n", r);
}
tr2a_wbflush();
*INTC_CLEAR_REG = 0x68;
*INTC_STATUS_REG;
}
if ((ipending & MIPS_INT_MASK_3) && (intc_cause & INTC_INT3)) {
printf("APbus HI\n");
tr2a_wbflush();
*INTC_CLEAR_REG = 0x54;
*INTC_STATUS_REG;
}
if ((ipending & MIPS_INT_MASK_2) && (intc_cause & INTC_INT2)) {
r = *ASO_INT_STATUS_REG;
if (r & 0x100) {
ih = &tr2a_intr_handler[6];
if (ih->func) {
ih->func(ih->arg);
ih->evcnt.ev_count++;
}
} else if (r & 0x200) {
ih = &tr2a_intr_handler[10];
if (ih->func) {
ih->func(ih->arg);
ih->evcnt.ev_count++;
}
} else if (r & 0x1) {
ih = &tr2a_intr_handler[0];
if (ih->func) {
ih->func(ih->arg);
ih->evcnt.ev_count++;
}
} else if (r != 0) {
printf("not for INT2 %x %x\n", r,
*ASO_DMAINT_STATUS_REG);
}
tr2a_wbflush();
*INTC_CLEAR_REG = 0x40;
*INTC_STATUS_REG;
}
if ((ipending & MIPS_INT_MASK_1) && (intc_cause & INTC_INT1)) {
printf("APbus LO\n");
tr2a_wbflush();
*INTC_CLEAR_REG = 0x2c;
*INTC_STATUS_REG;
}
if ((ipending & MIPS_INT_MASK_0) && (intc_cause & INTC_INT0)) {
r = *ASO_INT_STATUS_REG;
printf("INT0 %08x\n", r);
if (r & 0x8000) {
printf("INT0(1) NMI\n");
} else if (r & 0x8) {
printf("INT0(2)\n");
} else if (r & 0x4) {
printf("INT0(3)\n");
} else if (r != 0) {
printf("not for INT0 %x\n", r);
}
tr2a_wbflush();
*INTC_CLEAR_REG = 0x14;
*INTC_STATUS_REG;
}
intc_cause = *INTC_STATUS_REG & *INTC_MASK_REG;
}
}
void
tr2a_initclocks(void)
{
*INTC_MASK_REG |= INTC_INT5;
tr2a_wbflush();
}