Home | History | Annotate | Line # | Download | only in atari
      1 /*	$NetBSD: intr.c,v 1.35 2026/03/18 04:08:38 thorpej Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 1996 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, Jason R. Thorpe, and Leo Weppelman.
      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 #include <sys/cdefs.h>
     33 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.35 2026/03/18 04:08:38 thorpej Exp $");
     34 
     35 #include <sys/param.h>
     36 #include <sys/systm.h>
     37 #include <sys/kernel.h>
     38 #include <sys/kmem.h>
     39 #include <sys/vmmeter.h>
     40 #include <sys/queue.h>
     41 #include <sys/device.h>
     42 #include <sys/cpu.h>
     43 
     44 #include <machine/intr.h>
     45 
     46 #define	AVEC_MIN	1
     47 #define	AVEC_MAX	7
     48 #define	AVEC_LOC	25
     49 #define	UVEC_MIN	0
     50 #define	UVEC_MAX	191
     51 #define	UVEC_LOC	64
     52 
     53 typedef LIST_HEAD(, intrhand) ih_list_t;
     54 static ih_list_t autovec_list[AVEC_MAX - AVEC_MIN + 1];
     55 static ih_list_t uservec_list[UVEC_MAX - UVEC_MIN + 1];
     56 volatile unsigned int intr_depth;
     57 
     58 void
     59 intr_init(void)
     60 {
     61 	int i;
     62 
     63 	for (i = 0; i < (AVEC_MAX - AVEC_MIN + 1); ++i) {
     64 		LIST_INIT(&autovec_list[i]);
     65 	}
     66 	for (i = 0; i < (UVEC_MAX - UVEC_MIN + 1); ++i) {
     67 		LIST_INIT(&uservec_list[i]);
     68 	}
     69 }
     70 
     71 /*
     72  * Establish an interrupt vector.
     73  *   - vector
     74  *	The vector number the interrupt should be hooked on. It can either
     75  *	be an auto-vector or a user-vector.
     76  *   - type
     77  *	A bit-wise of:
     78  *		- AUTO_VEC (mutually exclusive with USER_VEC)
     79  *			Attach to one of the 7 auto vectors
     80  *		- USER_VEC (mutually exclusive with AUTO_VEC)
     81  *			Attach to one of the 192 user vectors
     82  *		- FAST_VEC
     83  *			The interrupt function 'ih_fun' will be
     84  *			put into the 'real' interrupt table. This
     85  *			means:
     86  *				- This vector can't be shared
     87  *				- 'ih_fun' must save registers
     88  *				- 'ih_fun' must do its own interrupt accounting
     89  *				- The argument to 'ih_fun' is a standard
     90  *				  interrupt frame.
     91  *		- ARG_CLOCKRAME
     92  *			The 'ih_fun' function will be called with
     93  *			a standard clock-frame instead of 'ih_arg'.
     94  *
     95  *   - pri
     96  *	When multiple interrupts are established on the same vector,
     97  *	interrupts with the highest priority will be called first. The
     98  *	basic ordering is the order of establishment.
     99  *   - ih_fun
    100  *	The interrupt function to be called
    101  *   - ih_arg
    102  *	The argument given to 'ih_fun' when ARG_CLOCKFRAME is not
    103  *	specified.
    104  */
    105 
    106 struct intrhand *
    107 intr_establish(int vector, int type, int pri, hw_ifun_t ih_fun, void *ih_arg)
    108 {
    109 	struct intrhand	*ih, *cur_vec;
    110 	ih_list_t	*vec_list;
    111 	u_long		*hard_vec;
    112 	int		s;
    113 
    114 	ih = kmem_alloc(sizeof *ih, KM_SLEEP);
    115 	ih->ih_fun    = ih_fun;
    116 	ih->ih_arg    = ih_arg;
    117 	ih->ih_type   = type;
    118 	ih->ih_pri    = pri;
    119 	ih->ih_vector = vector;
    120 
    121 	/*
    122 	 * Do some validity checking on the 'vector' argument and determine
    123 	 * vector list this interrupt should be on.
    124 	 */
    125 	switch (type & (AUTO_VEC|USER_VEC)) {
    126 	case AUTO_VEC:
    127 		if (vector < AVEC_MIN || vector > AVEC_MAX) {
    128 			kmem_free(ih, sizeof(*ih));
    129 			return NULL;
    130 		}
    131 		vec_list = &autovec_list[vector - 1];
    132 		hard_vec = &autovects[vector - 1];
    133 		ih->ih_intrcnt = &intrcnt_auto[vector - 1];
    134 		break;
    135 	case USER_VEC:
    136 		if (vector < UVEC_MIN || vector > UVEC_MAX) {
    137 			kmem_free(ih, sizeof(*ih));
    138 			return NULL;
    139 		}
    140 		vec_list = &uservec_list[vector];
    141 		hard_vec = &uservects[vector];
    142 		ih->ih_intrcnt = &intrcnt_user[vector];
    143 		break;
    144 	default:
    145 		printf("%s: bogus vector type\n", __func__);
    146 		kmem_free(ih, sizeof(*ih));
    147 		return NULL;
    148 	}
    149 
    150 	/*
    151 	 * If the vec_list is empty, we insert ourselves at the head of the
    152 	 * list and we re-route the 'hard-vector' to the appropriate handler.
    153 	 */
    154 	if (LIST_EMPTY(vec_list)) {
    155 
    156 		s = splhigh();
    157 		LIST_INSERT_HEAD(vec_list, ih, ih_link);
    158 		if ((type & FAST_VEC) != 0)
    159 			*hard_vec = (u_long)ih->ih_fun;
    160 		else if (*hard_vec != (u_long)intr_glue) {
    161 			/*
    162 			 * Normally, all settable vectors are already
    163 			 * re-routed to the intr_glue() function. The
    164 			 * marvelous exception to these are the HBL/VBL
    165 			 * interrupts. They happen *very* often and
    166 			 * can't be turned off on the Falcon. So they
    167 			 * are normally vectored to an 'rte' instruction.
    168 			 */
    169 			*hard_vec = (u_long)intr_glue;
    170 		}
    171 
    172 		splx(s);
    173 
    174 		return ih;
    175 	}
    176 
    177 	/*
    178 	 * Check for FAST_VEC botches
    179 	 */
    180 	cur_vec = LIST_FIRST(vec_list);
    181 	if (cur_vec->ih_type & FAST_VEC) {
    182 		kmem_free(ih, sizeof(*ih));
    183 		printf("%s: vector cannot be shared\n", __func__);
    184 		return NULL;
    185 	}
    186 
    187 	/*
    188 	 * We traverse the list and place ourselves after any handlers with
    189 	 * our current (or higher) priority level.
    190 	 */
    191 	for (cur_vec = LIST_FIRST(vec_list);
    192 	    LIST_NEXT(cur_vec, ih_link) != NULL;
    193 	    cur_vec = LIST_NEXT(cur_vec, ih_link)) {
    194 		if (ih->ih_pri > cur_vec->ih_pri) {
    195 
    196 			s = splhigh();
    197 			LIST_INSERT_BEFORE(cur_vec, ih, ih_link);
    198 			splx(s);
    199 
    200 			return ih;
    201 		}
    202 	}
    203 
    204 	/*
    205 	 * We're the least important entry, it seems.  We just go
    206 	 * on the end.
    207 	 */
    208 	s = splhigh();
    209 	LIST_INSERT_AFTER(cur_vec, ih, ih_link);
    210 	splx(s);
    211 
    212 	return ih;
    213 }
    214 
    215 int
    216 intr_disestablish(struct intrhand *ih)
    217 {
    218 	ih_list_t	*vec_list;
    219 	u_long		*hard_vec;
    220 	int		vector, s;
    221 	struct intrhand	*cur_vec;
    222 
    223 	vector = ih->ih_vector;
    224 	switch (ih->ih_type & (AUTO_VEC|USER_VEC)) {
    225 	case AUTO_VEC:
    226 		if (vector < AVEC_MIN || vector > AVEC_MAX)
    227 			return 0;
    228 		vec_list = &autovec_list[vector - 1];
    229 		hard_vec = &autovects[vector - 1];
    230 		break;
    231 	case USER_VEC:
    232 		if (vector < UVEC_MIN || vector > UVEC_MAX)
    233 			return 0;
    234 		vec_list = &uservec_list[vector];
    235 		hard_vec = &uservects[vector];
    236 		break;
    237 	default:
    238 		printf("%s: bogus vector type\n", __func__);
    239 		return 0;
    240 	}
    241 
    242 	/*
    243 	 * Check if the vector is really in the list we think it's in....
    244 	 */
    245 	for (cur_vec = LIST_FIRST(vec_list);
    246 	    LIST_NEXT(cur_vec, ih_link) != NULL;
    247 	    cur_vec = LIST_NEXT(cur_vec, ih_link)) {
    248 		if (ih == cur_vec)
    249 			break;
    250 	}
    251 	if (ih != cur_vec) {
    252 		printf("%s: 'ih' has inconsistent data\n", __func__);
    253 		return 0;
    254 	}
    255 
    256 	s = splhigh();
    257 	LIST_REMOVE(ih, ih_link);
    258 	if (LIST_EMPTY(vec_list) && (ih->ih_type & FAST_VEC) != 0)
    259 		*hard_vec = (u_long)intr_glue;
    260 	splx(s);
    261 
    262 	kmem_free(ih, sizeof(*ih));
    263 	return 1;
    264 }
    265 
    266 /*
    267  * This is the dispatcher called by the low-level
    268  * assembly language interrupt-glue routine.
    269  */
    270 void
    271 intr_dispatch(struct clockframe frame)
    272 {
    273 	static int	unexpected, straycount;
    274 	int		vector;
    275 	int		handled = 0;
    276 	ih_list_t	*vec_list;
    277 	struct intrhand	*ih;
    278 
    279 	curcpu()->ci_data.cpu_nintr++;
    280 	vector = (frame.cf_vo & 0xfff) >> 2;
    281 	if (vector < (AVEC_LOC + AVEC_MAX) && vector >= AVEC_LOC)
    282 		vec_list = &autovec_list[vector - AVEC_LOC];
    283 	else if (vector <= (UVEC_LOC + UVEC_MAX) && vector >= UVEC_LOC)
    284 		vec_list = &uservec_list[vector - UVEC_LOC];
    285 	else
    286 		panic("%s: Bogus vector %d", __func__, vector);
    287 
    288 	if ((ih = LIST_FIRST(vec_list)) == NULL) {
    289 		printf("%s: vector %d unexpected\n", __func__, vector);
    290 		if (++unexpected > 10)
    291 			panic("%s: too many unexpected interrupts", __func__);
    292 		return;
    293 	}
    294 	ih->ih_intrcnt[0]++;
    295 
    296 	/* Give all the handlers a chance. */
    297 	for (; ih != NULL; ih = LIST_NEXT(ih, ih_link))
    298 		handled |= (*ih->ih_fun)((ih->ih_type & ARG_CLOCKFRAME) ?
    299 		    &frame : ih->ih_arg, frame.cf_sr);
    300 
    301 	if (handled)
    302 		straycount = 0;
    303 	else if (++straycount > 50)
    304 		panic("%s: too many stray interrupts", __func__);
    305 	else
    306 		printf("%s: stray level %d interrupt\n", __func__, vector);
    307 }
    308 
    309 bool
    310 cpu_intr_p(void)
    311 {
    312 
    313 	return intr_depth != 0;
    314 }
    315 
    316 const uint16_t ipl2psl_table[NIPL] = {
    317 	[IPL_NONE]       = PSL_S | PSL_IPL0,
    318 	[IPL_SOFTCLOCK]  = PSL_S | PSL_IPL1,
    319 	[IPL_SOFTBIO]    = PSL_S | PSL_IPL1,
    320 	[IPL_SOFTNET]    = PSL_S | PSL_IPL1,
    321 	[IPL_SOFTSERIAL] = PSL_S | PSL_IPL1,
    322 	[IPL_VM]         = PSL_S | PSL_IPL4,
    323 	[IPL_SCHED]      = PSL_S | PSL_IPL6,
    324 	[IPL_HIGH]       = PSL_S | PSL_IPL7,
    325 };
    326