xref: /netbsd/sys/arch/next68k/next68k/isr.c (revision c4a72b64)
1 /*	$NetBSD: isr.c,v 1.17 2002/09/27 15:36:32 provos Exp $ */
2 
3 /*
4  * This file was taken from mvme68k/mvme68k/isr.c
5  * should probably be re-synced when needed.
6  * Darrin B. Jewell <jewell@mit.edu>  Tue Nov 10 05:07:16 1998
7  * original cvs id: NetBSD: isr.c,v 1.12 1998/07/05 06:49:07 jonathan Exp
8  */
9 
10 /*-
11  * Copyright (c) 1996 The NetBSD Foundation, Inc.
12  * All rights reserved.
13  *
14  * This code is derived from software contributed to The NetBSD Foundation
15  * by Adam Glass, Gordon W. Ross, and Jason R. Thorpe.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must reproduce the above copyright
23  *    notice, this list of conditions and the following disclaimer in the
24  *    documentation and/or other materials provided with the distribution.
25  * 3. All advertising materials mentioning features or use of this software
26  *    must display the following acknowledgement:
27  *        This product includes software developed by the NetBSD
28  *        Foundation, Inc. and its contributors.
29  * 4. Neither the name of The NetBSD Foundation nor the names of its
30  *    contributors may be used to endorse or promote products derived
31  *    from this software without specific prior written permission.
32  *
33  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
34  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
35  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
36  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
37  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
38  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
39  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
40  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
41  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
42  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
43  * POSSIBILITY OF SUCH DAMAGE.
44  */
45 
46 /*
47  * Link and dispatch interrupts.
48  */
49 
50 #include <sys/param.h>
51 #include <sys/systm.h>
52 #include <sys/malloc.h>
53 #include <sys/vmmeter.h>
54 
55 #include <uvm/uvm_extern.h>
56 
57 #include <net/netisr.h>
58 
59 #include <machine/bus.h>
60 #include <machine/cpu.h>
61 
62 #include <next68k/next68k/isr.h>
63 
64 #include <next68k/dev/intiovar.h>
65 
66 volatile unsigned int interrupt_depth;
67 isr_autovec_list_t isr_autovec[NISRAUTOVEC];
68 struct	isr_vectored isr_vectored[NISRVECTORED];
69 static const char irqgroupname[] = "hard irqs";
70 struct	evcnt next68k_irq_evcnt[] = {
71 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "spur"),
72 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev1"),
73 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev2"),
74 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev3"),
75 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev4"),
76 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev5"),
77 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev6"),
78 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "nmi")
79 };
80 
81 extern	int intrcnt[];		/* from locore.s. XXXSCW: will go away soon */
82 extern	void (*vectab[]) __P((void));
83 extern	void badtrap __P((void));
84 extern	void intrhand_vectored __P((void));
85 
86 #if 0
87 static	int spurintr __P((void *));
88 #endif
89 
90 void
91 isrinit()
92 {
93 	int i;
94 
95 	/* Initialize the autovector lists. */
96 	for (i = 0; i < NISRAUTOVEC; ++i)
97 		LIST_INIT(&isr_autovec[i]);
98 
99 	/* Initialise the interrupt event counts */
100 	for (i = 0; i < (sizeof(next68k_irq_evcnt) / sizeof(struct evcnt)); i++)
101 		evcnt_attach_static(&next68k_irq_evcnt[i]);
102 
103 	/* Arrange to trap Spurious and NMI auto-vectored Interrupts */
104 /* 	isrlink_autovec(spurintr, NULL, 0, 0, NULL); */
105 /* 	isrlink_autovec(nmihand, NULL, 7, 0, NULL); */
106 }
107 
108 /*
109  * Establish an autovectored interrupt handler.
110  * Called by driver attach functions.
111  */
112 void
113 isrlink_autovec(func, arg, ipl, priority, evcnt)
114 	int (*func) __P((void *));
115 	void *arg;
116 	int ipl;
117 	int priority;
118 	struct evcnt *evcnt;
119 {
120 	struct isr_autovec *newisr, *curisr;
121 	isr_autovec_list_t *list;
122 
123 #ifdef DIAGNOSTIC
124 	if ((ipl < 0) || (ipl >= NISRAUTOVEC))
125 		panic("isrlink_autovec: bad ipl %d", ipl);
126 #endif
127 
128 	newisr = (struct isr_autovec *)malloc(sizeof(struct isr_autovec),
129 	    M_DEVBUF, M_NOWAIT);
130 	if (newisr == NULL)
131 		panic("isrlink_autovec: can't allocate space for isr");
132 
133 	/* Fill in the new entry. */
134 	newisr->isr_func = func;
135 	newisr->isr_arg = arg;
136 	newisr->isr_ipl = ipl;
137 	newisr->isr_priority = priority;
138 	newisr->isr_evcnt = evcnt;
139 
140 	/*
141 	 * Some devices are particularly sensitive to interrupt
142 	 * handling latency.  The SCC, for example, can lose many
143 	 * characters if its interrupt isn't handled with reasonable
144 	 * speed.
145 	 *
146 	 * To work around this problem, each device can give itself a
147 	 * "priority".  An unbuffered SCC would give itself a higher
148 	 * priority than a SCSI device, for example.
149 	 *
150 	 * This solution was originally developed for the hp300, which
151 	 * has a flat spl scheme (by necessity).  Thankfully, the
152 	 * MVME systems don't have this problem, though this may serve
153 	 * a useful purpose in any case.
154 	 */
155 
156 	/*
157 	 * Get the appropriate ISR list.  If the list is empty, no
158 	 * additional work is necessary; we simply insert ourselves
159 	 * at the head of the list.
160 	 */
161 	list = &isr_autovec[ipl];
162 	if (list->lh_first == NULL) {
163 		LIST_INSERT_HEAD(list, newisr, isr_link);
164 		return;
165 	}
166 
167 	/*
168 	 * A little extra work is required.  We traverse the list
169 	 * and place ourselves after any ISRs with our current (or
170 	 * higher) priority.
171 	 */
172 	for (curisr = list->lh_first; curisr->isr_link.le_next != NULL;
173 	    curisr = curisr->isr_link.le_next) {
174 		if (newisr->isr_priority > curisr->isr_priority) {
175 			LIST_INSERT_BEFORE(curisr, newisr, isr_link);
176 			return;
177 		}
178 	}
179 
180 	/*
181 	 * We're the least important entry, it seems.  We just go
182 	 * on the end.
183 	 */
184 	LIST_INSERT_AFTER(curisr, newisr, isr_link);
185 }
186 
187 /*
188  * Establish a vectored interrupt handler.
189  * Called by bus interrupt establish functions.
190  */
191 void
192 isrlink_vectored(func, arg, ipl, vec, evcnt)
193 	int (*func) __P((void *));
194 	void *arg;
195 	int ipl, vec;
196 	struct evcnt *evcnt;
197 {
198 	struct isr_vectored *isr;
199 
200 #ifdef DIAGNOSTIC
201 	if ((ipl < 0) || (ipl >= NISRAUTOVEC))
202 		panic("isrlink_vectored: bad ipl %d", ipl);
203 	if ((vec < ISRVECTORED) || (vec >= ISRVECTORED + NISRVECTORED))
204 		panic("isrlink_vectored: bad vec 0x%x", vec);
205 #endif
206 
207 	isr = &isr_vectored[vec - ISRVECTORED];
208 
209 #ifdef DIAGNOSTIC
210 	if ((vectab[vec] != badtrap) || (isr->isr_func != NULL))
211 		panic("isrlink_vectored: vec 0x%x not available", vec);
212 #endif
213 
214 	/* Fill in the new entry. */
215 	isr->isr_func = func;
216 	isr->isr_arg = arg;
217 	isr->isr_ipl = ipl;
218 	isr->isr_evcnt = evcnt;
219 
220 	/* Hook into the vector table. */
221 	vectab[vec] = intrhand_vectored;
222 }
223 
224 /*
225  * Return a pointer to the evcnt structure for
226  * the specified ipl.
227  */
228 struct evcnt *
229 isrlink_evcnt(ipl)
230 	int ipl;
231 {
232 
233 #ifdef DIAGNOSTIC
234 	if (ipl < 0 ||
235 	    ipl >= (sizeof(next68k_irq_evcnt) / sizeof(struct evcnt)))
236 		panic("isrlink_evcnt: bad ipl %d", ipl);
237 #endif
238 
239 	return (&next68k_irq_evcnt[ipl]);
240 }
241 
242 /*
243  * Unhook a vectored interrupt.
244  */
245 void
246 isrunlink_vectored(vec)
247 	int vec;
248 {
249 
250 #ifdef DIAGNOSTIC
251 	if ((vec < ISRVECTORED) || (vec >= ISRVECTORED + NISRVECTORED))
252 		panic("isrunlink_vectored: bad vec 0x%x", vec);
253 
254 	if (vectab[vec] != intrhand_vectored)
255 		panic("isrunlink_vectored: not vectored interrupt");
256 #endif
257 
258 	vectab[vec] = badtrap;
259 	memset(&isr_vectored[vec - ISRVECTORED], 0, sizeof(struct isr_vectored));
260 }
261 
262 /*
263  * This is the dispatcher called by the low-level
264  * assembly language autovectored interrupt routine.
265  */
266 void
267 isrdispatch_autovec(frame)
268 	struct clockframe *frame;
269 {
270 	struct isr_autovec *isr;
271 	isr_autovec_list_t *list;
272 	int handled, ipl;
273 	void *arg;
274 	static int straycount, unexpected;
275 
276 	ipl = (frame->vec >> 2) - ISRAUTOVEC;
277 
278 #ifdef DIAGNOSTIC
279 	if ((ipl < 0) || (ipl >= NISRAUTOVEC))
280 		panic("isrdispatch_autovec: bad vec 0x%x", frame->vec);
281 #endif
282 
283 	intrcnt[ipl]++; /* XXXSCW: Will go away soon */
284 	next68k_irq_evcnt[ipl].ev_count++;
285 	uvmexp.intrs++;
286 
287 	list = &isr_autovec[ipl];
288 	if (list->lh_first == NULL) {
289 		printf("isrdispatch_autovec: ipl %d unexpected\n", ipl);
290 		if (++unexpected > 10)
291 			panic("too many unexpected interrupts");
292 		return;
293 	}
294 
295 	/* Give all the handlers a chance. */
296 	handled = 0;
297 	for (isr = list->lh_first ; isr != NULL; isr = isr->isr_link.le_next) {
298 		arg = isr->isr_arg ? isr->isr_arg : frame;
299 		if ((*isr->isr_func)(arg) != 0) {
300 			if (isr->isr_evcnt)
301 				isr->isr_evcnt->ev_count++;
302 			handled++;
303 		}
304 	}
305 
306 	if (handled)
307 		straycount = 0;
308 	else if (++straycount > 50)
309 		panic("isr_dispatch_autovec: too many stray interrupts");
310 	else {
311 		char sbuf[256];
312 
313 		printf("isrdispatch_autovec: stray level %d interrupt\n", ipl);
314 
315 		bitmask_snprintf((*(volatile u_long *)IIOV(NEXT_P_INTRSTAT)),
316 				 NEXT_INTR_BITS, sbuf, sizeof(sbuf));
317 		printf("  *intrstat = 0x%s\n", sbuf);
318 
319 		bitmask_snprintf((*(volatile u_long *)IIOV(NEXT_P_INTRMASK)),
320 				 NEXT_INTR_BITS, sbuf, sizeof(sbuf));
321 		printf("  *intrmask = 0x%s\n", sbuf);
322 	}
323 }
324 
325 /*
326  * This is the dispatcher called by the low-level
327  * assembly language vectored interrupt routine.
328  */
329 void
330 isrdispatch_vectored(ipl, frame)
331 	int ipl;
332 	struct clockframe *frame;
333 {
334 	struct isr_vectored *isr;
335 	int vec;
336 
337 	vec = (frame->vec >> 2) - ISRVECTORED;
338 
339 #ifdef DIAGNOSTIC
340 	if ((vec < 0) || (vec >= NISRVECTORED))
341 		panic("isrdispatch_vectored: bad vec 0x%x", frame->vec);
342 #endif
343 
344 	isr = &isr_vectored[vec];
345 
346 	intrcnt[ipl]++; /* XXXSCW: Will go away soon */
347 	next68k_irq_evcnt[ipl].ev_count++;
348 	uvmexp.intrs++;
349 
350 	if (isr->isr_func == NULL) {
351 		printf("isrdispatch_vectored: no handler for vec 0x%x\n",
352 		    frame->vec);
353 		vectab[vec + ISRVECTORED] = badtrap;
354 		return;
355 	}
356 
357 	/*
358 	 * Handler gets exception frame if argument is NULL.
359 	 */
360 	if ((*isr->isr_func)(isr->isr_arg ? isr->isr_arg : frame) == 0)
361 		printf("isrdispatch_vectored: vec 0x%x not claimed\n",
362 		    frame->vec);
363 	else
364 	if (isr->isr_evcnt)
365 		isr->isr_evcnt->ev_count++;
366 }
367 
368 /*
369  * netisr junk...
370  * should use an array of chars instead of
371  * a bitmask to avoid atomicity locking issues.
372  */
373 
374 void
375 netintr()
376 {
377 	int n, s;
378 
379 	s = splhigh();
380 	n = netisr;
381 	netisr = 0;
382 	splx(s);
383 
384 #define DONETISR(bit, fn) do {		\
385 		if (n & (1 << bit))	\
386 			fn();		\
387 		} while (0)
388 
389 	s = splsoftnet();
390 
391 #include <net/netisr_dispatch.h>
392 
393 #undef DONETISR
394 
395 	splx(s);
396 }
397 
398 #if 0
399 /* ARGSUSED */
400 static int
401 spurintr(void *arg)
402 {
403 
404 	return (1);
405 }
406 #endif
407