#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: becc_icu.c,v 1.15 2020/11/20 18:49:45 thorpej Exp $");
#ifndef EVBARM_SPL_NOINLINE
#define EVBARM_SPL_NOINLINE
#endif
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kmem.h>
#include <sys/bus.h>
#include <sys/intr.h>
#include <uvm/uvm_extern.h>
#include <arm/cpufunc.h>
#include <arm/xscale/beccreg.h>
#include <arm/xscale/beccvar.h>
#include <arm/xscale/i80200reg.h>
#include <arm/xscale/i80200var.h>
struct intrq intrq[NIRQ];
uint32_t becc_imask[NIPL];
volatile uint32_t becc_ipending;
volatile uint32_t becc_sipending;
volatile uint32_t intr_enabled;
uint32_t intr_steer;
const char * const becc_irqnames[] = {
"soft",
"timer A",
"timer B",
"irq 3",
"irq 4",
"irq 5",
"irq 6",
"diagerr",
"DMA EOT",
"DMA PERR",
"DMA TABT",
"DMA MABT",
"irq 12",
"irq 13",
"irq 14",
"irq 15",
"PCI PERR",
"irq 17",
"irq 18",
"PCI SERR",
"PCI OAPE",
"PCI OATA",
"PCI OAMA",
"irq 23",
"irq 24",
"irq 25",
"irq 26",
"irq 27",
"irq 28",
"irq 29",
"pushbutton",
"irq 31",
};
void becc_intr_dispatch(struct trapframe *frame);
static inline uint32_t
becc_icsr_read(void)
{
uint32_t icsr;
icsr = BECC_CSR_READ(BECC_ICSR);
return (icsr & intr_enabled);
}
static inline void
becc_set_intrsteer(void)
{
BECC_CSR_WRITE(BECC_ICSTR, intr_steer & ICU_VALID_MASK);
(void) BECC_CSR_READ(BECC_ICSTR);
}
static inline void
becc_enable_irq(int irq)
{
intr_enabled |= (1U << irq);
becc_set_intrmask();
}
static inline void
becc_disable_irq(int irq)
{
intr_enabled &= ~(1U << irq);
becc_set_intrmask();
}
static void
becc_intr_calculate_masks(void)
{
struct intrq *iq;
struct intrhand *ih;
int irq, ipl;
for (irq = 0; irq < NIRQ; irq++) {
int levels = 0;
iq = &intrq[irq];
becc_disable_irq(irq);
for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
ih = TAILQ_NEXT(ih, ih_list))
levels |= (1U << ih->ih_ipl);
iq->iq_levels = levels;
}
for (ipl = 0; ipl < NIPL; ipl++) {
int irqs = 0;
for (irq = 0; irq < NIRQ; irq++) {
if (intrq[irq].iq_levels & (1U << ipl))
irqs |= (1U << irq);
}
becc_imask[ipl] = irqs;
}
becc_imask[IPL_NONE] = 0;
becc_imask[IPL_VM] |= becc_imask[IPL_SOFTSERIAL];
becc_imask[IPL_SCHED] |= becc_imask[IPL_VM];
becc_imask[IPL_HIGH] |= becc_imask[IPL_SCHED];
for (irq = 0; irq < NIRQ; irq++) {
int irqs = (1U << irq);
iq = &intrq[irq];
if (TAILQ_FIRST(&iq->iq_list) != NULL)
becc_enable_irq(irq);
for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
ih = TAILQ_NEXT(ih, ih_list))
irqs |= becc_imask[ih->ih_ipl];
iq->iq_mask = irqs;
}
}
void
splx(int new)
{
becc_splx(new);
}
int
_spllower(int ipl)
{
return (becc_spllower(ipl));
}
int
_splraise(int ipl)
{
return (becc_splraise(ipl));
}
void
becc_icu_init(void)
{
intr_enabled = 0;
becc_set_intrmask();
intr_steer = 0;
becc_set_intrsteer();
i80200_extirq_dispatch = becc_intr_dispatch;
i80200_intr_enable(INTCTL_IM);
}
void
becc_intr_init(void)
{
struct intrq *iq;
int i;
intr_enabled = 0;
for (i = 0; i < NIRQ; i++) {
iq = &intrq[i];
TAILQ_INIT(&iq->iq_list);
evcnt_attach_dynamic(&iq->iq_ev, EVCNT_TYPE_INTR,
NULL, "becc", becc_irqnames[i]);
}
becc_intr_calculate_masks();
enable_interrupts(I32_bit);
}
void *
becc_intr_establish(int irq, int ipl, int (*func)(void *), void *arg)
{
struct intrq *iq;
struct intrhand *ih;
uint32_t oldirqstate;
if (irq < 0 || irq > NIRQ)
panic("becc_intr_establish: IRQ %d out of range", irq);
ih = kmem_alloc(sizeof(*ih), KM_SLEEP);
ih->ih_func = func;
ih->ih_arg = arg;
ih->ih_ipl = ipl;
ih->ih_irq = irq;
iq = &intrq[irq];
iq->iq_ist = IST_LEVEL;
oldirqstate = disable_interrupts(I32_bit);
TAILQ_INSERT_TAIL(&iq->iq_list, ih, ih_list);
becc_intr_calculate_masks();
restore_interrupts(oldirqstate);
return (ih);
}
void
becc_intr_disestablish(void *cookie)
{
struct intrhand *ih = cookie;
struct intrq *iq = &intrq[ih->ih_irq];
uint32_t oldirqstate;
oldirqstate = disable_interrupts(I32_bit);
TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
becc_intr_calculate_masks();
restore_interrupts(oldirqstate);
}
void
becc_intr_dispatch(struct trapframe *frame)
{
struct intrq *iq;
struct intrhand *ih;
uint32_t oldirqstate, irq, ibit, hwpend;
struct cpu_info * const ci = curcpu();
const int ppl = ci->ci_cpl;
const uint32_t imask = becc_imask[ppl];
hwpend = becc_icsr_read();
intr_enabled &= ~hwpend;
becc_set_intrmask();
while (hwpend != 0) {
irq = ffs(hwpend) - 1;
ibit = (1U << irq);
hwpend &= ~ibit;
if (imask & ibit) {
becc_ipending |= ibit;
continue;
}
becc_ipending &= ~ibit;
iq = &intrq[irq];
iq->iq_ev.ev_count++;
ci->ci_data.cpu_nintr++;
TAILQ_FOREACH(ih, &iq->iq_list, ih_list) {
ci->ci_cpl = ih->ih_ipl;
oldirqstate = enable_interrupts(I32_bit);
(void) (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame);
restore_interrupts(oldirqstate);
}
ci->ci_cpl = ppl;
intr_enabled |= ibit;
becc_set_intrmask();
}
if (becc_ipending & ~imask) {
intr_enabled |= (becc_ipending & ~imask);
becc_set_intrmask();
}
#ifdef __HAVE_FAST_SOFTINTS
cpu_dosoftints();
#endif
}