xref: /netbsd/sys/arch/mvme68k/mvme68k/isr.c (revision 6550d01e)
1 /*	$NetBSD: isr.c,v 1.33 2010/12/20 00:25:39 matt 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  *
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  * Link and dispatch interrupts.
34  */
35 
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: isr.c,v 1.33 2010/12/20 00:25:39 matt Exp $");
38 
39 #include <sys/param.h>
40 #include <sys/systm.h>
41 #include <sys/malloc.h>
42 #include <sys/vmmeter.h>
43 #include <sys/device.h>
44 #include <sys/cpu.h>
45 
46 #include <uvm/uvm_extern.h>
47 
48 #include <mvme68k/mvme68k/isr.h>
49 
50 volatile unsigned int interrupt_depth;
51 isr_autovec_list_t isr_autovec[NISRAUTOVEC];
52 struct	isr_vectored isr_vectored[NISRVECTORED];
53 static const char irqgroupname[] = "hard irqs";
54 struct	evcnt mvme68k_irq_evcnt[] = {
55 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "spur"),
56 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev1"),
57 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev2"),
58 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev3"),
59 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev4"),
60 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev5"),
61 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev6"),
62 	EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "nmi")
63 };
64 static int idepth;
65 
66 extern	int intrcnt[];		/* from locore.s. XXXSCW: will go away soon */
67 extern	void (*vectab[])(void);
68 extern	void badtrap(void);
69 extern	void intrhand_vectored(void);
70 
71 static	int spurintr(void *);
72 
73 
74 void
75 isrinit(void)
76 {
77 	int i;
78 
79 	/* Initialize the autovector lists. */
80 	for (i = 0; i < NISRAUTOVEC; ++i)
81 		LIST_INIT(&isr_autovec[i]);
82 
83 	/* Initialise the interrupt event counts */
84 	for (i = 0; i < (sizeof(mvme68k_irq_evcnt) / sizeof(struct evcnt)); i++)
85 		evcnt_attach_static(&mvme68k_irq_evcnt[i]);
86 
87 	/* Arrange to trap Spurious and NMI auto-vectored Interrupts */
88 	isrlink_autovec(spurintr, NULL, 0, 0, NULL);
89 	isrlink_autovec(nmihand, NULL, 7, 0, NULL);
90 }
91 
92 /*
93  * Establish an autovectored interrupt handler.
94  * Called by driver attach functions.
95  */
96 void
97 isrlink_autovec(int (*func)(void *), void *arg, int ipl, int priority,
98     struct evcnt *evcnt)
99 {
100 	struct isr_autovec *newisr, *curisr;
101 	isr_autovec_list_t *list;
102 
103 #ifdef DIAGNOSTIC
104 	if ((ipl < 0) || (ipl >= NISRAUTOVEC))
105 		panic("%s: bad ipl %d", __func__, ipl);
106 #endif
107 
108 	newisr = malloc(sizeof(struct isr_autovec), M_DEVBUF, M_NOWAIT);
109 	if (newisr == NULL)
110 		panic("%s: can't allocate space for isr", __func__);
111 
112 	/* Fill in the new entry. */
113 	newisr->isr_func = func;
114 	newisr->isr_arg = arg;
115 	newisr->isr_ipl = ipl;
116 	newisr->isr_priority = priority;
117 	newisr->isr_evcnt = evcnt;
118 
119 	/*
120 	 * Some devices are particularly sensitive to interrupt
121 	 * handling latency.  The SCC, for example, can lose many
122 	 * characters if its interrupt isn't handled with reasonable
123 	 * speed.
124 	 *
125 	 * To work around this problem, each device can give itself a
126 	 * "priority".  An unbuffered SCC would give itself a higher
127 	 * priority than a SCSI device, for example.
128 	 *
129 	 * This solution was originally developed for the hp300, which
130 	 * has a flat spl scheme (by necessity).  Thankfully, the
131 	 * MVME systems don't have this problem, though this may serve
132 	 * a useful purpose in any case.
133 	 */
134 
135 	/*
136 	 * Get the appropriate ISR list.  If the list is empty, no
137 	 * additional work is necessary; we simply insert ourselves
138 	 * at the head of the list.
139 	 */
140 	list = &isr_autovec[ipl];
141 	if (list->lh_first == NULL) {
142 		LIST_INSERT_HEAD(list, newisr, isr_link);
143 		return;
144 	}
145 
146 	/*
147 	 * A little extra work is required.  We traverse the list
148 	 * and place ourselves after any ISRs with our current (or
149 	 * higher) priority.
150 	 */
151 	for (curisr = list->lh_first; curisr->isr_link.le_next != NULL;
152 	    curisr = curisr->isr_link.le_next) {
153 		if (newisr->isr_priority > curisr->isr_priority) {
154 			LIST_INSERT_BEFORE(curisr, newisr, isr_link);
155 			return;
156 		}
157 	}
158 
159 	/*
160 	 * We're the least important entry, it seems.  We just go
161 	 * on the end.
162 	 */
163 	LIST_INSERT_AFTER(curisr, newisr, isr_link);
164 }
165 
166 /*
167  * Establish a vectored interrupt handler.
168  * Called by bus interrupt establish functions.
169  */
170 void
171 isrlink_vectored(int (*func)(void *), void *arg, int ipl, int vec,
172     struct evcnt *evcnt)
173 {
174 	struct isr_vectored *isr;
175 
176 #ifdef DIAGNOSTIC
177 	if ((ipl < 0) || (ipl >= NISRAUTOVEC))
178 		panic("%s: bad ipl %d", __func__, ipl);
179 	if ((vec < ISRVECTORED) || (vec >= ISRVECTORED + NISRVECTORED))
180 		panic("%s: bad vec 0x%x", __func__, vec);
181 #endif
182 
183 	isr = &isr_vectored[vec - ISRVECTORED];
184 
185 #ifdef DIAGNOSTIC
186 	if ((vectab[vec] != badtrap) || (isr->isr_func != NULL))
187 		panic("%s: vec 0x%x not available", __func__, vec);
188 #endif
189 
190 	/* Fill in the new entry. */
191 	isr->isr_func = func;
192 	isr->isr_arg = arg;
193 	isr->isr_ipl = ipl;
194 	isr->isr_evcnt = evcnt;
195 
196 	/* Hook into the vector table. */
197 	vectab[vec] = intrhand_vectored;
198 }
199 
200 /*
201  * Return a pointer to the evcnt structure for
202  * the specified ipl.
203  */
204 struct evcnt *
205 isrlink_evcnt(int ipl)
206 {
207 
208 #ifdef DIAGNOSTIC
209 	if (ipl < 0 ||
210 	    ipl >= (sizeof(mvme68k_irq_evcnt) / sizeof(struct evcnt)))
211 		panic("%s: bad ipl %d", __func__, ipl);
212 #endif
213 
214 	return &mvme68k_irq_evcnt[ipl];
215 }
216 
217 /*
218  * Unhook a vectored interrupt.
219  */
220 void
221 isrunlink_vectored(int vec)
222 {
223 
224 #ifdef DIAGNOSTIC
225 	if ((vec < ISRVECTORED) || (vec >= ISRVECTORED + NISRVECTORED))
226 		panic("%s: bad vec 0x%x", __func__, vec);
227 
228 	if (vectab[vec] != intrhand_vectored)
229 		panic("%s: not vectored interrupt", __func__);
230 #endif
231 
232 	vectab[vec] = badtrap;
233 	memset(&isr_vectored[vec - ISRVECTORED], 0,
234 	    sizeof(struct isr_vectored));
235 }
236 
237 /*
238  * This is the dispatcher called by the low-level
239  * assembly language autovectored interrupt routine.
240  */
241 void
242 isrdispatch_autovec(struct clockframe *frame)
243 {
244 	struct isr_autovec *isr;
245 	isr_autovec_list_t *list;
246 	int handled, ipl;
247 	void *arg;
248 	static int straycount, unexpected;
249 
250 	idepth++;
251 	ipl = (frame->vec >> 2) - ISRAUTOVEC;
252 
253 #ifdef DIAGNOSTIC
254 	if ((ipl < 0) || (ipl >= NISRAUTOVEC))
255 		panic("%s: bad vec 0x%x", __func__, frame->vec);
256 #endif
257 
258 	intrcnt[ipl]++;	/* XXXSCW: Will go away soon */
259 	mvme68k_irq_evcnt[ipl].ev_count++;
260 	curcpu()->ci_data.cpu_nintr++;
261 
262 	list = &isr_autovec[ipl];
263 	if (list->lh_first == NULL) {
264 		printf("%s: ipl %d unexpected\n", __func__, ipl);
265 		if (++unexpected > 10)
266 			panic("too many unexpected interrupts");
267 		idepth--;
268 		return;
269 	}
270 
271 	/* Give all the handlers a chance. */
272 	handled = 0;
273 	for (isr = list->lh_first ; isr != NULL; isr = isr->isr_link.le_next) {
274 		arg = isr->isr_arg ? isr->isr_arg : frame;
275 		if ((*isr->isr_func)(arg) != 0) {
276 			if (isr->isr_evcnt)
277 				isr->isr_evcnt->ev_count++;
278 			handled++;
279 		}
280 	}
281 
282 	if (handled)
283 		straycount = 0;
284 	else if (++straycount > 50)
285 		panic("%s: too many stray interrupts", __func__);
286 	else
287 		printf("%s: stray level %d interrupt\n", __func__, ipl);
288 
289 	idepth--;
290 }
291 
292 /*
293  * This is the dispatcher called by the low-level
294  * assembly language vectored interrupt routine.
295  */
296 void
297 isrdispatch_vectored(int ipl, struct clockframe *frame)
298 {
299 	struct isr_vectored *isr;
300 	int vec;
301 
302 	idepth++;
303 	vec = (frame->vec >> 2) - ISRVECTORED;
304 
305 #ifdef DIAGNOSTIC
306 	if ((vec < 0) || (vec >= NISRVECTORED))
307 		panic("%s: bad vec 0x%x", __func__, frame->vec);
308 #endif
309 
310 	isr = &isr_vectored[vec];
311 
312 	intrcnt[ipl]++;	/* XXXSCW: Will go away soon */
313 	mvme68k_irq_evcnt[ipl].ev_count++;
314 	curcpu()->ci_data.cpu_nintr++;
315 
316 	if (isr->isr_func == NULL) {
317 		printf("%s: no handler for vec 0x%x\n", __func__, frame->vec);
318 		vectab[vec + ISRVECTORED] = badtrap;
319 		idepth--;
320 		return;
321 	}
322 
323 	/*
324 	 * Handler gets exception frame if argument is NULL.
325 	 */
326 	if ((*isr->isr_func)(isr->isr_arg ? isr->isr_arg : frame) == 0)
327 		printf("%s: vec 0x%x not claimed\n", __func__, frame->vec);
328 	else if (isr->isr_evcnt)
329 		isr->isr_evcnt->ev_count++;
330 	idepth--;
331 }
332 
333 bool
334 cpu_intr_p(void)
335 {
336 
337 	return idepth != 0;
338 }
339 
340 /* ARGSUSED */
341 static int
342 spurintr(void *arg)
343 {
344 
345 	return 1;
346 }
347