/* $NetBSD: m68k_intr.c,v 1.13 2024/01/19 20:55:42 thorpej Exp $ */ /*- * Copyright (c) 1996, 2023, 2024 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Adam Glass, Gordon W. Ross, and Jason R. Thorpe. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * Common interrupt handling for m68k platforms. */ #include __KERNEL_RCSID(0, "$NetBSD: m68k_intr.c,v 1.13 2024/01/19 20:55:42 thorpej Exp $"); #define _M68K_INTR_PRIVATE #include #include #include #include #include #include #include #include #include #include #include #ifdef __HAVE_M68K_INTR_VECTORED #ifndef MACHINE_USERVEC_START #define MACHINE_USERVEC_START VECI_USRVEC_START #endif #define NVECHANDS (NVECTORS - MACHINE_USERVEC_START) #ifndef INTR_FREEVEC #define INTR_FREEVEC badtrap #endif extern char INTR_FREEVEC[]; extern char intrstub_vectored[]; #endif /* __HAVE_M68K_INTR_VECTORED */ /* A dummy event counter where interrupt stats go to die. */ static struct evcnt bitbucket; volatile unsigned int intr_depth; /* updated in assembly glue */ static struct m68k_intrhand_list m68k_intrhands_autovec[NAUTOVECTORS]; #ifdef __HAVE_M68K_INTR_VECTORED static struct m68k_intrhand *m68k_intrhands_vectored[NVECHANDS]; #endif #ifndef __HAVE_LEGACY_INTRCNT #ifndef MACHINE_INTREVCNT_NAMES #define MACHINE_INTREVCNT_NAMES \ { "spur", "lev1", "lev2", "lev3", "lev4", "lev5", "lev6", "nmi" } #endif static const char * const m68k_intr_evcnt_names[] = MACHINE_INTREVCNT_NAMES; __CTASSERT(__arraycount(m68k_intr_evcnt_names) == NAUTOVECTORS); static const char m68k_intr_evcnt_group[] = "cpu"; #define INTRCNT_INIT(x) \ EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, m68k_intr_evcnt_group, \ m68k_intr_evcnt_names[(x)]) struct evcnt m68k_intr_evcnt[] = { INTRCNT_INIT(0), INTRCNT_INIT(1), INTRCNT_INIT(2), INTRCNT_INIT(3), INTRCNT_INIT(4), INTRCNT_INIT(5), INTRCNT_INIT(6), INTRCNT_INIT(7), }; __CTASSERT(__arraycount(m68k_intr_evcnt) == NAUTOVECTORS); #undef INTRCNT_INIT #endif /* __HAVE_LEGACY_INTRCNT */ const uint16_t ipl2psl_table[NIPL] = { [IPL_NONE] = PSL_S | PSL_IPL0, [IPL_SOFTBIO] = PSL_S | MACHINE_PSL_IPL_SOFTBIO, [IPL_SOFTCLOCK] = PSL_S | MACHINE_PSL_IPL_SOFTCLOCK, [IPL_SOFTNET] = PSL_S | MACHINE_PSL_IPL_SOFTNET, [IPL_SOFTSERIAL] = PSL_S | MACHINE_PSL_IPL_SOFTSERIAL, [IPL_VM] = PSL_S | MACHINE_PSL_IPL_VM, [IPL_SCHED] = PSL_S | MACHINE_PSL_IPL_SCHED, [IPL_HIGH] = PSL_S | PSL_IPL7, }; /* * m68k_spurintr -- * Interrupt handler for the "spurious interrupt" that comes in * on auto-vector level 0. All we do is claim it; it gets counted * during the normal course of auto-vector interrupt handling. */ static int m68k_spurintr(void *arg) { return 1; } static struct m68k_intrhand m68k_spurintr_ih = { .ih_func = m68k_spurintr, .ih_arg = m68k_spurintr, .ih_evcnt = &bitbucket, }; static struct m68k_intrhand * m68k_ih_stdalloc(int km_flag) { return kmem_zalloc(sizeof(struct m68k_intrhand), km_flag); } static void m68k_ih_stdfree(struct m68k_intrhand *ih) { kmem_free(ih, sizeof(*ih)); } static const struct m68k_ih_allocfuncs m68k_ih_stdallocfuncs = { .alloc = m68k_ih_stdalloc, .free = m68k_ih_stdfree, }; static const struct m68k_ih_allocfuncs *ih_allocfuncs; static struct m68k_intrhand * m68k_ih_alloc(void) { KASSERT(ih_allocfuncs != NULL); return ih_allocfuncs->alloc(KM_SLEEP); } static void m68k_ih_free(struct m68k_intrhand *ih) { KASSERT(ih_allocfuncs != NULL); if (__predict_true(ih != &m68k_spurintr_ih)) { ih_allocfuncs->free(ih); } } #ifdef __HAVE_M68K_INTR_VECTORED #define INTRVEC_SLOT(vec) \ (&m68k_intrhands_vectored[(vec) - MACHINE_USERVEC_START]) #define m68k_intrvec_handler(vec) (*INTRVEC_SLOT(vec)) static bool m68k_intrvec_add(struct m68k_intrhand *ih) { if (ih->ih_vec < MACHINE_USERVEC_START || ih->ih_vec >= NVECTORS) { aprint_error("%s: vector=0x%x (invalid)\n", __func__, ih->ih_vec); return false; } struct m68k_intrhand **slot = &m68k_intrhands_vectored[ih->ih_vec - MACHINE_USERVEC_START]; if (*slot != NULL) { aprint_error("%s: vector=0x%x (in use)\n", __func__, ih->ih_vec); return false; } if (vec_get_entry(ih->ih_vec) != INTR_FREEVEC) { aprint_error("%s: vector=0x%x (unavailable)\n", __func__, ih->ih_vec); return false; } *slot = ih; vec_set_entry(ih->ih_vec, intrstub_vectored); return true; } static void m68k_intrvec_remove(struct m68k_intrhand *ih) { KASSERT(ih->ih_vec >= MACHINE_USERVEC_START); KASSERT(ih->ih_vec < NVECTORS); struct m68k_intrhand **slot = &m68k_intrhands_vectored[ih->ih_vec - MACHINE_USERVEC_START]; KASSERT(*slot == ih); KASSERT(vec_get_entry(ih->ih_vec) == intrstub_vectored); vec_set_entry(ih->ih_vec, INTR_FREEVEC); *slot = NULL; } /* XXX This is horrible and should burn to the ground. */ void * m68k_intrvec_intrhand(int vec) { KASSERT(vec >= MACHINE_USERVEC_START); KASSERT(vec < NVECTORS); return m68k_intrhands_vectored[vec - MACHINE_USERVEC_START]; } #endif /* __HAVE_M68K_INTR_VECTORED */ /* * m68k_intr_init -- * Initialize the interrupt subsystem. */ void m68k_intr_init(const struct m68k_ih_allocfuncs *allocfuncs) { int i; KASSERT(ih_allocfuncs == NULL); if (allocfuncs == NULL) { ih_allocfuncs = &m68k_ih_stdallocfuncs; } else { ih_allocfuncs = allocfuncs; } for (i = 0; i < NAUTOVECTORS; i++) { LIST_INIT(&m68k_intrhands_autovec[i]); #ifndef __HAVE_LEGACY_INTRCNT evcnt_attach_static(&m68k_intr_evcnt[i]); #endif } LIST_INSERT_HEAD(&m68k_intrhands_autovec[0], &m68k_spurintr_ih, ih_link); } /* * m68k_intr_establish -- * Establish an interrupt handler at the specified vector. * XXX We don't do anything with the flags yet. */ void * m68k_intr_establish(int (*func)(void *), void *arg, struct evcnt *ev, int vec, int ipl, int isrpri, int flags __unused) { struct m68k_intrhand *ih; int s; /* * If a platform doesn't want special behavior, we don't * require them to call m68k_intr_init(); we just handle * it here. * * XXX m68k_intr_init() might be called really early, so * XXX can't use a once control. */ if (__predict_false(ih_allocfuncs == NULL)) { m68k_intr_init(NULL); } /* These are m68k IPLs, not IPL_* values. */ if (ipl < 0 || ipl > 7) { return NULL; } #ifdef __HAVE_M68K_INTR_VECTORED KASSERT(vec >= 0); KASSERT(vec < NVECTORS); #else KASSERT(vec == 0); #endif /* __HAVE_M68K_INTR_VECTORED */ ih = m68k_ih_alloc(); ih->ih_func = func; ih->ih_arg = arg; ih->ih_vec = vec; ih->ih_ipl = ipl; ih->ih_isrpri = isrpri; if ((ih->ih_evcnt = ev) == NULL) { ih->ih_evcnt = &bitbucket; } #ifdef __HAVE_M68K_INTR_VECTORED if (vec != 0) { if (vec_get_entry(vec) != INTR_FREEVEC) { m68k_ih_free(ih); return NULL; } if (! m68k_intrvec_add(ih)) { m68k_ih_free(ih); return NULL; } return ih; } #endif /* * Some devices are particularly sensitive to interrupt * handling latency. Unbuffered serial ports, for example, * can lose data if their interrupts aren't handled with * reasonable speed. For this reason, we sort interrupt * handlers by an abstract "ISR" priority, inserting higher- * priority interrupts before lower-priority interrupts. */ struct m68k_intrhand_list * const list = &m68k_intrhands_autovec[ipl]; struct m68k_intrhand *curih; s = splhigh(); if (LIST_EMPTY(list)) { LIST_INSERT_HEAD(list, ih, ih_link); goto done; } for (curih = LIST_FIRST(list); LIST_NEXT(curih, ih_link) != NULL; curih = LIST_NEXT(curih, ih_link)) { if (ih->ih_isrpri > curih->ih_isrpri) { LIST_INSERT_BEFORE(curih, ih, ih_link); goto done; } } LIST_INSERT_AFTER(curih, ih, ih_link); done: splx(s); return ih; } /* * m68k_intr_disestablish -- * Remove an interrupt handler. Returns true if the handler * list for this vector is now empty. */ bool m68k_intr_disestablish(void *v) { struct m68k_intrhand *ih = v; int s; bool empty; #ifdef __HAVE_M68K_INTR_VECTORED if (ih->ih_vec != 0) { KASSERT(vec_get_entry(ih->ih_vec) == intrstub_vectored); m68k_intrvec_remove(ih); empty = true; } else #endif { s = splhigh(); LIST_REMOVE(ih, ih_link); empty = LIST_EMPTY(&m68k_intrhands_autovec[ih->ih_ipl]); splx(s); } m68k_ih_free(ih); return empty; } void m68k_intr_autovec(struct clockframe); #ifndef MACHINE_AUTOVEC_IGNORE_STRAY #define MACHINE_AUTOVEC_IGNORE_STRAY(ipl) 0 #endif /* * m68k_intr_autovec -- * Run the interrupt handlers for an auto-vectored interrupt. * Called from the assembly glue in m68k_intr_stubs.s */ void m68k_intr_autovec(struct clockframe frame) { const int ipl = VECO_TO_VECI(frame.cf_vo) - VECI_INTRAV0; struct m68k_intrhand *ih; bool rv = false; m68k_count_intr(ipl); LIST_FOREACH(ih, &m68k_intrhands_autovec[ipl], ih_link) { void *arg = ih->ih_arg ? ih->ih_arg : &frame; if (ih->ih_func(arg)) { ih->ih_evcnt->ev_count++; rv = true; } } if (!rv && !MACHINE_AUTOVEC_IGNORE_STRAY(ipl)) { printf("Stray level %d interrupt\n", ipl); } ATOMIC_CAS_CHECK(&frame); } #ifdef __HAVE_M68K_INTR_VECTORED void m68k_intr_vectored(struct clockframe); /* * m68k_intr_vectored -- * Run a vectored interrupt handler. * Called from the assembly glue in m68k_intr_stubs.s */ void m68k_intr_vectored(struct clockframe frame) { const int vec = VECO_TO_VECI(frame.cf_vo); const int ipl = (getsr() >> 8) & 7; struct m68k_intrhand *ih; m68k_count_intr(ipl); #ifdef DIAGNOSTIC if (vec < MACHINE_USERVEC_START || vec >= NVECTORS) { printf("%s: vector=0x%x (invalid)\n", __func__, vec); goto out; } #endif ih = m68k_intrvec_handler(vec); if (ih == NULL) { printf("%s: vector=0x%x (no handler?)\n", __func__, vec); vec_set_entry(vec, INTR_FREEVEC); } if (__predict_true((*ih->ih_func)(ih->ih_arg ? ih->ih_arg : &frame) != 0)) { ih->ih_evcnt->ev_count++; } else { printf("Stray level %d interrupt vector=0x%x\n", ipl, vec); } #ifdef DIAGNOSTIC out: #endif ATOMIC_CAS_CHECK(&frame); } #endif /* __HAVE_M68K_INTR_VECTORED */