#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: softintr.c,v 1.18 2020/11/21 21:27:09 thorpej Exp $");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/syslog.h>
#include <sys/kmem.h>
#include <machine/cpu.h>
#include <arm/cpufunc.h>
#include <machine/intr.h>
void softintr_free(void *);
void softintr_dispatch(int);
struct softintr_handler {
struct softintr_handler *sh_vlink;
struct softintr_handler *sh_hlink;
void (*sh_fun)(void *);
void *sh_arg;
int sh_level;
int sh_pending;
};
struct softintr_handler *softintrs;
struct softintr_handler *softintr_pending;
void *
softintr_establish(int level, void (*fun)(void *), void *arg)
{
struct softintr_handler *sh;
sh = kmem_alloc(sizeof(*sh), KM_SLEEP);
sh->sh_fun = fun;
sh->sh_level = ipl_to_spl(level);
sh->sh_arg = arg;
sh->sh_pending = 0;
return sh;
}
void
softintr_disestablish(void *cookie)
{
struct softintr_handler *sh = cookie;
u_int saved_cpsr;
saved_cpsr = SetCPSR(I32_bit, I32_bit);
if (sh->sh_pending) {
sh->sh_fun = softintr_free;
sh->sh_arg = sh;
SetCPSR(I32_bit, I32_bit & saved_cpsr);
} else {
SetCPSR(I32_bit, I32_bit & saved_cpsr);
kmem_free(sh, sizeof(*sh));
}
}
void
softintr_free(void *arg)
{
struct softintr_handler *sh = arg;
kmem_free(sh, sizeof(*sh));
}
void
softintr_schedule(void *cookie)
{
struct softintr_handler **p, *sh = cookie;
register int pending, saved_cpsr;
pending = 1;
__asm("swp %0, %0, [%1]" : "+r" (pending) : "r" (&sh->sh_pending));
if (pending)
return;
sh->sh_vlink = NULL;
sh->sh_hlink = NULL;
#ifdef __GNUC__
__asm volatile("mrs %0, cpsr\n orr r1, %0, %1\n msr cpsr_a;;, r1" :
"=r" (saved_cpsr) : "i" (I32_bit) : "r1");
#else
saved_cpsr = SetCPSR(I32_bit, I32_bit);
#endif
p = &softintr_pending;
for (;; p = &(*p)->sh_vlink) {
if (*p == NULL)
goto set_and_exit;
if ((*p)->sh_level <= sh->sh_level)
break;
}
if ((*p)->sh_level == sh->sh_level) {
sh->sh_hlink = *p;
sh->sh_vlink = (*p)->sh_vlink;
goto set_and_exit;
}
sh->sh_vlink = *p;
set_and_exit:
*p = sh;
#ifdef __GNUC__
__asm volatile("msr cpsr_c, %0" : : "r" (saved_cpsr));
#else
SetCPSR(I32_bit, I32_bit & saved_cpsr);
#endif
return;
}
void
softintr_dispatch(int s)
{
struct softintr_handler *sh, *sh1;
register int saved_cpsr;
while (1) {
#ifdef __GNUC__
__asm volatile("mrs %0, cpsr\n orr r1, %0, %1\n"
" msr cpsr_all, r1" : "=r" (saved_cpsr) :
"i" (I32_bit) : "r1");
#else
saved_cpsr = SetCPSR(I32_bit, I32_bit);
#endif
if (softintr_pending == NULL ||
softintr_pending->sh_level <= s) {
#ifdef __GNUC__
__asm("msr cpsr_c, %0" : : "r" (saved_cpsr));
#else
SetCPSR(I32_bit, I32_bit & saved_cpsr);
#endif
splx(s);
return;
}
sh = softintr_pending;
softintr_pending = softintr_pending->sh_vlink;
if (sh->sh_level > current_spl_level)
raisespl(sh->sh_level);
#ifdef __GNUC__
__asm volatile("msr cpsr_c, %0" : : "r" (saved_cpsr));
#else
SetCPSR(I32_bit, I32_bit & saved_cpsr);
#endif
if (sh->sh_level < current_spl_level)
lowerspl(sh->sh_level);
while (1) {
sh1 = sh->sh_hlink;
sh->sh_pending = 0;
(sh->sh_fun)(sh->sh_arg);
if (sh1 == NULL)
break;
sh = sh1;
}
}
splx(s);
}