#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ap_ms104_sh4_intr.c,v 1.4 2020/11/21 16:21:24 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
#include <sys/device.h>
#include <sh3/devreg.h>
#include <sh3/exception.h>
#include <machine/intr.h>
#include <evbsh3/ap_ms104_sh4/ap_ms104_sh4reg.h>
#include <evbsh3/ap_ms104_sh4/ap_ms104_sh4var.h>
#define _N_EXTINTR 16
struct intrhand {
int (*ih_fun)(void *);
void *ih_arg;
struct intrhand *ih_next;
int ih_enable;
int ih_level;
int ih_irq;
struct evcnt ih_evcnt;
};
struct extintr_handler {
void *eih_func;
struct intrhand *eih_ih;
int eih_nih;
};
static struct extintr_handler extintr_handler[_N_EXTINTR];
static const char *extintr_names[_N_EXTINTR] = {
"irq0", "irq1", "irq2", "irq3",
"irq4", "irq5", "irq6", "irq7",
"irq8", "irq9", "irq10", "irq11",
"irq12", "irq13", "irq14", "irq15"
};
static int fakeintr(void *arg);
static int extintr_intr_handler(void *arg);
void
extintr_init(void)
{
_reg_write_1(EXTINTR_MASK1, 0);
_reg_write_1(EXTINTR_MASK2, 0);
_reg_write_1(EXTINTR_MASK3, 0);
_reg_write_1(EXTINTR_MASK4, 0);
}
static int
fakeintr(void *arg)
{
return 0;
}
void *
extintr_establish(int irq, int trigger, int level,
int (*ih_fun)(void *), void *ih_arg)
{
static struct intrhand fakehand = {fakeintr};
struct extintr_handler *eih;
struct intrhand **p, *q, *ih;
const char *name;
int evtcode;
int s;
KDASSERT(irq >= 1 && irq <= 14);
ih = kmem_alloc(sizeof(*ih), KM_SLEEP);
s = _cpu_intr_suspend();
switch (level) {
default:
#if defined(DEBUG)
panic("extintr_establish: unknown level %d", level);
#endif
case IPL_VM:
break;
}
eih = &extintr_handler[irq];
if (eih->eih_func == NULL) {
evtcode = 0x200 + (irq << 5);
eih->eih_func = intc_intr_establish(evtcode, trigger, level,
extintr_intr_handler, eih);
}
for (p = &eih->eih_ih; (q = *p) != NULL; p = &q->ih_next)
continue;
fakehand.ih_level = level;
*p = &fakehand;
memset(ih, 0, sizeof(*ih));
ih->ih_fun = ih_fun;
ih->ih_arg = ih_arg;
ih->ih_next = NULL;
ih->ih_enable = 1;
ih->ih_level = level;
ih->ih_irq = irq;
name = extintr_names[irq];
evcnt_attach_dynamic(&ih->ih_evcnt, EVCNT_TYPE_INTR, NULL, "ext", name);
*p = ih;
if (++eih->eih_nih == 1) {
uint8_t reg;
switch (irq) {
case 1: case 2:
reg = _reg_read_1(EXTINTR_MASK4);
reg |= 1 << (2 - irq);
_reg_write_1(EXTINTR_MASK4, reg);
break;
case 3: case 4: case 5: case 6:
reg = _reg_read_1(EXTINTR_MASK3);
reg |= 1 << (6 - irq);
_reg_write_1(EXTINTR_MASK3, reg);
break;
case 7: case 8: case 9: case 10:
reg = _reg_read_1(EXTINTR_MASK2);
reg |= 1 << (10 - irq);
_reg_write_1(EXTINTR_MASK2, reg);
break;
case 11: case 12: case 13: case 14:
reg = _reg_read_1(EXTINTR_MASK1);
reg |= 1 << (14 - irq);
_reg_write_1(EXTINTR_MASK1, reg);
break;
default:
panic("unknown irq%d\n", irq);
break;
}
}
splx(s);
return (ih);
}
void
extintr_disestablish(void *cookie)
{
struct intrhand *ih = (struct intrhand *)cookie;
struct intrhand **p, *q;
struct extintr_handler *eih;
int irq;
int s;
KDASSERT(ih != NULL);
s = _cpu_intr_suspend();
irq = ih->ih_irq;
eih = &extintr_handler[irq];
for (p = &eih->eih_ih; (q = *p) != NULL && q != ih; p = &q->ih_next)
continue;
if (q == NULL)
panic("extintr_disestablish: handler not registered");
*p = q->ih_next;
evcnt_detach(&ih->ih_evcnt);
kmem_free((void *)ih, sizeof(*ih));
if (--eih->eih_nih == 0) {
uint8_t reg;
intc_intr_disestablish(eih->eih_func);
eih->eih_func = NULL;
switch (irq) {
case 1: case 2:
reg = _reg_read_1(EXTINTR_MASK4);
reg &= ~(1 << (2 - irq));
_reg_write_1(EXTINTR_MASK4, reg);
break;
case 3: case 4: case 5: case 6:
reg = _reg_read_1(EXTINTR_MASK3);
reg &= ~(1 << (6 - irq));
_reg_write_1(EXTINTR_MASK3, reg);
break;
case 7: case 8: case 9: case 10:
reg = _reg_read_1(EXTINTR_MASK2);
reg &= ~(1 << (10 - irq));
_reg_write_1(EXTINTR_MASK2, reg);
break;
case 11: case 12: case 13: case 14:
reg = _reg_read_1(EXTINTR_MASK1);
reg &= ~(1 << (14 - irq));
_reg_write_1(EXTINTR_MASK1, reg);
break;
default:
panic("unknown irq%d\n", irq);
break;
}
}
splx(s);
}
static int
extintr_intr_handler(void *arg)
{
struct extintr_handler *eih = arg;
struct intrhand *ih;
int r;
if (__predict_true(eih != NULL)) {
for (ih = eih->eih_ih; ih != NULL; ih = ih->ih_next) {
if (__predict_true(ih->ih_enable)) {
r = (*ih->ih_fun)(ih->ih_arg);
if (__predict_true(r != 0)) {
ih->ih_evcnt.ev_count++;
}
}
}
return 1;
}
return 0;
}