#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: m68k_intr.c,v 1.13 2024/01/19 20:55:42 thorpej Exp $");
#define _M68K_INTR_PRIVATE
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kmem.h>
#include <sys/vmmeter.h>
#include <sys/device.h>
#include <sys/cpu.h>
#include <sys/bus.h>
#include <sys/once.h>
#include <sys/intr.h>
#include <machine/vectors.h>
#include <uvm/uvm_extern.h>
#ifdef __HAVE_M68K_INTR_VECTORED
#ifndef MACHINE_USERVEC_START
#define MACHINE_USERVEC_START VECI_USRVEC_START
#endif
#define NVECHANDS (NVECTORS - MACHINE_USERVEC_START)
#ifndef INTR_FREEVEC
#define INTR_FREEVEC badtrap
#endif
extern char INTR_FREEVEC[];
extern char intrstub_vectored[];
#endif
static struct evcnt bitbucket;
volatile unsigned int intr_depth;
static struct m68k_intrhand_list m68k_intrhands_autovec[NAUTOVECTORS];
#ifdef __HAVE_M68K_INTR_VECTORED
static struct m68k_intrhand *m68k_intrhands_vectored[NVECHANDS];
#endif
#ifndef __HAVE_LEGACY_INTRCNT
#ifndef MACHINE_INTREVCNT_NAMES
#define MACHINE_INTREVCNT_NAMES \
{ "spur", "lev1", "lev2", "lev3", "lev4", "lev5", "lev6", "nmi" }
#endif
static const char * const m68k_intr_evcnt_names[] = MACHINE_INTREVCNT_NAMES;
__CTASSERT(__arraycount(m68k_intr_evcnt_names) == NAUTOVECTORS);
static const char m68k_intr_evcnt_group[] = "cpu";
#define INTRCNT_INIT(x) \
EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, m68k_intr_evcnt_group, \
m68k_intr_evcnt_names[(x)])
struct evcnt m68k_intr_evcnt[] = {
INTRCNT_INIT(0), INTRCNT_INIT(1), INTRCNT_INIT(2), INTRCNT_INIT(3),
INTRCNT_INIT(4), INTRCNT_INIT(5), INTRCNT_INIT(6), INTRCNT_INIT(7),
};
__CTASSERT(__arraycount(m68k_intr_evcnt) == NAUTOVECTORS);
#undef INTRCNT_INIT
#endif
const uint16_t ipl2psl_table[NIPL] = {
[IPL_NONE] = PSL_S | PSL_IPL0,
[IPL_SOFTBIO] = PSL_S | MACHINE_PSL_IPL_SOFTBIO,
[IPL_SOFTCLOCK] = PSL_S | MACHINE_PSL_IPL_SOFTCLOCK,
[IPL_SOFTNET] = PSL_S | MACHINE_PSL_IPL_SOFTNET,
[IPL_SOFTSERIAL] = PSL_S | MACHINE_PSL_IPL_SOFTSERIAL,
[IPL_VM] = PSL_S | MACHINE_PSL_IPL_VM,
[IPL_SCHED] = PSL_S | MACHINE_PSL_IPL_SCHED,
[IPL_HIGH] = PSL_S | PSL_IPL7,
};
static int
m68k_spurintr(void *arg)
{
return 1;
}
static struct m68k_intrhand m68k_spurintr_ih = {
.ih_func = m68k_spurintr,
.ih_arg = m68k_spurintr,
.ih_evcnt = &bitbucket,
};
static struct m68k_intrhand *
m68k_ih_stdalloc(int km_flag)
{
return kmem_zalloc(sizeof(struct m68k_intrhand), km_flag);
}
static void
m68k_ih_stdfree(struct m68k_intrhand *ih)
{
kmem_free(ih, sizeof(*ih));
}
static const struct m68k_ih_allocfuncs m68k_ih_stdallocfuncs = {
.alloc = m68k_ih_stdalloc,
.free = m68k_ih_stdfree,
};
static const struct m68k_ih_allocfuncs *ih_allocfuncs;
static struct m68k_intrhand *
m68k_ih_alloc(void)
{
KASSERT(ih_allocfuncs != NULL);
return ih_allocfuncs->alloc(KM_SLEEP);
}
static void
m68k_ih_free(struct m68k_intrhand *ih)
{
KASSERT(ih_allocfuncs != NULL);
if (__predict_true(ih != &m68k_spurintr_ih)) {
ih_allocfuncs->free(ih);
}
}
#ifdef __HAVE_M68K_INTR_VECTORED
#define INTRVEC_SLOT(vec) \
(&m68k_intrhands_vectored[(vec) - MACHINE_USERVEC_START])
#define m68k_intrvec_handler(vec) (*INTRVEC_SLOT(vec))
static bool
m68k_intrvec_add(struct m68k_intrhand *ih)
{
if (ih->ih_vec < MACHINE_USERVEC_START || ih->ih_vec >= NVECTORS) {
aprint_error("%s: vector=0x%x (invalid)\n", __func__,
ih->ih_vec);
return false;
}
struct m68k_intrhand **slot =
&m68k_intrhands_vectored[ih->ih_vec - MACHINE_USERVEC_START];
if (*slot != NULL) {
aprint_error("%s: vector=0x%x (in use)\n", __func__,
ih->ih_vec);
return false;
}
if (vec_get_entry(ih->ih_vec) != INTR_FREEVEC) {
aprint_error("%s: vector=0x%x (unavailable)\n", __func__,
ih->ih_vec);
return false;
}
*slot = ih;
vec_set_entry(ih->ih_vec, intrstub_vectored);
return true;
}
static void
m68k_intrvec_remove(struct m68k_intrhand *ih)
{
KASSERT(ih->ih_vec >= MACHINE_USERVEC_START);
KASSERT(ih->ih_vec < NVECTORS);
struct m68k_intrhand **slot =
&m68k_intrhands_vectored[ih->ih_vec - MACHINE_USERVEC_START];
KASSERT(*slot == ih);
KASSERT(vec_get_entry(ih->ih_vec) == intrstub_vectored);
vec_set_entry(ih->ih_vec, INTR_FREEVEC);
*slot = NULL;
}
void *
m68k_intrvec_intrhand(int vec)
{
KASSERT(vec >= MACHINE_USERVEC_START);
KASSERT(vec < NVECTORS);
return m68k_intrhands_vectored[vec - MACHINE_USERVEC_START];
}
#endif
void
m68k_intr_init(const struct m68k_ih_allocfuncs *allocfuncs)
{
int i;
KASSERT(ih_allocfuncs == NULL);
if (allocfuncs == NULL) {
ih_allocfuncs = &m68k_ih_stdallocfuncs;
} else {
ih_allocfuncs = allocfuncs;
}
for (i = 0; i < NAUTOVECTORS; i++) {
LIST_INIT(&m68k_intrhands_autovec[i]);
#ifndef __HAVE_LEGACY_INTRCNT
evcnt_attach_static(&m68k_intr_evcnt[i]);
#endif
}
LIST_INSERT_HEAD(&m68k_intrhands_autovec[0],
&m68k_spurintr_ih, ih_link);
}
void *
m68k_intr_establish(int (*func)(void *), void *arg, struct evcnt *ev,
int vec, int ipl, int isrpri, int flags __unused)
{
struct m68k_intrhand *ih;
int s;
if (__predict_false(ih_allocfuncs == NULL)) {
m68k_intr_init(NULL);
}
if (ipl < 0 || ipl > 7) {
return NULL;
}
#ifdef __HAVE_M68K_INTR_VECTORED
KASSERT(vec >= 0);
KASSERT(vec < NVECTORS);
#else
KASSERT(vec == 0);
#endif
ih = m68k_ih_alloc();
ih->ih_func = func;
ih->ih_arg = arg;
ih->ih_vec = vec;
ih->ih_ipl = ipl;
ih->ih_isrpri = isrpri;
if ((ih->ih_evcnt = ev) == NULL) {
ih->ih_evcnt = &bitbucket;
}
#ifdef __HAVE_M68K_INTR_VECTORED
if (vec != 0) {
if (vec_get_entry(vec) != INTR_FREEVEC) {
m68k_ih_free(ih);
return NULL;
}
if (! m68k_intrvec_add(ih)) {
m68k_ih_free(ih);
return NULL;
}
return ih;
}
#endif
struct m68k_intrhand_list * const list = &m68k_intrhands_autovec[ipl];
struct m68k_intrhand *curih;
s = splhigh();
if (LIST_EMPTY(list)) {
LIST_INSERT_HEAD(list, ih, ih_link);
goto done;
}
for (curih = LIST_FIRST(list);
LIST_NEXT(curih, ih_link) != NULL;
curih = LIST_NEXT(curih, ih_link)) {
if (ih->ih_isrpri > curih->ih_isrpri) {
LIST_INSERT_BEFORE(curih, ih, ih_link);
goto done;
}
}
LIST_INSERT_AFTER(curih, ih, ih_link);
done:
splx(s);
return ih;
}
bool
m68k_intr_disestablish(void *v)
{
struct m68k_intrhand *ih = v;
int s;
bool empty;
#ifdef __HAVE_M68K_INTR_VECTORED
if (ih->ih_vec != 0) {
KASSERT(vec_get_entry(ih->ih_vec) == intrstub_vectored);
m68k_intrvec_remove(ih);
empty = true;
} else
#endif
{
s = splhigh();
LIST_REMOVE(ih, ih_link);
empty = LIST_EMPTY(&m68k_intrhands_autovec[ih->ih_ipl]);
splx(s);
}
m68k_ih_free(ih);
return empty;
}
void m68k_intr_autovec(struct clockframe);
#ifndef MACHINE_AUTOVEC_IGNORE_STRAY
#define MACHINE_AUTOVEC_IGNORE_STRAY(ipl) 0
#endif
void
m68k_intr_autovec(struct clockframe frame)
{
const int ipl = VECO_TO_VECI(frame.cf_vo) - VECI_INTRAV0;
struct m68k_intrhand *ih;
bool rv = false;
m68k_count_intr(ipl);
LIST_FOREACH(ih, &m68k_intrhands_autovec[ipl], ih_link) {
void *arg = ih->ih_arg ? ih->ih_arg : &frame;
if (ih->ih_func(arg)) {
ih->ih_evcnt->ev_count++;
rv = true;
}
}
if (!rv && !MACHINE_AUTOVEC_IGNORE_STRAY(ipl)) {
printf("Stray level %d interrupt\n", ipl);
}
ATOMIC_CAS_CHECK(&frame);
}
#ifdef __HAVE_M68K_INTR_VECTORED
void m68k_intr_vectored(struct clockframe);
void
m68k_intr_vectored(struct clockframe frame)
{
const int vec = VECO_TO_VECI(frame.cf_vo);
const int ipl = (getsr() >> 8) & 7;
struct m68k_intrhand *ih;
m68k_count_intr(ipl);
#ifdef DIAGNOSTIC
if (vec < MACHINE_USERVEC_START || vec >= NVECTORS) {
printf("%s: vector=0x%x (invalid)\n", __func__, vec);
goto out;
}
#endif
ih = m68k_intrvec_handler(vec);
if (ih == NULL) {
printf("%s: vector=0x%x (no handler?)\n", __func__, vec);
vec_set_entry(vec, INTR_FREEVEC);
}
if (__predict_true((*ih->ih_func)(ih->ih_arg ? ih->ih_arg
: &frame) != 0)) {
ih->ih_evcnt->ev_count++;
} else {
printf("Stray level %d interrupt vector=0x%x\n",
ipl, vec);
}
#ifdef DIAGNOSTIC
out:
#endif
ATOMIC_CAS_CHECK(&frame);
}
#endif