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