xref: /netbsd/sys/arch/atari/atari/intr.c (revision 6550d01e)
1 /*	$NetBSD: intr.c,v 1.23 2010/12/20 00:25:30 matt 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.23 2010/12/20 00:25:30 matt Exp $");
34 
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/kernel.h>
38 #include <sys/malloc.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 ih_list_t autovec_list[AVEC_MAX - AVEC_MIN + 1];
55 ih_list_t uservec_list[UVEC_MAX - UVEC_MIN + 1];
56 int idepth;
57 volatile int ssir;
58 
59 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 it's 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 *
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 	/* no point in sleeping unless someone can free memory. */
116 	ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
117 	if (ih == NULL)
118 		panic("intr_establish: can't malloc handler info");
119 
120 	/*
121 	 * Initialize vector info
122 	 */
123 	ih->ih_fun    = ih_fun;
124 	ih->ih_arg    = ih_arg;
125 	ih->ih_type   = type;
126 	ih->ih_pri    = pri;
127 	ih->ih_vector = vector;
128 
129 	/*
130 	 * Do some validity checking on the 'vector' argument and determine
131 	 * vector list this interrupt should be on.
132 	 */
133 	switch (type & (AUTO_VEC|USER_VEC)) {
134 	case AUTO_VEC:
135 		if (vector < AVEC_MIN || vector > AVEC_MAX)
136 			return NULL;
137 		vec_list = &autovec_list[vector-1];
138 		hard_vec = &autovects[vector-1];
139 		ih->ih_intrcnt = &intrcnt_auto[vector-1];
140 		break;
141 	case USER_VEC:
142 		if (vector < UVEC_MIN || vector > UVEC_MAX)
143 			return NULL;
144 		vec_list = &uservec_list[vector];
145 		hard_vec = &uservects[vector];
146 		ih->ih_intrcnt = &intrcnt_user[vector];
147 		break;
148 	default:
149 		printf("intr_establish: bogus vector type\n");
150 		free(ih, M_DEVBUF);
151 		return NULL;
152 	}
153 
154 	/*
155 	 * If the vec_list is empty, we insert ourselves at the head of the
156 	 * list and we re-route the 'hard-vector' to the appropriate handler.
157 	 */
158 	if (vec_list->lh_first == NULL) {
159 
160 		s = splhigh();
161 		LIST_INSERT_HEAD(vec_list, ih, ih_link);
162 		if (type & FAST_VEC)
163 			*hard_vec = (u_long)ih->ih_fun;
164 		else if (*hard_vec != (u_long)intr_glue) {
165 			/*
166 			 * Normally, all settable vectors are already
167 			 * re-routed to the intr_glue() function. The
168 			 * marvelous exception to these are the HBL/VBL
169 			 * interrupts. They happen *very* often and
170 			 * can't be turned off on the Falcon. So they
171 			 * are normally vectored to an 'rte' instruction.
172 			 */
173 			*hard_vec = (u_long)intr_glue;
174 		}
175 
176 		splx(s);
177 
178 		return ih;
179 	}
180 
181 	/*
182 	 * Check for FAST_VEC botches
183 	 */
184 	cur_vec = vec_list->lh_first;
185 	if (cur_vec->ih_type & FAST_VEC) {
186 		free(ih, M_DEVBUF);
187 		printf("intr_establish: vector cannot be shared\n");
188 		return NULL;
189 	}
190 
191 	/*
192 	 * We traverse the list and place ourselves after any handlers with
193 	 * our current (or higher) priority level.
194 	 */
195 	for (cur_vec = vec_list->lh_first; cur_vec->ih_link.le_next != NULL;
196 	    cur_vec = cur_vec->ih_link.le_next) {
197 		if (ih->ih_pri > cur_vec->ih_pri) {
198 
199 			s = splhigh();
200 			LIST_INSERT_BEFORE(cur_vec, ih, ih_link);
201 			splx(s);
202 
203 			return ih;
204 		}
205 	}
206 
207 	/*
208 	 * We're the least important entry, it seems.  We just go
209 	 * on the end.
210 	 */
211 	s = splhigh();
212 	LIST_INSERT_AFTER(cur_vec, ih, ih_link);
213 	splx(s);
214 
215 	return ih;
216 }
217 
218 int
219 intr_disestablish(struct intrhand *ih)
220 {
221 	ih_list_t	*vec_list;
222 	u_long		*hard_vec;
223 	int		vector, s;
224 	struct intrhand	*cur_vec;
225 
226 	vector = ih->ih_vector;
227 	switch (ih->ih_type & (AUTO_VEC|USER_VEC)) {
228 	case AUTO_VEC:
229 		if (vector < AVEC_MIN || vector > AVEC_MAX)
230 			return 0;
231 		vec_list = &autovec_list[vector-1];
232 		hard_vec = &autovects[vector-1];
233 		break;
234 	case USER_VEC:
235 		if (vector < UVEC_MIN || vector > UVEC_MAX)
236 			return 0;
237 		vec_list = &uservec_list[vector];
238 		hard_vec = &uservects[vector];
239 		break;
240 	default:
241 		printf("intr_disestablish: bogus vector type\n");
242 		return 0;
243 	}
244 
245 	/*
246 	 * Check if the vector is really in the list we think it's in....
247 	 */
248 	for (cur_vec = vec_list->lh_first; cur_vec->ih_link.le_next != NULL;
249 	    cur_vec = cur_vec->ih_link.le_next) {
250 		if (ih == cur_vec)
251 			break;
252 	}
253 	if (ih != cur_vec) {
254 		printf("intr_disestablish: 'ih' has inconsistent data\n");
255 		return 0;
256 	}
257 
258 	s = splhigh();
259 	LIST_REMOVE(ih, ih_link);
260 	if ((vec_list->lh_first == NULL) && (ih->ih_type & FAST_VEC))
261 		*hard_vec = (u_long)intr_glue;
262 	splx(s);
263 
264 	free(ih, M_DEVBUF);
265 	return 1;
266 }
267 
268 /*
269  * This is the dispatcher called by the low-level
270  * assembly language interrupt-glue routine.
271  */
272 void
273 intr_dispatch(struct clockframe frame)
274 {
275 	static int	unexpected, straycount;
276 	int		vector;
277 	int		handled = 0;
278 	ih_list_t	*vec_list;
279 	struct intrhand	*ih;
280 
281 	curcpu()->ci_data.cpu_nintr++;
282 	vector = (frame.cf_vo & 0xfff) >> 2;
283 	if (vector < (AVEC_LOC+AVEC_MAX) && vector >= AVEC_LOC)
284 		vec_list = &autovec_list[vector - AVEC_LOC];
285 	else if (vector <= (UVEC_LOC+UVEC_MAX) && vector >= UVEC_LOC)
286 		vec_list = &uservec_list[vector - UVEC_LOC];
287 	else
288 		panic("intr_dispatch: Bogus vector %d", vector);
289 
290 	if ((ih = vec_list->lh_first) == NULL) {
291 		printf("intr_dispatch: vector %d unexpected\n", vector);
292 		if (++unexpected > 10)
293 			panic("intr_dispatch: too many unexpected interrupts");
294 		return;
295 	}
296 	ih->ih_intrcnt[0]++;
297 
298 	/* Give all the handlers a chance. */
299 	for (; ih != NULL; ih = ih->ih_link.le_next)
300 		handled |= (*ih->ih_fun)((ih->ih_type & ARG_CLOCKFRAME) ?
301 		    &frame : ih->ih_arg, frame.cf_sr);
302 
303 	if (handled)
304 		straycount = 0;
305 	else if (++straycount > 50)
306 		panic("intr_dispatch: too many stray interrupts");
307 	else
308 		printf("intr_dispatch: stray level %d interrupt\n", vector);
309 }
310 
311 bool
312 cpu_intr_p(void)
313 {
314 
315 	return idepth != 0;
316 }
317 
318 const uint16_t ipl2psl_table[NIPL] = {
319 	[IPL_NONE]       = PSL_S | PSL_IPL0,
320 	[IPL_SOFTCLOCK]  = PSL_S | PSL_IPL1,
321 	[IPL_SOFTBIO]    = PSL_S | PSL_IPL1,
322 	[IPL_SOFTNET]    = PSL_S | PSL_IPL1,
323 	[IPL_SOFTSERIAL] = PSL_S | PSL_IPL1,
324 	[IPL_VM]         = PSL_S | PSL_IPL4,
325 	[IPL_SCHED]      = PSL_S | PSL_IPL6,
326 	[IPL_HIGH]       = PSL_S | PSL_IPL7,
327 };
328