#include <sys/types.h>
#include <sys/cpuvar.h>
#include <sys/ivintr.h>
#include <sys/intreg.h>
#include <sys/cmn_err.h>
#include <sys/privregs.h>
#include <sys/sunddi.h>
static kmutex_t intr_vec_mutex;
static kmutex_t softint_mutex;
intr_vec_t *softint_list = NULL;
intr_vec_t *intr_vec_pool = NULL;
intr_vecx_t *intr_vecx_pool = NULL;
static kmutex_t intr_vec_pool_mutex;
kmem_cache_t *intr_vec_cache = NULL;
static kmutex_t intr_vec_cache_mutex;
void
init_ivintr()
{
mutex_init(&intr_vec_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&softint_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&intr_vec_pool_mutex, NULL, MUTEX_DRIVER, NULL);
mutex_init(&intr_vec_cache_mutex, NULL, MUTEX_DRIVER, NULL);
intr_vec_pool = (intr_vec_t *)((caddr_t)intr_vec_table +
(MAXIVNUM * sizeof (intr_vec_t *)));
intr_vecx_pool = (intr_vecx_t *)((caddr_t)intr_vec_pool +
(MAX_RSVD_IV * sizeof (intr_vec_t)));
bzero(intr_vec_table, MAXIVNUM * sizeof (intr_vec_t *));
bzero(intr_vec_pool, MAX_RSVD_IV * sizeof (intr_vec_t));
bzero(intr_vecx_pool, MAX_RSVD_IVX * sizeof (intr_vecx_t));
}
void
fini_ivintr()
{
mutex_enter(&intr_vec_cache_mutex);
if (intr_vec_cache) {
kmem_cache_destroy(intr_vec_cache);
intr_vec_cache = NULL;
}
mutex_exit(&intr_vec_cache_mutex);
mutex_destroy(&intr_vec_pool_mutex);
mutex_destroy(&softint_mutex);
mutex_destroy(&intr_vec_mutex);
mutex_destroy(&intr_vec_cache_mutex);
}
static intr_vec_t *
iv_alloc(softint_type_t type)
{
intr_vec_t *iv_p;
int i, count;
count = (type == SOFTINT_MT) ? MAX_RSVD_IVX : MAX_RSVD_IV;
mutex_enter(&intr_vec_pool_mutex);
for (i = 0; i < count; i++) {
iv_p = (type == SOFTINT_MT) ?
(intr_vec_t *)&intr_vecx_pool[i] : &intr_vec_pool[i];
if (iv_p->iv_pil == 0) {
iv_p->iv_pil = 1;
break;
}
}
mutex_exit(&intr_vec_pool_mutex);
if (i < count)
return (iv_p);
if (type == SOFTINT_MT)
cmn_err(CE_PANIC, "iv_alloc: exceeded number of multi "
"target software interrupts, %d", MAX_RSVD_IVX);
mutex_enter(&intr_vec_cache_mutex);
if (intr_vec_cache == NULL)
intr_vec_cache = kmem_cache_create("intr_vec_cache",
sizeof (intr_vec_t), 64, NULL, NULL, NULL, NULL, NULL, 0);
mutex_exit(&intr_vec_cache_mutex);
iv_p = kmem_cache_alloc(intr_vec_cache, KM_SLEEP);
bzero(iv_p, sizeof (intr_vec_t));
iv_p->iv_flags = IV_CACHE_ALLOC;
return (iv_p);
}
static void
iv_free(intr_vec_t *iv_p)
{
if (iv_p->iv_flags & IV_CACHE_ALLOC) {
ASSERT(!(iv_p->iv_flags & IV_SOFTINT_MT));
kmem_cache_free(intr_vec_cache, iv_p);
} else {
mutex_enter(&intr_vec_pool_mutex);
bzero(iv_p, (iv_p->iv_flags & IV_SOFTINT_MT) ?
sizeof (intr_vecx_t) : sizeof (intr_vec_t));
mutex_exit(&intr_vec_pool_mutex);
}
}
int
add_ivintr(uint_t inum, uint_t pil, intrfunc intr_handler,
caddr_t intr_arg1, caddr_t intr_arg2, caddr_t intr_payload)
{
intr_vec_t *iv_p, *new_iv_p;
if (inum >= MAXIVNUM || pil > PIL_MAX)
return (EINVAL);
ASSERT((uintptr_t)intr_handler > KERNELBASE);
VERIFY(((uint64_t)intr_payload & 0x7) == 0);
new_iv_p = iv_alloc(SOFTINT_ST);
mutex_enter(&intr_vec_mutex);
for (iv_p = (intr_vec_t *)intr_vec_table[inum];
iv_p; iv_p = iv_p->iv_vec_next) {
if (iv_p->iv_pil == pil) {
mutex_exit(&intr_vec_mutex);
iv_free(new_iv_p);
return (EINVAL);
}
}
ASSERT(iv_p == NULL);
new_iv_p->iv_handler = intr_handler;
new_iv_p->iv_arg1 = intr_arg1;
new_iv_p->iv_arg2 = intr_arg2;
new_iv_p->iv_payload_buf = intr_payload;
new_iv_p->iv_pil = (ushort_t)pil;
new_iv_p->iv_inum = inum;
new_iv_p->iv_vec_next = (intr_vec_t *)intr_vec_table[inum];
intr_vec_table[inum] = (uint64_t)new_iv_p;
mutex_exit(&intr_vec_mutex);
return (0);
}
int
rem_ivintr(uint_t inum, uint_t pil)
{
intr_vec_t *iv_p, *prev_iv_p;
if (inum >= MAXIVNUM || pil > PIL_MAX)
return (EINVAL);
mutex_enter(&intr_vec_mutex);
for (iv_p = prev_iv_p = (intr_vec_t *)intr_vec_table[inum];
iv_p; prev_iv_p = iv_p, iv_p = iv_p->iv_vec_next)
if (iv_p->iv_pil == pil)
break;
if (iv_p == NULL) {
mutex_exit(&intr_vec_mutex);
return (EIO);
}
ASSERT(iv_p->iv_pil_next == NULL);
if (prev_iv_p == iv_p)
intr_vec_table[inum] = (uint64_t)iv_p->iv_vec_next;
else
prev_iv_p->iv_vec_next = iv_p->iv_vec_next;
mutex_exit(&intr_vec_mutex);
iv_free(iv_p);
return (0);
}
uint64_t
add_softintr(uint_t pil, softintrfunc intr_handler, caddr_t intr_arg1,
softint_type_t type)
{
intr_vec_t *iv_p;
if (pil > PIL_MAX)
return ((uint64_t)NULL);
iv_p = iv_alloc(type);
iv_p->iv_handler = (intrfunc)intr_handler;
iv_p->iv_arg1 = intr_arg1;
iv_p->iv_pil = (ushort_t)pil;
if (type == SOFTINT_MT)
iv_p->iv_flags |= IV_SOFTINT_MT;
mutex_enter(&softint_mutex);
if (softint_list)
iv_p->iv_vec_next = softint_list;
softint_list = iv_p;
mutex_exit(&softint_mutex);
return ((uint64_t)iv_p);
}
int
rem_softintr(uint64_t softint_id)
{
intr_vec_t *iv_p = (intr_vec_t *)softint_id;
ASSERT(iv_p != NULL);
if (iv_p->iv_flags & IV_SOFTINT_PEND)
return (EIO);
ASSERT(iv_p->iv_pil_next == NULL);
mutex_enter(&softint_mutex);
if (softint_list == iv_p) {
softint_list = iv_p->iv_vec_next;
} else {
intr_vec_t *list = softint_list;
while (list && (list->iv_vec_next != iv_p))
list = list->iv_vec_next;
list->iv_vec_next = iv_p->iv_vec_next;
}
mutex_exit(&softint_mutex);
iv_free(iv_p);
return (0);
}
int
update_softint_arg2(uint64_t softint_id, caddr_t intr_arg2)
{
intr_vec_t *iv_p = (intr_vec_t *)softint_id;
ASSERT(iv_p != NULL);
if (iv_p->iv_flags & IV_SOFTINT_PEND)
return (EIO);
iv_p->iv_arg2 = intr_arg2;
return (0);
}
int
update_softint_pri(uint64_t softint_id, uint_t pil)
{
intr_vec_t *iv_p = (intr_vec_t *)softint_id;
ASSERT(iv_p != NULL);
if (pil > PIL_MAX)
return (EINVAL);
iv_p->iv_pil = pil;
return (0);
}