1 /*	$NetBSD: footbridge_irqhandler.c,v 1.4 2002/01/07 22:58:07 chris Exp $	*/
2 
3 /*
4  * Copyright (c) 1994-1998 Mark Brinicombe.
5  * Copyright (c) 1997 Causality Limited
6  * Copyright (c) 1994 Brini.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. All advertising materials mentioning features or use of this software
18  *    must display the following acknowledgement:
19  *	This product includes software developed by Mark Brinicombe
20  *	for the NetBSD Project.
21  * 4. The name of the company nor the name of the author may be used to
22  *    endorse or promote products derived from this software without specific
23  *    prior written permission.
24  *
25  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
26  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
27  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
28  * IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
29  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35  * SUCH DAMAGE.
36  *
37  *	from: iomd_irqhandler.c,v 1.16 $
38  */
39 
40 #include "opt_irqstats.h"
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/syslog.h>
45 #include <sys/malloc.h>
46 #include <uvm/uvm_extern.h>
47 
48 #include <machine/intr.h>
49 #include <machine/cpu.h>
50 #include <arm/arm32/machdep.h>
51 
52 irqhandler_t *irqhandlers[NIRQS];
53 
54 int current_intr_depth;		/* Depth of interrupt nesting */
55 u_int intr_claimed_mask;	/* Interrupts that are claimed */
56 u_int intr_disabled_mask;	/* Interrupts that are temporarily disabled */
57 u_int intr_current_mask;	/* Interrupts currently allowable */
58 u_int spl_mask;
59 u_int irqmasks[IPL_LEVELS];
60 u_int irqblock[NIRQS];
61 
62 extern u_int soft_interrupts;	/* Only so we can initialise it */
63 
64 extern char *_intrnames;
65 
66 void stray_irqhandler __P((void));
67 
68 void
69 irq_init(void)
70 {
71 	int loop;
72 
73 	/* Clear all the IRQ handlers and the irq block masks */
74 	for (loop = 0; loop < NIRQS; ++loop) {
75 		irqhandlers[loop] = NULL;
76 		irqblock[loop] = 0;
77 	}
78 
79 	/*
80 	 * Setup the irqmasks for the different Interrupt Priority Levels
81 	 * We will start with no bits set and these will be updated as handlers
82 	 * are installed at different IPL's.
83 	 */
84 	for (loop = 0; loop < IPL_LEVELS; ++loop)
85 		irqmasks[loop] = 0;
86 
87 	current_intr_depth = 0;
88 	intr_claimed_mask = 0x00000000;
89 	intr_disabled_mask = 0x00000000;
90 	intr_current_mask = 0x00000000;
91 	spl_mask = 0x00000000;
92 	soft_interrupts = 0x00000000;
93 
94 	set_spl_masks();
95 	irq_setmasks();
96 
97 	/* Enable IRQ's and FIQ's */
98 	enable_interrupts(I32_bit | F32_bit);
99 }
100 
101 void
102 stray_irqhandler()
103 {
104 	panic("stray irq\n");
105 }
106 
107 /*
108  * void disable_irq(int irq)
109  *
110  * Disables a specific irq. The irq is removed from the master irq mask
111  *
112  * Use of this function outside this file is deprecated.
113  */
114 
115 void
116 disable_irq(irq)
117 	int irq;
118 {
119 	int oldirqstate;
120 
121 	oldirqstate = disable_interrupts(I32_bit);
122 	intr_claimed_mask &= ~(1 << irq);
123 	intr_current_mask = intr_claimed_mask & ~intr_disabled_mask;
124 	irq_setmasks();
125 	restore_interrupts(oldirqstate);
126 }
127 
128 
129 /*
130  * void enable_irq(int irq)
131  *
132  * Enables a specific irq. The irq is added to the master irq mask
133  * This routine should be used with caution. A handler should already
134  * be installed.
135  *
136  * Use of this function outside this file is deprecated.
137  */
138 
139 void
140 enable_irq(irq)
141 	int irq;
142 {
143 	u_int oldirqstate;
144 
145 	oldirqstate = disable_interrupts(I32_bit);
146 	intr_claimed_mask |= (1 << irq);
147 	intr_current_mask = intr_claimed_mask & ~intr_disabled_mask;
148 	irq_setmasks();
149 	restore_interrupts(oldirqstate);
150 }
151 
152 /*
153  * int irq_claim(int irq, irqhandler_t *handler)
154  *
155  * Enable an IRQ and install a handler for it.
156  */
157 
158 int
159 irq_claim(irq, handler)
160 	int irq;
161 	irqhandler_t *handler;
162 {
163 	int level;
164 	int loop;
165 
166 #ifdef DIAGNOSTIC
167 	/* Sanity check */
168 	if (handler == NULL)
169 		panic("NULL interrupt handler\n");
170 	if (handler->ih_func == NULL)
171 		panic("Interrupt handler does not have a function\n");
172 #endif	/* DIAGNOSTIC */
173 
174 	/*
175 	 * IRQ_INSTRUCT indicates that we should get the irq number
176 	 * from the irq structure
177 	 */
178 	if (irq == IRQ_INSTRUCT)
179 		irq = handler->ih_num;
180 
181 	/* Make sure the irq number is valid */
182 	if (irq < 0 || irq >= NIRQS)
183 		return(-1);
184 
185 	/* Make sure the level is valid */
186 	if (handler->ih_level < 0 || handler->ih_level >= IPL_LEVELS)
187     	        return(-1);
188 
189 	/* Attach handler at top of chain */
190 	handler->ih_next = irqhandlers[irq];
191 	irqhandlers[irq] = handler;
192 
193 	/*
194 	 * Reset the flags for this handler.
195 	 * As the handler is now in the chain mark it as active.
196 	 */
197 	handler->ih_flags = 0 | IRQ_FLAG_ACTIVE;
198 
199 	/*
200 	 * Record the interrupt number for accounting.
201 	 * Done here as the accounting number may not be the same as the IRQ number
202 	 * though for the moment they are
203 	 */
204 	handler->ih_num = irq;
205 
206 #ifdef IRQSTATS
207 	/* Get the interrupt name from the head of the list */
208 	if (handler->ih_name) {
209 		char *ptr = _intrnames + (irq * 14);
210 		strcpy(ptr, "             ");
211 		strncpy(ptr, handler->ih_name,
212 		    min(strlen(handler->ih_name), 13));
213 	} else {
214 		char *ptr = _intrnames + (irq * 14);
215 		sprintf(ptr, "irq %2d     ", irq);
216 	}
217 #endif	/* IRQSTATS */
218 
219 	/*
220 	 * Update the irq masks.
221 	 * If ih_level is out of range then don't bother to update
222 	 * the masks.
223 	 */
224 	if (handler->ih_level >= 0 && handler->ih_level < IPL_LEVELS) {
225 		irqhandler_t *ptr;
226 
227 		/*
228 		 * Find the lowest interrupt priority on the irq chain.
229 		 * Interrupt is allowable at priorities lower than this.
230 		 */
231 		ptr = irqhandlers[irq];
232 		if (ptr) {
233 			level = ptr->ih_level - 1;
234 			while (ptr) {
235 				if (ptr->ih_level - 1 < level)
236 					level = ptr->ih_level - 1;
237 				ptr = ptr->ih_next;
238 			}
239 			for (loop = 0; loop < IPL_LEVELS; ++loop) {
240 				if (level >= loop)
241 					irqmasks[loop] |= (1 << irq);
242 				else
243 					irqmasks[loop] &= ~(1 << irq);
244 			}
245 		}
246 
247 #include "sl.h"
248 #include "ppp.h"
249 #if NSL > 0 || NPPP > 0
250 		/* In the presence of SLIP or PPP, splimp > spltty. */
251 		irqmasks[IPL_NET] &= irqmasks[IPL_TTY];
252 #endif
253 	}
254 
255 	/*
256 	 * We now need to update the irqblock array. This array indicates
257 	 * what other interrupts should be blocked when interrupt is asserted
258 	 * This basically emulates hardware interrupt priorities e.g. by blocking
259 	 * all other IPL_BIO interrupts with an IPL_BIO interrupt is asserted.
260 	 * For each interrupt we find the highest IPL and set the block mask to
261 	 * the interrupt mask for that level.
262 	 */
263 	for (loop = 0; loop < NIRQS; ++loop) {
264 		irqhandler_t *ptr;
265 
266 		ptr = irqhandlers[loop];
267 		if (ptr) {
268 			/* There is at least 1 handler so scan the chain */
269  			level = ptr->ih_level;
270 			while (ptr) {
271 				if (ptr->ih_level > level)
272 					level = ptr->ih_level;
273 				ptr = ptr->ih_next;
274 			}
275 			irqblock[loop] = ~irqmasks[level];
276 		} else
277 			/* No handlers for this irq so nothing to block */
278 			irqblock[loop] = 0;
279 	}
280 
281 	enable_irq(irq);
282 	set_spl_masks();
283 
284 	return(0);
285 }
286 
287 
288 /*
289  * int irq_release(int irq, irqhandler_t *handler)
290  *
291  * Disable an IRQ and remove a handler for it.
292  */
293 
294 int
295 irq_release(irq, handler)
296 	int irq;
297 	irqhandler_t *handler;
298 {
299 	int level;
300 	int loop;
301 	irqhandler_t *irqhand;
302 	irqhandler_t **prehand;
303 #ifdef IRQSTATS
304 	extern char *_intrnames;
305 #endif	/* IRQSTATS */
306 	/*
307 	 * IRQ_INSTRUCT indicates that we should get the irq number
308 	 * from the irq structure
309 	 */
310 	if (irq == IRQ_INSTRUCT)
311 		irq = handler->ih_num;
312 
313 	/* Make sure the irq number is valid */
314 	if (irq < 0 || irq >= NIRQS)
315 		return(-1);
316 
317 	/* Locate the handler */
318 	irqhand = irqhandlers[irq];
319 	prehand = &irqhandlers[irq];
320 
321 	while (irqhand && handler != irqhand) {
322 		prehand = &irqhand->ih_next;
323 		irqhand = irqhand->ih_next;
324 	}
325 
326 	/* Remove the handler if located */
327 	if (irqhand)
328 		*prehand = irqhand->ih_next;
329 	else
330 		return(-1);
331 
332 	/* Now the handler has been removed from the chain mark is as inactive */
333 	irqhand->ih_flags &= ~IRQ_FLAG_ACTIVE;
334 
335 	/* Make sure the head of the handler list is active */
336 	if (irqhandlers[irq])
337 		irqhandlers[irq]->ih_flags |= IRQ_FLAG_ACTIVE;
338 
339 #ifdef IRQSTATS
340 	/* Get the interrupt name from the head of the list */
341 	if (irqhandlers[irq] && irqhandlers[irq]->ih_name) {
342 		char *ptr = _intrnames + (irq * 14);
343 		strcpy(ptr, "             ");
344 		strncpy(ptr, irqhandlers[irq]->ih_name,
345 		    min(strlen(irqhandlers[irq]->ih_name), 13));
346 	} else {
347 		char *ptr = _intrnames + (irq * 14);
348 		sprintf(ptr, "irq %2d     ", irq);
349 	}
350 #endif	/* IRQSTATS */
351 
352 	/*
353 	 * Update the irq masks.
354 	 * If ih_level is out of range then don't bother to update
355 	 * the masks.
356 	 */
357 	if (handler->ih_level >= 0 && handler->ih_level < IPL_LEVELS) {
358 		irqhandler_t *ptr;
359 
360 		/*
361 		 * Find the lowest interrupt priority on the irq chain.
362 		 * Interrupt is allowable at priorities lower than this.
363 		 */
364 		ptr = irqhandlers[irq];
365 		if (ptr) {
366 			level = ptr->ih_level - 1;
367 			while (ptr) {
368 				if (ptr->ih_level - 1 < level)
369 					level = ptr->ih_level - 1;
370 				ptr = ptr->ih_next;
371 			}
372 			for (loop = 0; loop < IPL_LEVELS; ++loop) {
373 				if (level >= loop)
374 					irqmasks[loop] |= (1 << irq);
375 				else
376 					irqmasks[loop] &= ~(1 << irq);
377 			}
378 		}
379 	}
380 
381 	/*
382 	 * We now need to update the irqblock array. This array indicates
383 	 * what other interrupts should be blocked when interrupt is asserted
384 	 * This basically emulates hardware interrupt priorities e.g. by
385 	 * blocking all other IPL_BIO interrupts with an IPL_BIO interrupt
386 	 * is asserted. For each interrupt we find the highest IPL and set
387 	 * the block mask to the interrupt mask for that level.
388 	 */
389 	for (loop = 0; loop < NIRQS; ++loop) {
390 		irqhandler_t *ptr;
391 
392 		ptr = irqhandlers[loop];
393 		if (ptr) {
394 			/* There is at least 1 handler so scan the chain */
395 			level = ptr->ih_level;
396 			while (ptr) {
397 				if (ptr->ih_level > level)
398 					level = ptr->ih_level;
399 				ptr = ptr->ih_next;
400 			}
401 			irqblock[loop] = ~irqmasks[level];
402 		} else
403 			/* No handlers for this irq so nothing to block */
404 			irqblock[loop] = 0;
405 	}
406 
407 	/*
408 	 * Disable the appropriate mask bit if there are no handlers left for
409 	 * this IRQ.
410 	 */
411 	if (irqhandlers[irq] == NULL)
412 		disable_irq(irq);
413 
414 	set_spl_masks();
415 
416 	return(0);
417 }
418 
419 
420 void *
421 intr_claim(irq, level, name, ih_func, ih_arg)
422 	int irq;
423 	int level;
424 	const char *name;
425 	int (*ih_func) __P((void *));
426 	void *ih_arg;
427 {
428 	irqhandler_t *ih;
429 
430 	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
431 	if (!ih)
432 		panic("intr_claim(): Cannot malloc handler memory\n");
433 
434 	ih->ih_level = level;
435 	ih->ih_name = name;
436 	ih->ih_func = ih_func;
437 	ih->ih_arg = ih_arg;
438 	ih->ih_flags = 0;
439 
440 	if (irq_claim(irq, ih) != 0)
441 		return(NULL);
442 	return(ih);
443 }
444 
445 
446 int
447 intr_release(arg)
448 	void *arg;
449 {
450 	irqhandler_t *ih = (irqhandler_t *)arg;
451 
452 	if (irq_release(ih->ih_num, ih) == 0) {
453 		free(ih, M_DEVBUF);
454 		return(0);
455 	}
456 	return(1);
457 }
458