#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.9 2025/12/10 21:33:02 andvar Exp $");
#define __MUTEX_PRIVATE
#include <sys/param.h>
#include <sys/cpu.h>
#include <uvm/uvm_extern.h>
#include <machine/autoconf.h>
#include <machine/cpufunc.h>
#include <machine/intr.h>
#include <machine/reg.h>
#include <hppa/hppa/machdep.h>
#include <machine/mutex.h>
#if defined(_KERNEL_OPT)
#include "opt_lockdebug.h"
#endif
static int hppa_intr_ipl_next(struct cpu_info *);
void hppa_intr_calculatemasks(struct cpu_info *);
int hppa_intr_ipending(struct hppa_interrupt_register *, int);
void hppa_intr_dispatch(int , int , struct trapframe *);
struct hppa_interrupt_register *hppa_interrupt_registers[HPPA_INTERRUPT_BITS];
void
hppa_interrupt_register_establish(struct cpu_info *ci,
struct hppa_interrupt_register *ir)
{
int idx;
memset(ir, 0, sizeof(*ir));
ir->ir_ci = ci;
for (idx = 0; idx < HPPA_INTERRUPT_BITS; idx++)
ir->ir_bits_map[idx] = IR_BIT_UNUSED;
ir->ir_bits = ~0;
for (idx = 0; idx < HPPA_INTERRUPT_BITS; idx++)
if (hppa_interrupt_registers[idx] == NULL)
break;
if (idx == HPPA_INTERRUPT_BITS)
panic("%s: too many regs", __func__);
hppa_interrupt_registers[idx] = ir;
}
void
hppa_intr_initialise(struct cpu_info *ci)
{
int i;
for (i = 0; i < NIPL; i++)
ci->ci_imask[i] = -1;
ci->ci_cpl = -1;
ci->ci_ipending = 0;
ci->ci_intr_depth = 0;
memset(ci->ci_ib, 0, sizeof(ci->ci_ib));
memset(hppa_interrupt_registers, 0, sizeof(hppa_interrupt_registers));
}
void *
hppa_intr_establish(int ipl, int (*handler)(void *), void *arg,
struct hppa_interrupt_register *ir, int bit_pos)
{
struct hppa_interrupt_bit *ib;
struct cpu_info *ci = ir->ir_ci;
int idx;
if (bit_pos < 0 || bit_pos >= HPPA_INTERRUPT_BITS)
panic("%s: bad interrupt bit %d", __func__, bit_pos);
if (handler != NULL) {
if (IR_BIT_USED_P(ir->ir_bits_map[31 ^ bit_pos]))
panic("%s: interrupt already handled", __func__);
}
if (handler == NULL) {
for (idx = 1; idx < HPPA_INTERRUPT_BITS; idx++)
if (hppa_interrupt_registers[idx] == arg)
break;
if (idx == HPPA_INTERRUPT_BITS)
panic("%s: unknown int reg", __func__);
ir->ir_bits_map[31 ^ bit_pos] = IR_BIT_REG(idx);
return NULL;
}
idx = hppa_intr_ipl_next(ir->ir_ci);
ir->ir_bits &= ~(1 << bit_pos);
ir->ir_rbits &= ~(1 << bit_pos);
if (!IR_BIT_USED_P(ir->ir_bits_map[31 ^ bit_pos])) {
ir->ir_bits_map[31 ^ bit_pos] = 1 << idx;
} else {
int j;
ir->ir_bits_map[31 ^ bit_pos] |= 1 << idx;
j = (ir - hppa_interrupt_registers[0]);
ci->ci_ishared |= (1 << j);
}
ib = &ci->ci_ib[idx];
ib->ib_reg = ir;
ib->ib_ipl = ipl;
ib->ib_spl = (1 << idx);
snprintf(ib->ib_name, sizeof(ib->ib_name), "irq %d", bit_pos);
evcnt_attach_dynamic(&ib->ib_evcnt, EVCNT_TYPE_INTR, NULL, ir->ir_name,
ib->ib_name);
ib->ib_handler = handler;
ib->ib_arg = arg;
hppa_intr_calculatemasks(ci);
return ib;
}
int
hppa_intr_allocate_bit(struct hppa_interrupt_register *ir, int irq)
{
int bit_pos;
int last_bit;
u_int mask;
int *bits;
if (irq == -1) {
bit_pos = 31;
last_bit = 0;
bits = &ir->ir_bits;
} else {
bit_pos = irq;
last_bit = irq;
bits = &ir->ir_rbits;
}
for (mask = (1 << bit_pos); bit_pos >= last_bit; bit_pos--) {
if (*bits & mask)
break;
mask >>= 1;
}
if (bit_pos >= last_bit) {
*bits &= ~mask;
return bit_pos;
}
return -1;
}
static int
hppa_intr_ipl_next(struct cpu_info *ci)
{
int idx;
for (idx = 0; idx < HPPA_INTERRUPT_BITS; idx++)
if (ci->ci_ib[idx].ib_reg == NULL)
break;
if (idx == HPPA_INTERRUPT_BITS)
panic("%s: too many devices", __func__);
return idx;
}
void
hppa_intr_calculatemasks(struct cpu_info *ci)
{
struct hppa_interrupt_bit *ib;
struct hppa_interrupt_register *ir;
int idx, bit_pos;
int mask;
int ipl;
memset(ci->ci_imask, 0, sizeof(ci->ci_imask));
for (bit_pos = 0; bit_pos < HPPA_INTERRUPT_BITS; bit_pos++) {
ib = &ci->ci_ib[bit_pos];
if (ib->ib_reg == NULL)
continue;
ci->ci_imask[ib->ib_ipl] |= ib->ib_spl;
}
ci->ci_imask[IPL_NONE] = 0;
for (ipl = NIPL - 1; ipl > 0; ipl--)
ci->ci_imask[ipl - 1] |= ci->ci_imask[ipl];
KASSERT(ci->ci_cpl == -1);
KASSERT(ci->ci_ipending == 0);
for (idx = 0; idx < HPPA_INTERRUPT_BITS; idx++) {
ir = hppa_interrupt_registers[idx];
if (ir == NULL || ir->ir_ci != ci)
continue;
mask = 0;
for (bit_pos = 0; bit_pos < HPPA_INTERRUPT_BITS; bit_pos++) {
if (!IR_BIT_UNUSED_P(ir->ir_bits_map[31 ^ bit_pos]))
mask |= (1 << bit_pos);
}
if (ir->ir_iscpu)
ir->ir_ci->ci_eiem = mask;
else if (ir->ir_mask != NULL)
*ir->ir_mask = mask;
}
}
void
hppa_intr_enable(void)
{
struct cpu_info *ci = curcpu();
mtctl(ci->ci_eiem, CR_EIEM);
ci->ci_psw |= PSW_I;
hppa_enable_irq();
}
void
hppa_intr(struct trapframe *frame)
{
struct cpu_info *ci = curcpu();
int eirr;
int i;
#ifndef LOCKDEBUG
extern char mutex_enter_crit_start[];
extern char mutex_enter_crit_end[];
#ifndef MULTIPROCESSOR
extern char _lock_cas_ras_start[];
extern char _lock_cas_ras_end[];
if (frame->tf_iisq_head == HPPA_SID_KERNEL &&
frame->tf_iioq_head > (u_int)_lock_cas_ras_start &&
frame->tf_iioq_head < (u_int)_lock_cas_ras_end) {
frame->tf_iioq_head = (u_int)_lock_cas_ras_start;
frame->tf_iioq_tail = (u_int)_lock_cas_ras_start + 4;
}
#endif
if (frame->tf_iisq_head == HPPA_SID_KERNEL &&
frame->tf_iioq_head > (u_int)mutex_enter_crit_start &&
frame->tf_iioq_head < (u_int)mutex_enter_crit_end &&
frame->tf_ret0 != 0)
((kmutex_t *)frame->tf_arg0)->mtx_owner = (uintptr_t)curlwp;
#endif
mfctl(CR_EIRR, eirr);
mtctl(eirr, CR_EIRR);
ci->ci_ipending |= hppa_intr_ipending(&ci->ci_ir, eirr);
i = 0;
while (ci->ci_ipending & ~ci->ci_cpl) {
int shared;
hppa_intr_dispatch(ci->ci_cpl, frame->tf_eiem, frame);
shared = ci->ci_ishared;
while (shared) {
struct hppa_interrupt_register *sir;
int sbit, lvl;
sbit = ffs(shared) - 1;
sir = hppa_interrupt_registers[sbit];
lvl = *sir->ir_level;
ci->ci_ipending |= hppa_intr_ipending(sir, lvl);
shared &= ~(1 << sbit);
}
i++;
KASSERTMSG(i <= 2,
"%s: ci->ipending %08x ci->ci_cpl %08x shared %08x\n",
__func__, ci->ci_ipending, ci->ci_cpl, shared);
}
}
void
hppa_intr_dispatch(int ncpl, int eiem, struct trapframe *frame)
{
struct cpu_info *ci = curcpu();
struct hppa_interrupt_bit *ib;
struct clockframe clkframe;
int ipending_run;
int bit_pos;
void *arg;
int handled __unused;
bool locked = false;
ci->ci_intr_depth++;
for (;;) {
ipending_run = (ci->ci_ipending & ~ncpl);
if (ipending_run == 0)
break;
bit_pos = ffs(ipending_run) - 1;
ib = &ci->ci_ib[bit_pos];
ib->ib_evcnt.ev_count++;
arg = ib->ib_arg;
if (arg == NULL) {
clkframe.cf_flags = (ci->ci_intr_depth > 1 ?
TFF_INTR : 0);
clkframe.cf_spl = ncpl;
if (frame != NULL) {
clkframe.cf_flags |= frame->tf_flags;
clkframe.cf_pc = frame->tf_iioq_head;
}
arg = &clkframe;
}
ci->ci_ipending &= ~(1 << bit_pos);
ci->ci_cpl = ncpl | ci->ci_imask[ib->ib_ipl];
mtctl(eiem, CR_EIEM);
if (ib->ib_ipl == IPL_VM) {
KERNEL_LOCK(1, NULL);
locked = true;
}
ci->ci_data.cpu_nintr++;
handled = (*ib->ib_handler)(arg);
#if 0
if (!handled)
printf("%s: can't handle interrupt\n",
ib->ib_evcnt.ev_name);
#endif
if (locked) {
KERNEL_UNLOCK_ONE(NULL);
locked = false;
}
mtctl(0, CR_EIEM);
}
ci->ci_cpl = ncpl;
ci->ci_intr_depth--;
}
int
hppa_intr_ipending(struct hppa_interrupt_register *ir, int eirr)
{
int pending = 0;
int idx;
for (idx = 31; idx >= 0; idx--) {
if ((eirr & (1 << idx)) == 0)
continue;
if (IR_BIT_NESTED_P(ir->ir_bits_map[31 ^ idx])) {
struct hppa_interrupt_register *nir;
int reg = ir->ir_bits_map[31 ^ idx] & ~IR_BIT_MASK;
nir = hppa_interrupt_registers[reg];
pending |= hppa_intr_ipending(nir, *(nir->ir_req));
} else {
pending |= ir->ir_bits_map[31 ^ idx];
}
}
return pending;
}
bool
cpu_intr_p(void)
{
struct cpu_info *ci = curcpu();
#ifdef __HAVE_FAST_SOFTINTS
#error this should not count fast soft interrupts
#else
return ci->ci_intr_depth != 0;
#endif
}