xref: /netbsd/sys/arch/atari/atari/intr.c (revision bf9ec67e)
1 /*	$NetBSD: intr.c,v 1.8 2000/06/29 08:28:23 mrg 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  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *	This product includes software developed by the NetBSD
21  *	Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/kernel.h>
42 #include <sys/malloc.h>
43 #include <sys/vmmeter.h>
44 #include <sys/queue.h>
45 #include <sys/device.h>
46 
47 #include <uvm/uvm_extern.h>
48 
49 #include <machine/cpu.h>
50 
51 #include <atari/atari/intr.h>
52 
53 #define	AVEC_MIN	1
54 #define	AVEC_MAX	7
55 #define	AVEC_LOC	25
56 #define	UVEC_MIN	0
57 #define	UVEC_MAX	191
58 #define	UVEC_LOC	64
59 
60 typedef LIST_HEAD(, intrhand) ih_list_t;
61 ih_list_t autovec_list[AVEC_MAX - AVEC_MIN + 1];
62 ih_list_t uservec_list[UVEC_MAX - UVEC_MIN + 1];
63 
64 void
65 intr_init()
66 {
67 	int i;
68 
69 	for (i = 0; i < (AVEC_MAX - AVEC_MIN + 1); ++i) {
70 		LIST_INIT(&autovec_list[i]);
71 	}
72 	for (i = 0; i < (UVEC_MAX - UVEC_MIN + 1); ++i) {
73 		LIST_INIT(&uservec_list[i]);
74 	}
75 }
76 
77 /*
78  * Establish an interrupt vector.
79  *   - vector
80  *	The vector numer the interrupt should be hooked on. It can either
81  *	be an auto-vector or a user-vector.
82  *   - type
83  *	A bit-wise of:
84  *		- AUTO_VEC (mutually exclusive with USER_VEC)
85  *			Attach to one of the 7 auto vectors
86  *		- USER_VEC (mutually exclusive with AUTO_VEC)
87  *			Attach to one of the 192 user vectors
88  *		- FAST_VEC
89  *			The interrupt function 'ih_fun' will be
90  *			put into the 'real' interrupt table. This
91  *			means:
92  *				- This vector can't be shared
93  *				- 'ih_fun' must save registers
94  *				- 'ih_fun' must do it's own interrupt accounting
95  *				- The argument to 'ih_fun' is a standard
96  *				  interrupt frame.
97  *		- ARG_CLOCKRAME
98  *			The 'ih_fun' function will be called with
99  *			a standard clock-frame instead of 'ih_arg'.
100  *
101  *   - pri
102  *	When multiple interrupts are established on the same vector,
103  *	interrupts with the highest priority will be called first. The
104  *	basic ordering is the order of establishment.
105  *   - ih_fun
106  *	The interrupt function to be called
107  *   - ih_arg
108  *	The argument given to 'ih_fun' when ARG_CLOCKFRAME is not
109  *	specified.
110  */
111 
112 struct intrhand *
113 intr_establish(vector, type, pri, ih_fun, ih_arg)
114 	void		*ih_arg;
115         int		vector;
116         int		type;
117         int		pri;
118         hw_ifun_t	ih_fun;
119 {
120 	struct intrhand	*ih, *cur_vec;
121 	ih_list_t	*vec_list;
122 	u_long		*hard_vec;
123 	int		s;
124 
125 	/* no point in sleeping unless someone can free memory. */
126 	ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
127 	if (ih == NULL)
128 		panic("intr_establish: can't malloc handler info");
129 
130 	/*
131 	 * Initialize vector info
132 	 */
133 	ih->ih_fun    = ih_fun;
134 	ih->ih_arg    = ih_arg;
135 	ih->ih_type   = type;
136 	ih->ih_pri    = pri;
137 	ih->ih_vector = vector;
138 
139 	/*
140 	 * Do some validity checking on the 'vector' argument and determine
141 	 * vector list this interrupt should be on.
142 	 */
143 	switch(type & (AUTO_VEC|USER_VEC)) {
144 		case AUTO_VEC:
145 			if (vector < AVEC_MIN || vector > AVEC_MAX)
146 				return (NULL);
147 			vec_list = &autovec_list[vector-1];
148 			hard_vec = &autovects[vector-1];
149 			ih->ih_intrcnt = &intrcnt_auto[vector-1];
150 			break;
151 		case USER_VEC:
152 			if (vector < UVEC_MIN || vector > UVEC_MAX)
153 				return (NULL);
154 			vec_list = &uservec_list[vector];
155 			hard_vec = &uservects[vector];
156 			ih->ih_intrcnt = &intrcnt_user[vector];
157 			break;
158 		default:
159 			printf("intr_establish: bogus vector type\n");
160 			free(ih, M_DEVBUF);
161 			return(NULL);
162 	}
163 
164 	/*
165 	 * If the vec_list is empty, we insert ourselves at the head of the
166 	 * list and we re-route the 'hard-vector' to the appropriate handler.
167 	 */
168 	if (vec_list->lh_first == NULL) {
169 
170 		s = splhigh();
171 		LIST_INSERT_HEAD(vec_list, ih, ih_link);
172 		if (type & FAST_VEC)
173 			*hard_vec = (u_long)ih->ih_fun;
174 		else if(*hard_vec != (u_long)intr_glue) {
175 			/*
176 			 * Normally, all settable vectors are already
177 			 * re-routed to the intr_glue() function. The
178 			 * marvelous exeption to these are the HBL/VBL
179 			 * interrupts. They happen *very* often and
180 			 * can't be turned off on the Falcon. So they
181 			 * are normally vectored to an 'rte' instruction.
182 			 */
183 			*hard_vec = (u_long)intr_glue;
184 		}
185 
186 		splx(s);
187 
188 		return ih;
189 	}
190 
191 	/*
192 	 * Check for FAST_VEC botches
193 	 */
194 	cur_vec = vec_list->lh_first;
195 	if (cur_vec->ih_type & FAST_VEC) {
196 		free(ih, M_DEVBUF);
197 		printf("intr_establish: vector cannot be shared\n");
198 		return (NULL);
199 	}
200 
201 	/*
202 	 * We traverse the list and place ourselves after any handlers with
203 	 * our current (or higher) priority level.
204 	 */
205 	for (cur_vec = vec_list->lh_first; cur_vec->ih_link.le_next != NULL;
206 	    cur_vec = cur_vec->ih_link.le_next) {
207 		if (ih->ih_pri > cur_vec->ih_pri) {
208 
209 			s = splhigh();
210 			LIST_INSERT_BEFORE(cur_vec, ih, ih_link);
211 			splx(s);
212 
213 			return (ih);
214 		}
215 	}
216 
217 	/*
218 	 * We're the least important entry, it seems.  We just go
219 	 * on the end.
220 	 */
221 	s = splhigh();
222 	LIST_INSERT_AFTER(cur_vec, ih, ih_link);
223 	splx(s);
224 
225 	return ih;
226 }
227 
228 int
229 intr_disestablish(ih)
230 struct intrhand	*ih;
231 {
232 	ih_list_t	*vec_list;
233 	u_long		*hard_vec;
234 	int		vector, s;
235 	struct intrhand	*cur_vec;
236 
237 	vector = ih->ih_vector;
238 	switch(ih->ih_type & (AUTO_VEC|USER_VEC)) {
239 		case AUTO_VEC:
240 			if (vector < AVEC_MIN || vector > AVEC_MAX)
241 				return 0;
242 			vec_list = &autovec_list[vector-1];
243 			hard_vec = &autovects[vector-1];
244 			break;
245 		case USER_VEC:
246 			if (vector < UVEC_MIN || vector > UVEC_MAX)
247 				return 0;
248 			vec_list = &uservec_list[vector];
249 			hard_vec = &uservects[vector];
250 			break;
251 		default:
252 			printf("intr_disestablish: bogus vector type\n");
253 			return  0;
254 	}
255 
256 	/*
257 	 * Check if the vector is really in the list we think it's in....
258 	 */
259 	for (cur_vec = vec_list->lh_first; cur_vec->ih_link.le_next != NULL;
260 	    cur_vec = cur_vec->ih_link.le_next) {
261 		if (ih == cur_vec)
262 			break;
263 	}
264 	if (ih != cur_vec) {
265 		printf("intr_disestablish: 'ih' has inconsistent data\n");
266 		return 0;
267 	}
268 
269 	s = splhigh();
270 	LIST_REMOVE(ih, ih_link);
271 	if ((vec_list->lh_first == NULL) && (ih->ih_type & FAST_VEC))
272 		*hard_vec = (u_long)intr_glue;
273 	splx(s);
274 
275 	free(ih, M_DEVBUF);
276 	return 1;
277 }
278 
279 /*
280  * This is the dispatcher called by the low-level
281  * assembly language interrupt-glue routine.
282  */
283 void
284 intr_dispatch(frame)
285 struct clockframe	frame;
286 {
287 	static int	unexpected, straycount;
288 	int		vector;
289 	int		handled = 0;
290 	ih_list_t	*vec_list;
291 	struct intrhand	*ih;
292 
293 	uvmexp.intrs++;
294 	vector = (frame.cf_vo & 0xfff) >> 2;
295 	if (vector < (AVEC_LOC+AVEC_MAX) && vector >= AVEC_LOC)
296 		vec_list = &autovec_list[vector - AVEC_LOC];
297 	else if (vector <= (UVEC_LOC+UVEC_MAX) && vector >= UVEC_LOC)
298 		vec_list = &uservec_list[vector - UVEC_LOC];
299 	else panic("intr_dispatch: Bogus vector %d\n", vector);
300 
301 	if ((ih = vec_list->lh_first) == NULL) {
302 		printf("intr_dispatch: vector %d unexpected\n", vector);
303 		if (++unexpected > 10)
304 		  panic("intr_dispatch: too many unexpected interrupts");
305 		return;
306 	}
307 	ih->ih_intrcnt[0]++;
308 
309 	/* Give all the handlers a chance. */
310 	for ( ; ih != NULL; ih = ih->ih_link.le_next)
311 		handled |= (*ih->ih_fun)((ih->ih_type & ARG_CLOCKFRAME)
312 					? &frame : ih->ih_arg, frame.cf_sr);
313 
314 	if (handled)
315 	    straycount = 0;
316 	else if (++straycount > 50)
317 	    panic("intr_dispatch: too many stray interrupts");
318 	else
319 	    printf("intr_dispatch: stray level %d interrupt\n", vector);
320 }
321