#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 <arm/cpufunc.h>
#include <evbarm/ifpga/ifpgareg.h>
#include <evbarm/ifpga/ifpgavar.h>
struct intrq intrq[NIRQ];
int ifpga_imask[NIPL];
volatile int ifpga_ipending;
volatile uint32_t intr_enabled;
uint32_t intr_steer;
const char * const ifpga_irqnames[] = {
"soft",
"uart 0",
"uart 1",
"kbd",
"mouse",
"tmr 0",
"tmr 1 hard",
"tmr 2 stat",
"rtc",
"exp 0",
"exp 1",
"exp 2",
"exp 3",
"pci 0",
"pci 1",
"pci 2",
"pci 3",
"V3 br",
"deg",
"enum",
"pci lb",
"autoPC",
"irq 22",
"mmc 0",
"mmc 1",
"irq 25",
"irq 26",
"irq 27",
"irq 28",
"irq 29",
"irq 30",
"irq 31",
};
void ifpga_intr_dispatch(struct clockframe *frame);
extern struct ifpga_softc *ifpga_sc;
static inline uint32_t
ifpga_iintsrc_read(void)
{
return bus_space_read_4(ifpga_sc->sc_iot, ifpga_sc->sc_irq_ioh,
IFPGA_INTR_STATUS);
}
static inline void
ifpga_enable_irq(int irq)
{
intr_enabled |= (1U << irq);
ifpga_set_intrmask();
}
static inline void
ifpga_disable_irq(int irq)
{
intr_enabled &= ~(1U << irq);
ifpga_set_intrmask();
}
static void
ifpga_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];
ifpga_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);
}
ifpga_imask[ipl] = irqs;
}
KASSERT(ifpga_imask[IPL_NONE] == 0);
ifpga_imask[IPL_VM] |= 0;
ifpga_imask[IPL_SCHED] |= ifpga_imask[IPL_VM];
ifpga_imask[IPL_HIGH] |= ifpga_imask[IPL_SCHED];
for (irq = 0; irq < NIRQ; irq++) {
int irqs = (1U << irq);
iq = &intrq[irq];
if (TAILQ_FIRST(&iq->iq_list) != NULL)
ifpga_enable_irq(irq);
for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
ih = TAILQ_NEXT(ih, ih_list))
irqs |= ifpga_imask[ih->ih_ipl];
iq->iq_mask = irqs;
}
}
void
splx(int new)
{
ifpga_splx(new);
}
int
_spllower(int ipl)
{
return (ifpga_spllower(ipl));
}
int
_splraise(int ipl)
{
return (ifpga_splraise(ipl));
}
void
ifpga_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, "ifpga", ifpga_irqnames[i]);
}
}
void
ifpga_intr_postinit(void)
{
ifpga_intr_calculate_masks();
enable_interrupts(I32_bit);
}
void *
ifpga_intr_establish(int irq, int ipl, int (*func)(void *), void *arg)
{
struct intrq *iq;
struct intrhand *ih;
u_int oldirqstate;
if (irq < 0 || irq > NIRQ)
panic("ifpga_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);
ifpga_intr_calculate_masks();
restore_interrupts(oldirqstate);
return (ih);
}
void
ifpga_intr_disestablish(void *cookie)
{
struct intrhand *ih = cookie;
struct intrq *iq = &intrq[ih->ih_irq];
int oldirqstate;
oldirqstate = disable_interrupts(I32_bit);
TAILQ_REMOVE(&iq->iq_list, ih, ih_list);
ifpga_intr_calculate_masks();
restore_interrupts(oldirqstate);
}
void
ifpga_intr_dispatch(struct clockframe *frame)
{
struct intrq *iq;
struct intrhand *ih;
int oldirqstate, pcpl, irq, ibit, hwpend;
struct cpu_info * const ci = curcpu();
pcpl = ci->ci_cpl;
hwpend = ifpga_iintsrc_read();
intr_enabled &= ~hwpend;
ifpga_set_intrmask();
while ((ifpga_iintsrc_read() & hwpend) != 0)
;
while (hwpend != 0) {
irq = ffs(hwpend) - 1;
ibit = (1U << irq);
hwpend &= ~ibit;
if (pcpl & ibit) {
ifpga_ipending |= ibit;
continue;
}
ifpga_ipending &= ~ibit;
iq = &intrq[irq];
iq->iq_ev.ev_count++;
ci->ci_data.cpu_nintr++;
ci->ci_cpl |= iq->iq_mask;
oldirqstate = enable_interrupts(I32_bit);
for (ih = TAILQ_FIRST(&iq->iq_list); ih != NULL;
ih = TAILQ_NEXT(ih, ih_list)) {
(void) (*ih->ih_func)(ih->ih_arg ? ih->ih_arg : frame);
}
restore_interrupts(oldirqstate);
ci->ci_cpl = pcpl;
hwpend |= (ifpga_ipending & IFPGA_INTR_HWMASK) & ~pcpl;
intr_enabled |= ibit;
ifpga_set_intrmask();
}
#ifdef __HAVE_FAST_SOFTINTS
cpu_dosoftints();
#endif
}