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