xref: /netbsd/sys/arch/hp300/hp300/intr.c (revision c4a72b64)
1 /*	$NetBSD: intr.c,v 1.23 2002/09/27 15:36:02 provos Exp $	*/
2 
3 /*-
4  * Copyright (c) 1996, 1997, 1999 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/cdefs.h>
44 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.23 2002/09/27 15:36:02 provos Exp $");
45 
46 #define _HP300_INTR_H_PRIVATE
47 
48 #include <sys/param.h>
49 #include <sys/systm.h>
50 #include <sys/malloc.h>
51 #include <sys/vmmeter.h>
52 
53 #include <uvm/uvm_extern.h>
54 
55 #include <net/netisr.h>
56 
57 #include <machine/cpu.h>
58 #include <machine/intr.h>
59 
60 /*
61  * The location and size of the autovectored interrupt portion
62  * of the vector table.
63  */
64 #define ISRLOC		0x18
65 #define NISR		8
66 
67 struct hp300_intr hp300_intr_list[NISR];
68 static const char *hp300_intr_names[NISR] = {
69 	"spurious",
70 	"lev1",
71 	"lev2",
72 	"lev3",
73 	"lev4",
74 	"lev5",
75 	"clock",
76 	"nmi",
77 };
78 
79 u_short hp300_ipls[HP300_NIPLS];
80 
81 void	intr_computeipl __P((void));
82 void	netintr __P((void));
83 
84 void
85 intr_init()
86 {
87 	struct hp300_intr *hi;
88 	int i;
89 
90 	/* Initialize the ISR lists. */
91 	for (i = 0; i < NISR; ++i) {
92 		hi = &hp300_intr_list[i];
93 		LIST_INIT(&hi->hi_q);
94 		evcnt_attach_dynamic(&hi->hi_evcnt, EVCNT_TYPE_INTR,
95 		    NULL, hp300_intr_names[i], "intr");
96 	}
97 
98 	/* Default interrupt priorities. */
99 	hp300_ipls[HP300_IPL_SOFT] = PSL_S|PSL_IPL1;
100 	hp300_ipls[HP300_IPL_BIO] = PSL_S|PSL_IPL3;
101 	hp300_ipls[HP300_IPL_NET] = PSL_S|PSL_IPL3;
102 	hp300_ipls[HP300_IPL_TTY] = PSL_S|PSL_IPL3;
103 	hp300_ipls[HP300_IPL_VM] = PSL_S|PSL_IPL3;
104 	hp300_ipls[HP300_IPL_CLOCK] = PSL_S|PSL_IPL6;
105 	hp300_ipls[HP300_IPL_HIGH] = PSL_S|PSL_IPL7;
106 }
107 
108 /*
109  * Scan all of the ISRs, recomputing the interrupt levels for the spl*()
110  * calls.  This doesn't have to be fast.
111  */
112 void
113 intr_computeipl()
114 {
115 	struct hp300_intrhand *ih;
116 	int ipl;
117 
118 	/* Start with low values. */
119 	hp300_ipls[HP300_IPL_BIO] =
120 	hp300_ipls[HP300_IPL_NET] =
121 	hp300_ipls[HP300_IPL_TTY] =
122 	hp300_ipls[HP300_IPL_VM] = PSL_S|PSL_IPL3;
123 
124 	for (ipl = 0; ipl < NISR; ipl++) {
125 		for (ih = LIST_FIRST(&hp300_intr_list[ipl].hi_q); ih != NULL;
126 		    ih = LIST_NEXT(ih, ih_q)) {
127 			/*
128 			 * Bump up the level for a given priority,
129 			 * if necessary.
130 			 */
131 			switch (ih->ih_priority) {
132 			case IPL_BIO:
133 				if (ipl > PSLTOIPL(hp300_ipls[HP300_IPL_BIO]))
134 					hp300_ipls[HP300_IPL_BIO] =
135 					    IPLTOPSL(ipl);
136 				break;
137 
138 			case IPL_NET:
139 				if (ipl > PSLTOIPL(hp300_ipls[HP300_IPL_NET]))
140 					hp300_ipls[HP300_IPL_NET] =
141 					    IPLTOPSL(ipl);
142 				break;
143 
144 			case IPL_TTY:
145 			case IPL_TTYNOBUF:
146 				if (ipl > PSLTOIPL(hp300_ipls[HP300_IPL_TTY]))
147 					hp300_ipls[HP300_IPL_TTY] =
148 					    IPLTOPSL(ipl);
149 				break;
150 
151 			default:
152 				printf("priority = %d\n", ih->ih_priority);
153 				panic("intr_computeipl: bad priority");
154 			}
155 		}
156 	}
157 
158 	/*
159 	 * Enforce `bio <= net <= tty <= imp'
160 	 */
161 
162 	if (hp300_ipls[HP300_IPL_NET] < hp300_ipls[HP300_IPL_BIO])
163 		hp300_ipls[HP300_IPL_NET] = hp300_ipls[HP300_IPL_BIO];
164 
165 	if (hp300_ipls[HP300_IPL_TTY] < hp300_ipls[HP300_IPL_NET])
166 		hp300_ipls[HP300_IPL_TTY] = hp300_ipls[HP300_IPL_NET];
167 
168 	if (hp300_ipls[HP300_IPL_VM] < hp300_ipls[HP300_IPL_TTY])
169 		hp300_ipls[HP300_IPL_VM] = hp300_ipls[HP300_IPL_TTY];
170 }
171 
172 void
173 intr_printlevels()
174 {
175 
176 #ifdef DEBUG
177 	printf("psl: bio = 0x%x, net = 0x%x, tty = 0x%x, imp = 0x%x\n",
178 	    hp300_ipls[HP300_IPL_BIO], hp300_ipls[HP300_IPL_NET],
179 	    hp300_ipls[HP300_IPL_TTY], hp300_ipls[HP300_IPL_VM]);
180 #endif
181 
182 	printf("interrupt levels: bio = %d, net = %d, tty = %d\n",
183 	    PSLTOIPL(hp300_ipls[HP300_IPL_BIO]),
184 	    PSLTOIPL(hp300_ipls[HP300_IPL_NET]),
185 	    PSLTOIPL(hp300_ipls[HP300_IPL_TTY]));
186 }
187 
188 /*
189  * Establish an interrupt handler.
190  * Called by driver attach functions.
191  */
192 void *
193 intr_establish(func, arg, ipl, priority)
194 	int (*func) __P((void *));
195 	void *arg;
196 	int ipl;
197 	int priority;
198 {
199 	struct hp300_intrhand *newih, *curih;
200 
201 	if ((ipl < 0) || (ipl >= NISR))
202 		panic("intr_establish: bad ipl %d", ipl);
203 
204 	MALLOC(newih, struct hp300_intrhand *, sizeof(struct hp300_intrhand),
205 	    M_DEVBUF, M_NOWAIT);
206 	if (newih == NULL)
207 		panic("intr_establish: can't allocate space for handler");
208 
209 	/* Fill in the new entry. */
210 	newih->ih_fn = func;
211 	newih->ih_arg = arg;
212 	newih->ih_ipl = ipl;
213 	newih->ih_priority = priority;
214 
215 	/*
216 	 * Some devices are particularly sensitive to interrupt
217 	 * handling latency.  The DCA, for example, can lose many
218 	 * characters if its interrupt isn't handled with reasonable
219 	 * speed.  For this reason, we sort ISRs by IPL_* priority,
220 	 * inserting higher priority interrupts before lower priority
221 	 * interrupts.
222 	 */
223 
224 	/*
225 	 * Get the appropriate ISR list.  If the list is empty, no
226 	 * additional work is necessary; we simply insert ourselves
227 	 * at the head of the list.
228 	 */
229 
230 	if (LIST_FIRST(&hp300_intr_list[ipl].hi_q) == NULL) {
231 		LIST_INSERT_HEAD(&hp300_intr_list[ipl].hi_q, newih, ih_q);
232 		goto compute;
233 	}
234 
235 	/*
236 	 * A little extra work is required.  We traverse the list
237 	 * and place ourselves after any ISRs with our current (or
238 	 * higher) priority.
239 	 */
240 
241         for (curih = LIST_FIRST(&hp300_intr_list[ipl].hi_q);
242 	    LIST_NEXT(curih,ih_q) != NULL;
243 	    curih = LIST_NEXT(curih,ih_q)) {
244 		if (newih->ih_priority > curih->ih_priority) {
245 			LIST_INSERT_BEFORE(curih, newih, ih_q);
246 			goto compute;
247 		}
248 	}
249 
250 	/*
251 	 * We're the least important entry, it seems.  We just go
252 	 * on the end.
253 	 */
254 	LIST_INSERT_AFTER(curih, newih, ih_q);
255 
256  compute:
257 	/* Compute new interrupt levels. */
258 	intr_computeipl();
259 	return (newih);
260 }
261 
262 /*
263  * Disestablish an interrupt handler.
264  */
265 void
266 intr_disestablish(arg)
267 	void *arg;
268 {
269 	struct hp300_intrhand *ih = arg;
270 
271 	LIST_REMOVE(ih, ih_q);
272 	free(ih, M_DEVBUF);
273 	intr_computeipl();
274 }
275 
276 /*
277  * This is the dispatcher called by the low-level
278  * assembly language interrupt routine.
279  */
280 void
281 intr_dispatch(evec)
282 	int evec;		/* format | vector offset */
283 {
284 	struct hp300_intrhand *ih;
285 	struct hp300_intr *list;
286 	int handled, ipl, vec;
287 	static int straycount, unexpected;
288 
289 	vec = (evec & 0xfff) >> 2;
290 	if ((vec < ISRLOC) || (vec >= (ISRLOC + NISR)))
291 		panic("intr_dispatch: bad vec 0x%x", vec);
292 	ipl = vec - ISRLOC;
293 
294 	hp300_intr_list[ipl].hi_evcnt.ev_count++;
295 	uvmexp.intrs++;
296 
297 	list = &hp300_intr_list[ipl];
298 	if (LIST_FIRST(&list->hi_q) == NULL) {
299 		printf("intr_dispatch: ipl %d unexpected\n", ipl);
300 		if (++unexpected > 10)
301 			panic("intr_dispatch: too many unexpected interrupts");
302 		return;
303 	}
304 
305 	handled = 0;
306 	/* Give all the handlers a chance. */
307 	for (ih = LIST_FIRST(&list->hi_q) ; ih != NULL;
308 	    ih = LIST_NEXT(ih, ih_q))
309 		handled |= (*ih->ih_fn)(ih->ih_arg);
310 
311 	if (handled)
312 		straycount = 0;
313 	else if (++straycount > 50)
314 		panic("intr_dispatch: too many stray interrupts");
315 	else
316 		printf("intr_dispatch: stray level %d interrupt\n", ipl);
317 }
318 
319 void
320 netintr()
321 {
322 	int s, isr;
323 
324 	for (;;) {
325 		s = splhigh();
326 		isr = netisr;
327 		netisr = 0;
328 		splx(s);
329 
330 		if (isr == 0)
331 			return;
332 
333 #define DONETISR(bit, fn) do {			\
334 		if (isr & (1 << bit))		\
335 			fn();			\
336 		} while(0)
337 
338 		s = splsoftnet();
339 
340 #include <net/netisr_dispatch.h>
341 
342 #undef DONETISR
343 
344 		splx(s);
345 	}
346 }
347