Home | History | Annotate | Line # | Download | only in m68k
      1 /*	$NetBSD: m68k_intr.c,v 1.13 2024/01/19 20:55:42 thorpej Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1996, 2023, 2024 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by Adam Glass, Gordon W. Ross, and Jason R. Thorpe.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 /*
     33  * Common interrupt handling for m68k platforms.
     34  */
     35 
     36 #include <sys/cdefs.h>
     37 __KERNEL_RCSID(0, "$NetBSD: m68k_intr.c,v 1.13 2024/01/19 20:55:42 thorpej Exp $");
     38 
     39 #define	_M68K_INTR_PRIVATE
     40 
     41 #include <sys/param.h>
     42 #include <sys/systm.h>
     43 #include <sys/kmem.h>
     44 #include <sys/vmmeter.h>
     45 #include <sys/device.h>
     46 #include <sys/cpu.h>
     47 #include <sys/bus.h>
     48 #include <sys/once.h>
     49 #include <sys/intr.h>
     50 
     51 #include <machine/vectors.h>
     52 
     53 #include <uvm/uvm_extern.h>
     54 
     55 #ifdef __HAVE_M68K_INTR_VECTORED
     56 #ifndef MACHINE_USERVEC_START
     57 #define	MACHINE_USERVEC_START	VECI_USRVEC_START
     58 #endif
     59 
     60 #define	NVECHANDS	(NVECTORS - MACHINE_USERVEC_START)
     61 
     62 #ifndef INTR_FREEVEC
     63 #define	INTR_FREEVEC	badtrap
     64 #endif
     65 
     66 extern char INTR_FREEVEC[];
     67 extern char intrstub_vectored[];
     68 #endif /* __HAVE_M68K_INTR_VECTORED */
     69 
     70 /* A dummy event counter where interrupt stats go to die. */
     71 static struct evcnt bitbucket;
     72 
     73 volatile unsigned int intr_depth;	/* updated in assembly glue */
     74 
     75 static struct m68k_intrhand_list m68k_intrhands_autovec[NAUTOVECTORS];
     76 #ifdef __HAVE_M68K_INTR_VECTORED
     77 static struct m68k_intrhand *m68k_intrhands_vectored[NVECHANDS];
     78 #endif
     79 
     80 #ifndef __HAVE_LEGACY_INTRCNT
     81 #ifndef MACHINE_INTREVCNT_NAMES
     82 #define	MACHINE_INTREVCNT_NAMES						\
     83 	{ "spur", "lev1", "lev2", "lev3", "lev4", "lev5", "lev6", "nmi" }
     84 #endif
     85 static const char * const m68k_intr_evcnt_names[] = MACHINE_INTREVCNT_NAMES;
     86 __CTASSERT(__arraycount(m68k_intr_evcnt_names) == NAUTOVECTORS);
     87 static const char m68k_intr_evcnt_group[] = "cpu";
     88 
     89 #define	INTRCNT_INIT(x)							\
     90 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, m68k_intr_evcnt_group,	\
     91 			  m68k_intr_evcnt_names[(x)])
     92 
     93 struct evcnt m68k_intr_evcnt[] = {
     94 	INTRCNT_INIT(0), INTRCNT_INIT(1), INTRCNT_INIT(2), INTRCNT_INIT(3),
     95 	INTRCNT_INIT(4), INTRCNT_INIT(5), INTRCNT_INIT(6), INTRCNT_INIT(7),
     96 };
     97 __CTASSERT(__arraycount(m68k_intr_evcnt) == NAUTOVECTORS);
     98 
     99 #undef INTRCNT_INIT
    100 #endif /* __HAVE_LEGACY_INTRCNT */
    101 
    102 const uint16_t ipl2psl_table[NIPL] = {
    103 	[IPL_NONE]	 = PSL_S | PSL_IPL0,
    104 	[IPL_SOFTBIO]	 = PSL_S | MACHINE_PSL_IPL_SOFTBIO,
    105 	[IPL_SOFTCLOCK]	 = PSL_S | MACHINE_PSL_IPL_SOFTCLOCK,
    106 	[IPL_SOFTNET]	 = PSL_S | MACHINE_PSL_IPL_SOFTNET,
    107 	[IPL_SOFTSERIAL] = PSL_S | MACHINE_PSL_IPL_SOFTSERIAL,
    108 	[IPL_VM]	 = PSL_S | MACHINE_PSL_IPL_VM,
    109 	[IPL_SCHED]	 = PSL_S | MACHINE_PSL_IPL_SCHED,
    110 	[IPL_HIGH]	 = PSL_S | PSL_IPL7,
    111 };
    112 
    113 /*
    114  * m68k_spurintr --
    115  *	Interrupt handler for the "spurious interrupt" that comes in
    116  *	on auto-vector level 0.  All we do is claim it; it gets counted
    117  *	during the normal course of auto-vector interrupt handling.
    118  */
    119 static int
    120 m68k_spurintr(void *arg)
    121 {
    122 	return 1;
    123 }
    124 
    125 static struct m68k_intrhand m68k_spurintr_ih = {
    126 	.ih_func  = m68k_spurintr,
    127 	.ih_arg   = m68k_spurintr,
    128 	.ih_evcnt = &bitbucket,
    129 };
    130 
    131 static struct m68k_intrhand *
    132 m68k_ih_stdalloc(int km_flag)
    133 {
    134 	return kmem_zalloc(sizeof(struct m68k_intrhand), km_flag);
    135 }
    136 
    137 static void
    138 m68k_ih_stdfree(struct m68k_intrhand *ih)
    139 {
    140 	kmem_free(ih, sizeof(*ih));
    141 }
    142 
    143 static const struct m68k_ih_allocfuncs m68k_ih_stdallocfuncs = {
    144 	.alloc = m68k_ih_stdalloc,
    145 	.free  = m68k_ih_stdfree,
    146 };
    147 
    148 static const struct m68k_ih_allocfuncs *ih_allocfuncs;
    149 
    150 static struct m68k_intrhand *
    151 m68k_ih_alloc(void)
    152 {
    153 	KASSERT(ih_allocfuncs != NULL);
    154 	return ih_allocfuncs->alloc(KM_SLEEP);
    155 }
    156 
    157 static void
    158 m68k_ih_free(struct m68k_intrhand *ih)
    159 {
    160 	KASSERT(ih_allocfuncs != NULL);
    161 	if (__predict_true(ih != &m68k_spurintr_ih)) {
    162 		ih_allocfuncs->free(ih);
    163 	}
    164 }
    165 
    166 #ifdef __HAVE_M68K_INTR_VECTORED
    167 
    168 #define	INTRVEC_SLOT(vec)	\
    169 	(&m68k_intrhands_vectored[(vec) - MACHINE_USERVEC_START])
    170 
    171 #define	m68k_intrvec_handler(vec)	(*INTRVEC_SLOT(vec))
    172 
    173 static bool
    174 m68k_intrvec_add(struct m68k_intrhand *ih)
    175 {
    176 	if (ih->ih_vec < MACHINE_USERVEC_START || ih->ih_vec >= NVECTORS) {
    177 		aprint_error("%s: vector=0x%x (invalid)\n", __func__,
    178 		    ih->ih_vec);
    179 		return false;
    180 	}
    181 
    182 	struct m68k_intrhand **slot =
    183 	    &m68k_intrhands_vectored[ih->ih_vec - MACHINE_USERVEC_START];
    184 
    185 	if (*slot != NULL) {
    186 		aprint_error("%s: vector=0x%x (in use)\n", __func__,
    187 		    ih->ih_vec);
    188 		return false;
    189 	}
    190 
    191 	if (vec_get_entry(ih->ih_vec) != INTR_FREEVEC) {
    192 		aprint_error("%s: vector=0x%x (unavailable)\n", __func__,
    193 		    ih->ih_vec);
    194 		return false;
    195 	}
    196 
    197 	*slot = ih;
    198 	vec_set_entry(ih->ih_vec, intrstub_vectored);
    199 	return true;
    200 }
    201 
    202 static void
    203 m68k_intrvec_remove(struct m68k_intrhand *ih)
    204 {
    205 	KASSERT(ih->ih_vec >= MACHINE_USERVEC_START);
    206 	KASSERT(ih->ih_vec < NVECTORS);
    207 
    208 	struct m68k_intrhand **slot =
    209 	    &m68k_intrhands_vectored[ih->ih_vec - MACHINE_USERVEC_START];
    210 
    211 	KASSERT(*slot == ih);
    212 	KASSERT(vec_get_entry(ih->ih_vec) == intrstub_vectored);
    213 
    214 	vec_set_entry(ih->ih_vec, INTR_FREEVEC);
    215 	*slot = NULL;
    216 }
    217 
    218 /* XXX This is horrible and should burn to the ground. */
    219 void *
    220 m68k_intrvec_intrhand(int vec)
    221 {
    222 	KASSERT(vec >= MACHINE_USERVEC_START);
    223 	KASSERT(vec < NVECTORS);
    224 
    225 	return m68k_intrhands_vectored[vec - MACHINE_USERVEC_START];
    226 }
    227 
    228 #endif /* __HAVE_M68K_INTR_VECTORED */
    229 
    230 /*
    231  * m68k_intr_init --
    232  *	Initialize the interrupt subsystem.
    233  */
    234 void
    235 m68k_intr_init(const struct m68k_ih_allocfuncs *allocfuncs)
    236 {
    237 	int i;
    238 
    239 	KASSERT(ih_allocfuncs == NULL);
    240 	if (allocfuncs == NULL) {
    241 		ih_allocfuncs = &m68k_ih_stdallocfuncs;
    242 	} else {
    243 		ih_allocfuncs = allocfuncs;
    244 	}
    245 
    246 	for (i = 0; i < NAUTOVECTORS; i++) {
    247 		LIST_INIT(&m68k_intrhands_autovec[i]);
    248 #ifndef __HAVE_LEGACY_INTRCNT
    249 		evcnt_attach_static(&m68k_intr_evcnt[i]);
    250 #endif
    251 	}
    252 	LIST_INSERT_HEAD(&m68k_intrhands_autovec[0],
    253 	    &m68k_spurintr_ih, ih_link);
    254 }
    255 
    256 /*
    257  * m68k_intr_establish --
    258  *	Establish an interrupt handler at the specified vector.
    259  *	XXX We don't do anything with the flags yet.
    260  */
    261 void *
    262 m68k_intr_establish(int (*func)(void *), void *arg, struct evcnt *ev,
    263     int vec, int ipl, int isrpri, int flags __unused)
    264 {
    265 	struct m68k_intrhand *ih;
    266 	int s;
    267 
    268 	/*
    269 	 * If a platform doesn't want special behavior, we don't
    270 	 * require them to call m68k_intr_init(); we just handle
    271 	 * it here.
    272 	 *
    273 	 * XXX m68k_intr_init() might be called really early, so
    274 	 * XXX can't use a once control.
    275 	 */
    276 	if (__predict_false(ih_allocfuncs == NULL)) {
    277 		m68k_intr_init(NULL);
    278 	}
    279 
    280 	/* These are m68k IPLs, not IPL_* values. */
    281 	if (ipl < 0 || ipl > 7) {
    282 		return NULL;
    283 	}
    284 
    285 #ifdef __HAVE_M68K_INTR_VECTORED
    286 	KASSERT(vec >= 0);
    287 	KASSERT(vec < NVECTORS);
    288 #else
    289 	KASSERT(vec == 0);
    290 #endif /* __HAVE_M68K_INTR_VECTORED */
    291 
    292 	ih = m68k_ih_alloc();
    293 	ih->ih_func = func;
    294 	ih->ih_arg = arg;
    295 	ih->ih_vec = vec;
    296 	ih->ih_ipl = ipl;
    297 	ih->ih_isrpri = isrpri;
    298 	if ((ih->ih_evcnt = ev) == NULL) {
    299 		ih->ih_evcnt = &bitbucket;
    300 	}
    301 
    302 #ifdef __HAVE_M68K_INTR_VECTORED
    303 	if (vec != 0) {
    304 		if (vec_get_entry(vec) != INTR_FREEVEC) {
    305 			m68k_ih_free(ih);
    306 			return NULL;
    307 		}
    308 		if (! m68k_intrvec_add(ih)) {
    309 			m68k_ih_free(ih);
    310 			return NULL;
    311 		}
    312 		return ih;
    313 	}
    314 #endif
    315 
    316 	/*
    317 	 * Some devices are particularly sensitive to interrupt
    318 	 * handling latency.  Unbuffered serial ports, for example,
    319 	 * can lose data if their interrupts aren't handled with
    320 	 * reasonable speed.  For this reason, we sort interrupt
    321 	 * handlers by an abstract "ISR" priority, inserting higher-
    322 	 * priority interrupts before lower-priority interrupts.
    323 	 */
    324 	struct m68k_intrhand_list * const list = &m68k_intrhands_autovec[ipl];
    325 	struct m68k_intrhand *curih;
    326 
    327 	s = splhigh();
    328 	if (LIST_EMPTY(list)) {
    329 		LIST_INSERT_HEAD(list, ih, ih_link);
    330 		goto done;
    331 	}
    332 	for (curih = LIST_FIRST(list);
    333 	     LIST_NEXT(curih, ih_link) != NULL;
    334 	     curih = LIST_NEXT(curih, ih_link)) {
    335 		if (ih->ih_isrpri > curih->ih_isrpri) {
    336 			LIST_INSERT_BEFORE(curih, ih, ih_link);
    337 			goto done;
    338 		}
    339 	}
    340 	LIST_INSERT_AFTER(curih, ih, ih_link);
    341  done:
    342 	splx(s);
    343 
    344 	return ih;
    345 }
    346 
    347 /*
    348  * m68k_intr_disestablish --
    349  *	Remove an interrupt handler.  Returns true if the handler
    350  *	list for this vector is now empty.
    351  */
    352 bool
    353 m68k_intr_disestablish(void *v)
    354 {
    355 	struct m68k_intrhand *ih = v;
    356 	int s;
    357 	bool empty;
    358 
    359 #ifdef __HAVE_M68K_INTR_VECTORED
    360 	if (ih->ih_vec != 0) {
    361 		KASSERT(vec_get_entry(ih->ih_vec) == intrstub_vectored);
    362 		m68k_intrvec_remove(ih);
    363 		empty = true;
    364 	} else
    365 #endif
    366 	{
    367 		s = splhigh();
    368 		LIST_REMOVE(ih, ih_link);
    369 		empty = LIST_EMPTY(&m68k_intrhands_autovec[ih->ih_ipl]);
    370 		splx(s);
    371 	}
    372 
    373 	m68k_ih_free(ih);
    374 
    375 	return empty;
    376 }
    377 
    378 void	m68k_intr_autovec(struct clockframe);
    379 
    380 #ifndef MACHINE_AUTOVEC_IGNORE_STRAY
    381 #define	MACHINE_AUTOVEC_IGNORE_STRAY(ipl)	0
    382 #endif
    383 
    384 /*
    385  * m68k_intr_autovec --
    386  *	Run the interrupt handlers for an auto-vectored interrupt.
    387  *	Called from the assembly glue in m68k_intr_stubs.s
    388  */
    389 void
    390 m68k_intr_autovec(struct clockframe frame)
    391 {
    392 	const int ipl = VECO_TO_VECI(frame.cf_vo) - VECI_INTRAV0;
    393 	struct m68k_intrhand *ih;
    394 	bool rv = false;
    395 
    396 	m68k_count_intr(ipl);
    397 
    398 	LIST_FOREACH(ih, &m68k_intrhands_autovec[ipl], ih_link) {
    399 		void *arg = ih->ih_arg ? ih->ih_arg : &frame;
    400 		if (ih->ih_func(arg)) {
    401 			ih->ih_evcnt->ev_count++;
    402 			rv = true;
    403 		}
    404 	}
    405 	if (!rv && !MACHINE_AUTOVEC_IGNORE_STRAY(ipl)) {
    406 		printf("Stray level %d interrupt\n", ipl);
    407 	}
    408 
    409 	ATOMIC_CAS_CHECK(&frame);
    410 }
    411 
    412 #ifdef __HAVE_M68K_INTR_VECTORED
    413 void	m68k_intr_vectored(struct clockframe);
    414 
    415 /*
    416  * m68k_intr_vectored --
    417  *	Run a vectored interrupt handler.
    418  *	Called from the assembly glue in m68k_intr_stubs.s
    419  */
    420 void
    421 m68k_intr_vectored(struct clockframe frame)
    422 {
    423 	const int vec = VECO_TO_VECI(frame.cf_vo);
    424 	const int ipl = (getsr() >> 8) & 7;
    425 	struct m68k_intrhand *ih;
    426 
    427 	m68k_count_intr(ipl);
    428 
    429 #ifdef DIAGNOSTIC
    430 	if (vec < MACHINE_USERVEC_START || vec >= NVECTORS) {
    431 		printf("%s: vector=0x%x (invalid)\n", __func__, vec);
    432 		goto out;
    433 	}
    434 #endif
    435 	ih = m68k_intrvec_handler(vec);
    436 	if (ih == NULL) {
    437 		printf("%s: vector=0x%x (no handler?)\n", __func__, vec);
    438 		vec_set_entry(vec, INTR_FREEVEC);
    439 	}
    440 
    441 	if (__predict_true((*ih->ih_func)(ih->ih_arg ? ih->ih_arg
    442 						     : &frame) != 0)) {
    443 		ih->ih_evcnt->ev_count++;
    444 	} else {
    445 		printf("Stray level %d interrupt vector=0x%x\n",
    446 		    ipl, vec);
    447 	}
    448 #ifdef DIAGNOSTIC
    449  out:
    450 #endif
    451 	ATOMIC_CAS_CHECK(&frame);
    452 }
    453 #endif /* __HAVE_M68K_INTR_VECTORED */
    454