xref: /netbsd-src/sys/arch/m68k/m68k/m68k_intr.c (revision a7c95ef528cdc69beba8a7ca7bd0dfa555e1f4bd)
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