#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.35 2026/03/18 04:08:38 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
#include <sys/vmmeter.h>
#include <sys/queue.h>
#include <sys/device.h>
#include <sys/cpu.h>
#include <machine/intr.h>
#define AVEC_MIN 1
#define AVEC_MAX 7
#define AVEC_LOC 25
#define UVEC_MIN 0
#define UVEC_MAX 191
#define UVEC_LOC 64
typedef LIST_HEAD(, intrhand) ih_list_t;
static ih_list_t autovec_list[AVEC_MAX - AVEC_MIN + 1];
static ih_list_t uservec_list[UVEC_MAX - UVEC_MIN + 1];
volatile unsigned int intr_depth;
void
intr_init(void)
{
int i;
for (i = 0; i < (AVEC_MAX - AVEC_MIN + 1); ++i) {
LIST_INIT(&autovec_list[i]);
}
for (i = 0; i < (UVEC_MAX - UVEC_MIN + 1); ++i) {
LIST_INIT(&uservec_list[i]);
}
}
struct intrhand *
intr_establish(int vector, int type, int pri, hw_ifun_t ih_fun, void *ih_arg)
{
struct intrhand *ih, *cur_vec;
ih_list_t *vec_list;
u_long *hard_vec;
int s;
ih = kmem_alloc(sizeof *ih, KM_SLEEP);
ih->ih_fun = ih_fun;
ih->ih_arg = ih_arg;
ih->ih_type = type;
ih->ih_pri = pri;
ih->ih_vector = vector;
switch (type & (AUTO_VEC|USER_VEC)) {
case AUTO_VEC:
if (vector < AVEC_MIN || vector > AVEC_MAX) {
kmem_free(ih, sizeof(*ih));
return NULL;
}
vec_list = &autovec_list[vector - 1];
hard_vec = &autovects[vector - 1];
ih->ih_intrcnt = &intrcnt_auto[vector - 1];
break;
case USER_VEC:
if (vector < UVEC_MIN || vector > UVEC_MAX) {
kmem_free(ih, sizeof(*ih));
return NULL;
}
vec_list = &uservec_list[vector];
hard_vec = &uservects[vector];
ih->ih_intrcnt = &intrcnt_user[vector];
break;
default:
printf("%s: bogus vector type\n", __func__);
kmem_free(ih, sizeof(*ih));
return NULL;
}
if (LIST_EMPTY(vec_list)) {
s = splhigh();
LIST_INSERT_HEAD(vec_list, ih, ih_link);
if ((type & FAST_VEC) != 0)
*hard_vec = (u_long)ih->ih_fun;
else if (*hard_vec != (u_long)intr_glue) {
*hard_vec = (u_long)intr_glue;
}
splx(s);
return ih;
}
cur_vec = LIST_FIRST(vec_list);
if (cur_vec->ih_type & FAST_VEC) {
kmem_free(ih, sizeof(*ih));
printf("%s: vector cannot be shared\n", __func__);
return NULL;
}
for (cur_vec = LIST_FIRST(vec_list);
LIST_NEXT(cur_vec, ih_link) != NULL;
cur_vec = LIST_NEXT(cur_vec, ih_link)) {
if (ih->ih_pri > cur_vec->ih_pri) {
s = splhigh();
LIST_INSERT_BEFORE(cur_vec, ih, ih_link);
splx(s);
return ih;
}
}
s = splhigh();
LIST_INSERT_AFTER(cur_vec, ih, ih_link);
splx(s);
return ih;
}
int
intr_disestablish(struct intrhand *ih)
{
ih_list_t *vec_list;
u_long *hard_vec;
int vector, s;
struct intrhand *cur_vec;
vector = ih->ih_vector;
switch (ih->ih_type & (AUTO_VEC|USER_VEC)) {
case AUTO_VEC:
if (vector < AVEC_MIN || vector > AVEC_MAX)
return 0;
vec_list = &autovec_list[vector - 1];
hard_vec = &autovects[vector - 1];
break;
case USER_VEC:
if (vector < UVEC_MIN || vector > UVEC_MAX)
return 0;
vec_list = &uservec_list[vector];
hard_vec = &uservects[vector];
break;
default:
printf("%s: bogus vector type\n", __func__);
return 0;
}
for (cur_vec = LIST_FIRST(vec_list);
LIST_NEXT(cur_vec, ih_link) != NULL;
cur_vec = LIST_NEXT(cur_vec, ih_link)) {
if (ih == cur_vec)
break;
}
if (ih != cur_vec) {
printf("%s: 'ih' has inconsistent data\n", __func__);
return 0;
}
s = splhigh();
LIST_REMOVE(ih, ih_link);
if (LIST_EMPTY(vec_list) && (ih->ih_type & FAST_VEC) != 0)
*hard_vec = (u_long)intr_glue;
splx(s);
kmem_free(ih, sizeof(*ih));
return 1;
}
void
intr_dispatch(struct clockframe frame)
{
static int unexpected, straycount;
int vector;
int handled = 0;
ih_list_t *vec_list;
struct intrhand *ih;
curcpu()->ci_data.cpu_nintr++;
vector = (frame.cf_vo & 0xfff) >> 2;
if (vector < (AVEC_LOC + AVEC_MAX) && vector >= AVEC_LOC)
vec_list = &autovec_list[vector - AVEC_LOC];
else if (vector <= (UVEC_LOC + UVEC_MAX) && vector >= UVEC_LOC)
vec_list = &uservec_list[vector - UVEC_LOC];
else
panic("%s: Bogus vector %d", __func__, vector);
if ((ih = LIST_FIRST(vec_list)) == NULL) {
printf("%s: vector %d unexpected\n", __func__, vector);
if (++unexpected > 10)
panic("%s: too many unexpected interrupts", __func__);
return;
}
ih->ih_intrcnt[0]++;
for (; ih != NULL; ih = LIST_NEXT(ih, ih_link))
handled |= (*ih->ih_fun)((ih->ih_type & ARG_CLOCKFRAME) ?
&frame : ih->ih_arg, frame.cf_sr);
if (handled)
straycount = 0;
else if (++straycount > 50)
panic("%s: too many stray interrupts", __func__);
else
printf("%s: stray level %d interrupt\n", __func__, vector);
}
bool
cpu_intr_p(void)
{
return intr_depth != 0;
}
const uint16_t ipl2psl_table[NIPL] = {
[IPL_NONE] = PSL_S | PSL_IPL0,
[IPL_SOFTCLOCK] = PSL_S | PSL_IPL1,
[IPL_SOFTBIO] = PSL_S | PSL_IPL1,
[IPL_SOFTNET] = PSL_S | PSL_IPL1,
[IPL_SOFTSERIAL] = PSL_S | PSL_IPL1,
[IPL_VM] = PSL_S | PSL_IPL4,
[IPL_SCHED] = PSL_S | PSL_IPL6,
[IPL_HIGH] = PSL_S | PSL_IPL7,
};