1 /* $NetBSD: m68k_intr.c,v 1.13 2024/01/19 20:55:42 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 1996, 2023, 2024 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, and Jason R. Thorpe.
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 /*
33 * Common interrupt handling for m68k platforms.
34 */
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: m68k_intr.c,v 1.13 2024/01/19 20:55:42 thorpej Exp $");
38
39 #define _M68K_INTR_PRIVATE
40
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/kmem.h>
44 #include <sys/vmmeter.h>
45 #include <sys/device.h>
46 #include <sys/cpu.h>
47 #include <sys/bus.h>
48 #include <sys/once.h>
49 #include <sys/intr.h>
50
51 #include <machine/vectors.h>
52
53 #include <uvm/uvm_extern.h>
54
55 #ifdef __HAVE_M68K_INTR_VECTORED
56 #ifndef MACHINE_USERVEC_START
57 #define MACHINE_USERVEC_START VECI_USRVEC_START
58 #endif
59
60 #define NVECHANDS (NVECTORS - MACHINE_USERVEC_START)
61
62 #ifndef INTR_FREEVEC
63 #define INTR_FREEVEC badtrap
64 #endif
65
66 extern char INTR_FREEVEC[];
67 extern char intrstub_vectored[];
68 #endif /* __HAVE_M68K_INTR_VECTORED */
69
70 /* A dummy event counter where interrupt stats go to die. */
71 static struct evcnt bitbucket;
72
73 volatile unsigned int intr_depth; /* updated in assembly glue */
74
75 static struct m68k_intrhand_list m68k_intrhands_autovec[NAUTOVECTORS];
76 #ifdef __HAVE_M68K_INTR_VECTORED
77 static struct m68k_intrhand *m68k_intrhands_vectored[NVECHANDS];
78 #endif
79
80 #ifndef __HAVE_LEGACY_INTRCNT
81 #ifndef MACHINE_INTREVCNT_NAMES
82 #define MACHINE_INTREVCNT_NAMES \
83 { "spur", "lev1", "lev2", "lev3", "lev4", "lev5", "lev6", "nmi" }
84 #endif
85 static const char * const m68k_intr_evcnt_names[] = MACHINE_INTREVCNT_NAMES;
86 __CTASSERT(__arraycount(m68k_intr_evcnt_names) == NAUTOVECTORS);
87 static const char m68k_intr_evcnt_group[] = "cpu";
88
89 #define INTRCNT_INIT(x) \
90 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, m68k_intr_evcnt_group, \
91 m68k_intr_evcnt_names[(x)])
92
93 struct evcnt m68k_intr_evcnt[] = {
94 INTRCNT_INIT(0), INTRCNT_INIT(1), INTRCNT_INIT(2), INTRCNT_INIT(3),
95 INTRCNT_INIT(4), INTRCNT_INIT(5), INTRCNT_INIT(6), INTRCNT_INIT(7),
96 };
97 __CTASSERT(__arraycount(m68k_intr_evcnt) == NAUTOVECTORS);
98
99 #undef INTRCNT_INIT
100 #endif /* __HAVE_LEGACY_INTRCNT */
101
102 const uint16_t ipl2psl_table[NIPL] = {
103 [IPL_NONE] = PSL_S | PSL_IPL0,
104 [IPL_SOFTBIO] = PSL_S | MACHINE_PSL_IPL_SOFTBIO,
105 [IPL_SOFTCLOCK] = PSL_S | MACHINE_PSL_IPL_SOFTCLOCK,
106 [IPL_SOFTNET] = PSL_S | MACHINE_PSL_IPL_SOFTNET,
107 [IPL_SOFTSERIAL] = PSL_S | MACHINE_PSL_IPL_SOFTSERIAL,
108 [IPL_VM] = PSL_S | MACHINE_PSL_IPL_VM,
109 [IPL_SCHED] = PSL_S | MACHINE_PSL_IPL_SCHED,
110 [IPL_HIGH] = PSL_S | PSL_IPL7,
111 };
112
113 /*
114 * m68k_spurintr --
115 * Interrupt handler for the "spurious interrupt" that comes in
116 * on auto-vector level 0. All we do is claim it; it gets counted
117 * during the normal course of auto-vector interrupt handling.
118 */
119 static int
m68k_spurintr(void * arg)120 m68k_spurintr(void *arg)
121 {
122 return 1;
123 }
124
125 static struct m68k_intrhand m68k_spurintr_ih = {
126 .ih_func = m68k_spurintr,
127 .ih_arg = m68k_spurintr,
128 .ih_evcnt = &bitbucket,
129 };
130
131 static struct m68k_intrhand *
m68k_ih_stdalloc(int km_flag)132 m68k_ih_stdalloc(int km_flag)
133 {
134 return kmem_zalloc(sizeof(struct m68k_intrhand), km_flag);
135 }
136
137 static void
m68k_ih_stdfree(struct m68k_intrhand * ih)138 m68k_ih_stdfree(struct m68k_intrhand *ih)
139 {
140 kmem_free(ih, sizeof(*ih));
141 }
142
143 static const struct m68k_ih_allocfuncs m68k_ih_stdallocfuncs = {
144 .alloc = m68k_ih_stdalloc,
145 .free = m68k_ih_stdfree,
146 };
147
148 static const struct m68k_ih_allocfuncs *ih_allocfuncs;
149
150 static struct m68k_intrhand *
m68k_ih_alloc(void)151 m68k_ih_alloc(void)
152 {
153 KASSERT(ih_allocfuncs != NULL);
154 return ih_allocfuncs->alloc(KM_SLEEP);
155 }
156
157 static void
m68k_ih_free(struct m68k_intrhand * ih)158 m68k_ih_free(struct m68k_intrhand *ih)
159 {
160 KASSERT(ih_allocfuncs != NULL);
161 if (__predict_true(ih != &m68k_spurintr_ih)) {
162 ih_allocfuncs->free(ih);
163 }
164 }
165
166 #ifdef __HAVE_M68K_INTR_VECTORED
167
168 #define INTRVEC_SLOT(vec) \
169 (&m68k_intrhands_vectored[(vec) - MACHINE_USERVEC_START])
170
171 #define m68k_intrvec_handler(vec) (*INTRVEC_SLOT(vec))
172
173 static bool
m68k_intrvec_add(struct m68k_intrhand * ih)174 m68k_intrvec_add(struct m68k_intrhand *ih)
175 {
176 if (ih->ih_vec < MACHINE_USERVEC_START || ih->ih_vec >= NVECTORS) {
177 aprint_error("%s: vector=0x%x (invalid)\n", __func__,
178 ih->ih_vec);
179 return false;
180 }
181
182 struct m68k_intrhand **slot =
183 &m68k_intrhands_vectored[ih->ih_vec - MACHINE_USERVEC_START];
184
185 if (*slot != NULL) {
186 aprint_error("%s: vector=0x%x (in use)\n", __func__,
187 ih->ih_vec);
188 return false;
189 }
190
191 if (vec_get_entry(ih->ih_vec) != INTR_FREEVEC) {
192 aprint_error("%s: vector=0x%x (unavailable)\n", __func__,
193 ih->ih_vec);
194 return false;
195 }
196
197 *slot = ih;
198 vec_set_entry(ih->ih_vec, intrstub_vectored);
199 return true;
200 }
201
202 static void
m68k_intrvec_remove(struct m68k_intrhand * ih)203 m68k_intrvec_remove(struct m68k_intrhand *ih)
204 {
205 KASSERT(ih->ih_vec >= MACHINE_USERVEC_START);
206 KASSERT(ih->ih_vec < NVECTORS);
207
208 struct m68k_intrhand **slot =
209 &m68k_intrhands_vectored[ih->ih_vec - MACHINE_USERVEC_START];
210
211 KASSERT(*slot == ih);
212 KASSERT(vec_get_entry(ih->ih_vec) == intrstub_vectored);
213
214 vec_set_entry(ih->ih_vec, INTR_FREEVEC);
215 *slot = NULL;
216 }
217
218 /* XXX This is horrible and should burn to the ground. */
219 void *
m68k_intrvec_intrhand(int vec)220 m68k_intrvec_intrhand(int vec)
221 {
222 KASSERT(vec >= MACHINE_USERVEC_START);
223 KASSERT(vec < NVECTORS);
224
225 return m68k_intrhands_vectored[vec - MACHINE_USERVEC_START];
226 }
227
228 #endif /* __HAVE_M68K_INTR_VECTORED */
229
230 /*
231 * m68k_intr_init --
232 * Initialize the interrupt subsystem.
233 */
234 void
m68k_intr_init(const struct m68k_ih_allocfuncs * allocfuncs)235 m68k_intr_init(const struct m68k_ih_allocfuncs *allocfuncs)
236 {
237 int i;
238
239 KASSERT(ih_allocfuncs == NULL);
240 if (allocfuncs == NULL) {
241 ih_allocfuncs = &m68k_ih_stdallocfuncs;
242 } else {
243 ih_allocfuncs = allocfuncs;
244 }
245
246 for (i = 0; i < NAUTOVECTORS; i++) {
247 LIST_INIT(&m68k_intrhands_autovec[i]);
248 #ifndef __HAVE_LEGACY_INTRCNT
249 evcnt_attach_static(&m68k_intr_evcnt[i]);
250 #endif
251 }
252 LIST_INSERT_HEAD(&m68k_intrhands_autovec[0],
253 &m68k_spurintr_ih, ih_link);
254 }
255
256 /*
257 * m68k_intr_establish --
258 * Establish an interrupt handler at the specified vector.
259 * XXX We don't do anything with the flags yet.
260 */
261 void *
m68k_intr_establish(int (* func)(void *),void * arg,struct evcnt * ev,int vec,int ipl,int isrpri,int flags __unused)262 m68k_intr_establish(int (*func)(void *), void *arg, struct evcnt *ev,
263 int vec, int ipl, int isrpri, int flags __unused)
264 {
265 struct m68k_intrhand *ih;
266 int s;
267
268 /*
269 * If a platform doesn't want special behavior, we don't
270 * require them to call m68k_intr_init(); we just handle
271 * it here.
272 *
273 * XXX m68k_intr_init() might be called really early, so
274 * XXX can't use a once control.
275 */
276 if (__predict_false(ih_allocfuncs == NULL)) {
277 m68k_intr_init(NULL);
278 }
279
280 /* These are m68k IPLs, not IPL_* values. */
281 if (ipl < 0 || ipl > 7) {
282 return NULL;
283 }
284
285 #ifdef __HAVE_M68K_INTR_VECTORED
286 KASSERT(vec >= 0);
287 KASSERT(vec < NVECTORS);
288 #else
289 KASSERT(vec == 0);
290 #endif /* __HAVE_M68K_INTR_VECTORED */
291
292 ih = m68k_ih_alloc();
293 ih->ih_func = func;
294 ih->ih_arg = arg;
295 ih->ih_vec = vec;
296 ih->ih_ipl = ipl;
297 ih->ih_isrpri = isrpri;
298 if ((ih->ih_evcnt = ev) == NULL) {
299 ih->ih_evcnt = &bitbucket;
300 }
301
302 #ifdef __HAVE_M68K_INTR_VECTORED
303 if (vec != 0) {
304 if (vec_get_entry(vec) != INTR_FREEVEC) {
305 m68k_ih_free(ih);
306 return NULL;
307 }
308 if (! m68k_intrvec_add(ih)) {
309 m68k_ih_free(ih);
310 return NULL;
311 }
312 return ih;
313 }
314 #endif
315
316 /*
317 * Some devices are particularly sensitive to interrupt
318 * handling latency. Unbuffered serial ports, for example,
319 * can lose data if their interrupts aren't handled with
320 * reasonable speed. For this reason, we sort interrupt
321 * handlers by an abstract "ISR" priority, inserting higher-
322 * priority interrupts before lower-priority interrupts.
323 */
324 struct m68k_intrhand_list * const list = &m68k_intrhands_autovec[ipl];
325 struct m68k_intrhand *curih;
326
327 s = splhigh();
328 if (LIST_EMPTY(list)) {
329 LIST_INSERT_HEAD(list, ih, ih_link);
330 goto done;
331 }
332 for (curih = LIST_FIRST(list);
333 LIST_NEXT(curih, ih_link) != NULL;
334 curih = LIST_NEXT(curih, ih_link)) {
335 if (ih->ih_isrpri > curih->ih_isrpri) {
336 LIST_INSERT_BEFORE(curih, ih, ih_link);
337 goto done;
338 }
339 }
340 LIST_INSERT_AFTER(curih, ih, ih_link);
341 done:
342 splx(s);
343
344 return ih;
345 }
346
347 /*
348 * m68k_intr_disestablish --
349 * Remove an interrupt handler. Returns true if the handler
350 * list for this vector is now empty.
351 */
352 bool
m68k_intr_disestablish(void * v)353 m68k_intr_disestablish(void *v)
354 {
355 struct m68k_intrhand *ih = v;
356 int s;
357 bool empty;
358
359 #ifdef __HAVE_M68K_INTR_VECTORED
360 if (ih->ih_vec != 0) {
361 KASSERT(vec_get_entry(ih->ih_vec) == intrstub_vectored);
362 m68k_intrvec_remove(ih);
363 empty = true;
364 } else
365 #endif
366 {
367 s = splhigh();
368 LIST_REMOVE(ih, ih_link);
369 empty = LIST_EMPTY(&m68k_intrhands_autovec[ih->ih_ipl]);
370 splx(s);
371 }
372
373 m68k_ih_free(ih);
374
375 return empty;
376 }
377
378 void m68k_intr_autovec(struct clockframe);
379
380 #ifndef MACHINE_AUTOVEC_IGNORE_STRAY
381 #define MACHINE_AUTOVEC_IGNORE_STRAY(ipl) 0
382 #endif
383
384 /*
385 * m68k_intr_autovec --
386 * Run the interrupt handlers for an auto-vectored interrupt.
387 * Called from the assembly glue in m68k_intr_stubs.s
388 */
389 void
m68k_intr_autovec(struct clockframe frame)390 m68k_intr_autovec(struct clockframe frame)
391 {
392 const int ipl = VECO_TO_VECI(frame.cf_vo) - VECI_INTRAV0;
393 struct m68k_intrhand *ih;
394 bool rv = false;
395
396 m68k_count_intr(ipl);
397
398 LIST_FOREACH(ih, &m68k_intrhands_autovec[ipl], ih_link) {
399 void *arg = ih->ih_arg ? ih->ih_arg : &frame;
400 if (ih->ih_func(arg)) {
401 ih->ih_evcnt->ev_count++;
402 rv = true;
403 }
404 }
405 if (!rv && !MACHINE_AUTOVEC_IGNORE_STRAY(ipl)) {
406 printf("Stray level %d interrupt\n", ipl);
407 }
408
409 ATOMIC_CAS_CHECK(&frame);
410 }
411
412 #ifdef __HAVE_M68K_INTR_VECTORED
413 void m68k_intr_vectored(struct clockframe);
414
415 /*
416 * m68k_intr_vectored --
417 * Run a vectored interrupt handler.
418 * Called from the assembly glue in m68k_intr_stubs.s
419 */
420 void
m68k_intr_vectored(struct clockframe frame)421 m68k_intr_vectored(struct clockframe frame)
422 {
423 const int vec = VECO_TO_VECI(frame.cf_vo);
424 const int ipl = (getsr() >> 8) & 7;
425 struct m68k_intrhand *ih;
426
427 m68k_count_intr(ipl);
428
429 #ifdef DIAGNOSTIC
430 if (vec < MACHINE_USERVEC_START || vec >= NVECTORS) {
431 printf("%s: vector=0x%x (invalid)\n", __func__, vec);
432 goto out;
433 }
434 #endif
435 ih = m68k_intrvec_handler(vec);
436 if (ih == NULL) {
437 printf("%s: vector=0x%x (no handler?)\n", __func__, vec);
438 vec_set_entry(vec, INTR_FREEVEC);
439 }
440
441 if (__predict_true((*ih->ih_func)(ih->ih_arg ? ih->ih_arg
442 : &frame) != 0)) {
443 ih->ih_evcnt->ev_count++;
444 } else {
445 printf("Stray level %d interrupt vector=0x%x\n",
446 ipl, vec);
447 }
448 #ifdef DIAGNOSTIC
449 out:
450 #endif
451 ATOMIC_CAS_CHECK(&frame);
452 }
453 #endif /* __HAVE_M68K_INTR_VECTORED */
454