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