#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ar_intr.c,v 1.7 2021/01/04 17:42:29 thorpej Exp $");
#define __INTR_PRIVATE
#include <sys/param.h>
#include <sys/intr.h>
#include <sys/cpu.h>
#include <sys/kernel.h>
#include <sys/kmem.h>
#include <mips/cpuregs.h>
#include <mips/locore.h>
#include <mips/atheros/include/platform.h>
#define REGVAL(x) *((volatile uint32_t *)(MIPS_PHYS_TO_KSEG1((x))))
#define NINTRS 7
#define NIRQS 32
struct atheros_intrhand {
LIST_ENTRY(atheros_intrhand) ih_q;
int (*ih_func)(void *);
void *ih_arg;
int ih_irq;
};
struct atheros_intr {
LIST_HEAD(, atheros_intrhand) intr_qh;
struct evcnt intr_count;
};
static struct atheros_intr cpu_intrs[NINTRS];
static struct atheros_intr misc_intrs[NIRQS];
static uint32_t
misc_intstat_get(void)
{
return REGVAL(platformsw->apsw_misc_intstat);
}
static void
misc_intstat_put(uint32_t v)
{
REGVAL(platformsw->apsw_misc_intstat) = v;
}
static uint32_t
misc_intmask_get(void)
{
return REGVAL(platformsw->apsw_misc_intmask);
}
static void
misc_intmask_put(uint32_t v)
{
REGVAL(platformsw->apsw_misc_intmask) = v;
}
static void *
genath_cpu_intr_establish(int intr, int (*func)(void *), void *arg)
{
struct atheros_intrhand *ih;
ih = kmem_alloc(sizeof(*ih), KM_SLEEP);
ih->ih_func = func;
ih->ih_arg = arg;
ih->ih_irq = intr;
const int s = splhigh();
LIST_INSERT_HEAD(&cpu_intrs[intr].intr_qh, ih, ih_q);
splx(s);
return (ih);
}
static void
genath_cpu_intr_disestablish(void *arg)
{
struct atheros_intrhand * const ih = arg;
const int s = splhigh();
LIST_REMOVE(ih, ih_q);
splx(s);
kmem_free(ih, sizeof(*ih));
}
static void *
genath_misc_intr_establish(int irq, int (*func)(void *), void *arg)
{
struct atheros_intr * const intr = &misc_intrs[irq];
struct atheros_intrhand *ih;
bool first;
int s;
ih = kmem_alloc(sizeof(*ih), KM_SLEEP);
ih->ih_func = func;
ih->ih_arg = arg;
ih->ih_irq = irq;
s = splhigh();
first = LIST_EMPTY(&intr->intr_qh);
LIST_INSERT_HEAD(&intr->intr_qh, ih, ih_q);
if (first) {
const uint32_t mask = misc_intmask_get() | __BIT(irq);
misc_intmask_put(mask);
(void) misc_intmask_get();
}
splx(s);
return ih;
}
static void
genath_misc_intr_disestablish(void *arg)
{
struct atheros_intrhand *ih = arg;
struct atheros_intr * const intr = &misc_intrs[ih->ih_irq];
const int s = splhigh();
LIST_REMOVE(ih, ih_q);
if (LIST_EMPTY(&intr->intr_qh)) {
const uint32_t mask = misc_intmask_get() & ~__BIT(ih->ih_irq);
misc_intmask_put(mask);
(void) misc_intmask_get();
}
splx(s);
kmem_free(ih, sizeof(*ih));
}
static int
genath_misc_intr(void *arg)
{
uint32_t isr;
uint32_t mask;
int rv = 0;
struct atheros_intr *intr = arg;
isr = misc_intstat_get();
mask = misc_intmask_get();
misc_intstat_put(isr & ~mask);
isr &= mask;
while (isr != 0) {
struct atheros_intrhand *ih;
int index = 31 - __builtin_clz(isr & -isr);
intr += index;
intr->intr_count.ev_count++;
LIST_FOREACH(ih, &intr->intr_qh, ih_q) {
rv |= (*ih->ih_func)(ih->ih_arg);
}
isr >>= index + 1;
intr++;
}
return rv;
}
static void
genath_iointr(int cpl, vaddr_t pc, uint32_t ipending)
{
struct atheros_intr *intr = &cpu_intrs[NINTRS-1];
ipending *= __BIT(31) / (MIPS_INT_MASK_0 << (NINTRS-1));
while (ipending != 0) {
struct atheros_intrhand *ih;
int index = __builtin_clz(ipending);
intr -= index;
ipending <<= index;
KASSERT(ipending & __BIT(31));
KASSERT(intr >= cpu_intrs);
intr->intr_count.ev_count++;
LIST_FOREACH(ih, &intr->intr_qh, ih_q) {
(*ih->ih_func)(ih->ih_arg);
}
ipending <<= 1;
intr--;
}
}
static void
genath_intr_init(void)
{
const struct atheros_platformsw * const apsw = platformsw;
KASSERT(apsw->apsw_ipl_sr_map != NULL);
ipl_sr_map = *apsw->apsw_ipl_sr_map;
for (size_t i = 0; i < apsw->apsw_cpu_nintrs; i++) {
if (apsw->apsw_cpu_intrnames[i] != NULL) {
LIST_INIT(&cpu_intrs[i].intr_qh);
evcnt_attach_dynamic(&cpu_intrs[i].intr_count,
EVCNT_TYPE_INTR, NULL, "cpu",
apsw->apsw_cpu_intrnames[i]);
}
}
for (size_t i = 0; i < apsw->apsw_misc_nintrs; i++) {
if (apsw->apsw_misc_intrnames[i] != NULL) {
LIST_INIT(&misc_intrs[i].intr_qh);
evcnt_attach_dynamic(&misc_intrs[i].intr_count,
EVCNT_TYPE_INTR, NULL, "misc",
apsw->apsw_misc_intrnames[i]);
}
}
(void) misc_intstat_get();
misc_intmask_put(0);
misc_intstat_put(0);
genath_cpu_intr_establish(apsw->apsw_cpuirq_misc,
genath_misc_intr, misc_intrs);
}
const struct atheros_intrsw atheros_intrsw = {
.aisw_init = genath_intr_init,
.aisw_cpu_establish = genath_cpu_intr_establish,
.aisw_cpu_disestablish = genath_cpu_intr_disestablish,
.aisw_misc_establish = genath_misc_intr_establish,
.aisw_misc_disestablish = genath_misc_intr_disestablish,
.aisw_iointr = genath_iointr,
};