xref: /netbsd/sys/arch/arm/iomd/iomd_irqhandler.c (revision c4a72b64)
1 /*	$NetBSD: iomd_irqhandler.c,v 1.6 2002/09/27 15:35:46 provos Exp $	*/
2 
3 /*
4  * Copyright (c) 1994-1998 Mark Brinicombe.
5  * Copyright (c) 1994 Brini.
6  * All rights reserved.
7  *
8  * This code is derived from software written for Brini by Mark Brinicombe
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 Mark Brinicombe
21  *	for the NetBSD Project.
22  * 4. The name of the company nor the name of the author may be used to
23  *    endorse or promote products derived from this software without specific
24  *    prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
27  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
28  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
30  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
31  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
35  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36  *
37  * IRQ/FIQ initialisation, claim, release and handler routines
38  *
39  *	from: irqhandler.c,v 1.14 1997/04/02 21:52:19 christos Exp $
40  */
41 
42 #include "opt_irqstats.h"
43 
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/syslog.h>
47 #include <sys/malloc.h>
48 #include <uvm/uvm_extern.h>
49 
50 #include <arm/iomd/iomdreg.h>
51 #include <arm/iomd/iomdvar.h>
52 
53 #include <machine/intr.h>
54 #include <machine/cpu.h>
55 #include <arm/arm32/katelib.h>
56 
57 irqhandler_t *irqhandlers[NIRQS];
58 
59 int current_intr_depth;
60 u_int current_mask;
61 u_int actual_mask;
62 u_int disabled_mask;
63 u_int spl_mask;
64 u_int irqmasks[IPL_LEVELS];
65 u_int irqblock[NIRQS];
66 
67 extern u_int soft_interrupts;	/* Only so we can initialise it */
68 
69 extern char *_intrnames;
70 
71 /* Prototypes */
72 
73 extern void set_spl_masks	__P((void));
74 
75 /*
76  * void irq_init(void)
77  *
78  * Initialise the IRQ/FIQ sub system
79  */
80 
81 void
82 irq_init()
83 {
84 	int loop;
85 
86 	/* Clear all the IRQ handlers and the irq block masks */
87 	for (loop = 0; loop < NIRQS; ++loop) {
88 		irqhandlers[loop] = NULL;
89 		irqblock[loop] = 0;
90 	}
91 
92 	/* Clear the IRQ/FIQ masks in the IOMD */
93 	IOMD_WRITE_BYTE(IOMD_IRQMSKA, 0x00);
94 	IOMD_WRITE_BYTE(IOMD_IRQMSKB, 0x00);
95 
96 	switch (IOMD_ID) {
97 	case RPC600_IOMD_ID:
98 		break;
99 	case ARM7500_IOC_ID:
100 	case ARM7500FE_IOC_ID:
101 		IOMD_WRITE_BYTE(IOMD_IRQMSKC, 0x00);
102 		IOMD_WRITE_BYTE(IOMD_IRQMSKD, 0x00);
103 		break;
104 	default:
105 		printf("Unknown IOMD id (%d) found in irq_init()\n", IOMD_ID);
106 	};
107 
108 	IOMD_WRITE_BYTE(IOMD_FIQMSK, 0x00);
109 	IOMD_WRITE_BYTE(IOMD_DMAMSK, 0x00);
110 
111 	/*
112 	 * Setup the irqmasks for the different Interrupt Priority Levels
113 	 * We will start with no bits set and these will be updated as handlers
114 	 * are installed at different IPL's.
115 	 */
116 	for (loop = 0; loop < IPL_LEVELS; ++loop)
117 		irqmasks[loop] = 0;
118 
119 	current_intr_depth = 0;
120 	current_mask = 0x00000000;
121 	disabled_mask = 0x00000000;
122 	actual_mask = 0x00000000;
123 	spl_mask = 0x00000000;
124 	soft_interrupts = 0x00000000;
125 
126 	set_spl_masks();
127 
128 	/* Enable IRQ's and FIQ's */
129 	enable_interrupts(I32_bit | F32_bit);
130 }
131 
132 
133 /*
134  * int irq_claim(int irq, irqhandler_t *handler)
135  *
136  * Enable an IRQ and install a handler for it.
137  */
138 
139 int
140 irq_claim(irq, handler)
141 	int irq;
142 	irqhandler_t *handler;
143 {
144 	int level;
145 	int loop;
146 
147 #ifdef DIAGNOSTIC
148 	/* Sanity check */
149 	if (handler == NULL)
150 		panic("NULL interrupt handler");
151 	if (handler->ih_func == NULL)
152 		panic("Interrupt handler does not have a function");
153 #endif	/* DIAGNOSTIC */
154 
155 	/*
156 	 * IRQ_INSTRUCT indicates that we should get the irq number
157 	 * from the irq structure
158 	 */
159 	if (irq == IRQ_INSTRUCT)
160 		irq = handler->ih_num;
161 
162 	/* Make sure the irq number is valid */
163 	if (irq < 0 || irq >= NIRQS)
164 		return(-1);
165 
166 	/* Make sure the level is valid */
167 	if (handler->ih_level < 0 || handler->ih_level >= IPL_LEVELS)
168     	        return(-1);
169 
170 	/* Attach handler at top of chain */
171 	handler->ih_next = irqhandlers[irq];
172 	irqhandlers[irq] = handler;
173 
174 	/*
175 	 * Reset the flags for this handler.
176 	 * As the handler is now in the chain mark it as active.
177 	 */
178 	handler->ih_flags = 0 | IRQ_FLAG_ACTIVE;
179 
180 	/*
181 	 * Record the interrupt number for accounting.
182 	 * Done here as the accounting number may not be the same as the
183 	 * IRQ number though for the moment they are
184 	 */
185 	handler->ih_num = irq;
186 
187 #ifdef IRQSTATS
188 	/* Get the interrupt name from the head of the list */
189 	if (handler->ih_name) {
190 		char *ptr = _intrnames + (irq * 14);
191 		strcpy(ptr, "             ");
192 		strncpy(ptr, handler->ih_name,
193 		    min(strlen(handler->ih_name), 13));
194 	} else {
195 		char *ptr = _intrnames + (irq * 14);
196 		sprintf(ptr, "irq %2d     ", irq);
197 	}
198 #endif	/* IRQSTATS */
199 
200 	/*
201 	 * Update the irq masks.
202 	 * Find the lowest interrupt priority on the irq chain.
203 	 * Interrupt is allowable at priorities lower than this.
204 	 * If ih_level is out of range then don't bother to update
205 	 * the masks.
206 	 */
207 	if (handler->ih_level >= 0 && handler->ih_level < IPL_LEVELS) {
208 		irqhandler_t *ptr;
209 
210 		/*
211 		 * Find the lowest interrupt priority on the irq chain.
212 		 * Interrupt is allowable at priorities lower than this.
213 		 */
214 		ptr = irqhandlers[irq];
215 		if (ptr) {
216 			int max_level;
217 
218 			level = ptr->ih_level - 1;
219 			max_level = ptr->ih_level - 1;
220 			while (ptr) {
221 				if (ptr->ih_level - 1 < level)
222 					level = ptr->ih_level - 1;
223 				else if (ptr->ih_level - 1 > max_level)
224 					max_level = ptr->ih_level - 1;
225 				ptr = ptr->ih_next;
226 			}
227 			/* Clear out any levels that we cannot now allow */
228 			while (max_level >=0 && max_level > level) {
229 				irqmasks[max_level] &= ~(1 << irq);
230 				--max_level;
231 			}
232 			while (level >= 0) {
233 				irqmasks[level] |= (1 << irq);
234 				--level;
235 			}
236 		}
237 
238 #include "sl.h"
239 #include "ppp.h"
240 #if NSL > 0 || NPPP > 0
241 		/* In the presence of SLIP or PPP, splimp > spltty. */
242 		irqmasks[IPL_NET] &= irqmasks[IPL_TTY];
243 #endif
244 	}
245 
246 	/*
247 	 * We now need to update the irqblock array. This array indicates
248 	 * what other interrupts should be blocked when interrupt is asserted
249 	 * This basically emulates hardware interrupt priorities e.g. by
250 	 * blocking all other IPL_BIO interrupts with an IPL_BIO interrupt
251 	 * is asserted. For each interrupt we find the highest IPL and set
252 	 * the block mask to the interrupt mask for that level.
253 	 */
254 	for (loop = 0; loop < NIRQS; ++loop) {
255 		irqhandler_t *ptr;
256 
257 		ptr = irqhandlers[loop];
258 		if (ptr) {
259 			/* There is at least 1 handler so scan the chain */
260 			level = ptr->ih_level;
261 			while (ptr) {
262 				if (ptr->ih_level > level)
263 					level = ptr->ih_level;
264 				ptr = ptr->ih_next;
265 			}
266 			irqblock[loop] = ~irqmasks[level];
267 		} else
268 			/* No handlers for this irq so nothing to block */
269 			irqblock[loop] = 0;
270 	}
271 
272 	enable_irq(irq);
273 	set_spl_masks();
274 
275 	return(0);
276 }
277 
278 
279 /*
280  * int irq_release(int irq, irqhandler_t *handler)
281  *
282  * Disable an IRQ and remove a handler for it.
283  */
284 
285 int
286 irq_release(irq, handler)
287 	int irq;
288 	irqhandler_t *handler;
289 {
290 	int level;
291 	int loop;
292 	irqhandler_t *irqhand;
293 	irqhandler_t **prehand;
294 #ifdef IRQSTATS
295 	extern char *_intrnames;
296 #endif
297 
298 	/*
299 	 * IRQ_INSTRUCT indicates that we should get the irq number
300 	 * from the irq structure
301 	 */
302 	if (irq == IRQ_INSTRUCT)
303 		irq = handler->ih_num;
304 
305 	/* Make sure the irq number is valid */
306 	if (irq < 0 || irq >= NIRQS)
307 		return(-1);
308 
309 	/* Locate the handler */
310 	irqhand = irqhandlers[irq];
311 	prehand = &irqhandlers[irq];
312 
313 	while (irqhand && handler != irqhand) {
314 		prehand = &irqhand->ih_next;
315 		irqhand = irqhand->ih_next;
316 	}
317 
318 	/* Remove the handler if located */
319 	if (irqhand)
320 		*prehand = irqhand->ih_next;
321 	else
322 		return(-1);
323 
324 	/* Now the handler has been removed from the chain mark is as inactive */
325 	irqhand->ih_flags &= ~IRQ_FLAG_ACTIVE;
326 
327 	/* Make sure the head of the handler list is active */
328 	if (irqhandlers[irq])
329 		irqhandlers[irq]->ih_flags |= IRQ_FLAG_ACTIVE;
330 
331 #ifdef IRQSTATS
332 	/* Get the interrupt name from the head of the list */
333 	if (irqhandlers[irq] && irqhandlers[irq]->ih_name) {
334 		char *ptr = _intrnames + (irq * 14);
335 		strcpy(ptr, "             ");
336 		strncpy(ptr, irqhandlers[irq]->ih_name,
337 		    min(strlen(irqhandlers[irq]->ih_name), 13));
338 	} else {
339 		char *ptr = _intrnames + (irq * 14);
340 		sprintf(ptr, "irq %2d     ", irq);
341 	}
342 #endif	/* IRQSTATS */
343 
344 	/*
345 	 * Update the irq masks.
346 	 * If ih_level is out of range then don't bother to update
347 	 * the masks.
348 	 */
349 	if (handler->ih_level >= 0 && handler->ih_level < IPL_LEVELS) {
350 		irqhandler_t *ptr;
351 
352 		/* Clean the bit from all the masks */
353 		for (level = 0; level < IPL_LEVELS; ++level)
354 			irqmasks[level] &= ~(1 << irq);
355 
356 		/*
357 		 * Find the lowest interrupt priority on the irq chain.
358 		 * Interrupt is allowable at priorities lower than this.
359 		 */
360 		ptr = irqhandlers[irq];
361 		if (ptr) {
362 			level = ptr->ih_level - 1;
363 			while (ptr) {
364 				if (ptr->ih_level - 1 < level)
365 					level = ptr->ih_level - 1;
366 				ptr = ptr->ih_next;
367 			}
368 			while (level >= 0) {
369 				irqmasks[level] |= (1 << irq);
370 				--level;
371 			}
372 		}
373 	}
374 
375 	/*
376 	 * We now need to update the irqblock array. This array indicates
377 	 * what other interrupts should be blocked when interrupt is asserted
378 	 * This basically emulates hardware interrupt priorities e.g. by
379 	 * blocking all other IPL_BIO interrupts with an IPL_BIO interrupt
380 	 * is asserted. For each interrupt we find the highest IPL and set
381 	 * the block mask to the interrupt mask for that level.
382 	 */
383 	for (loop = 0; loop < NIRQS; ++loop) {
384 		irqhandler_t *ptr;
385 
386 		ptr = irqhandlers[loop];
387 		if (ptr) {
388 			/* There is at least 1 handler so scan the chain */
389 			level = ptr->ih_level;
390 			while (ptr) {
391 				if (ptr->ih_level > level)
392 					level = ptr->ih_level;
393 				ptr = ptr->ih_next;
394 			}
395 			irqblock[loop] = ~irqmasks[level];
396 		} else
397 			/* No handlers for this irq so nothing to block */
398 			irqblock[loop] = 0;
399 	}
400 
401 	/*
402 	 * Disable the appropriate mask bit if there are no handlers left for
403 	 * this IRQ.
404 	 */
405 	if (irqhandlers[irq] == NULL)
406 		disable_irq(irq);
407 
408 	set_spl_masks();
409 
410 	return(0);
411 }
412 
413 
414 void *
415 intr_claim(irq, level, name, ih_func, ih_arg)
416 	int irq;
417 	int level;
418 	const char *name;
419 	int (*ih_func) __P((void *));
420 	void *ih_arg;
421 {
422 	irqhandler_t *ih;
423 
424 	ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT);
425 	if (!ih)
426 		panic("intr_claim(): Cannot malloc handler memory");
427 
428 	ih->ih_level = level;
429 	ih->ih_name = name;
430 	ih->ih_func = ih_func;
431 	ih->ih_arg = ih_arg;
432 	ih->ih_flags = 0;
433 
434 	if (irq_claim(irq, ih) != 0)
435 		return(NULL);
436 	return(ih);
437 }
438 
439 
440 int
441 intr_release(arg)
442 	void *arg;
443 {
444 	irqhandler_t *ih = (irqhandler_t *)arg;
445 
446 	if (irq_release(ih->ih_num, ih) == 0) {
447 		free(ih, M_DEVBUF);
448 		return(0);
449 	}
450 	return(1);
451 }
452 
453 #if 0
454 u_int
455 disable_interrupts(mask)
456 	u_int mask;
457 {
458 	u_int cpsr;
459 
460 	cpsr = SetCPSR(mask, mask);
461 	return(cpsr);
462 }
463 
464 
465 u_int
466 restore_interrupts(old_cpsr)
467 	u_int old_cpsr;
468 {
469 	int mask = I32_bit | F32_bit;
470 	return(SetCPSR(mask, old_cpsr & mask));
471 }
472 
473 
474 u_int
475 enable_interrupts(mask)
476 	u_int mask;
477 {
478 	return(SetCPSR(mask, 0));
479 }
480 #endif
481 
482 /*
483  * void disable_irq(int irq)
484  *
485  * Disables a specific irq. The irq is removed from the master irq mask
486  */
487 
488 void
489 disable_irq(irq)
490 	int irq;
491 {
492 	u_int oldirqstate;
493 
494 	oldirqstate = disable_interrupts(I32_bit);
495 	current_mask &= ~(1 << irq);
496 	irq_setmasks();
497 	restore_interrupts(oldirqstate);
498 }
499 
500 
501 /*
502  * void enable_irq(int irq)
503  *
504  * Enables a specific irq. The irq is added to the master irq mask
505  * This routine should be used with caution. A handler should already
506  * be installed.
507  */
508 
509 void
510 enable_irq(irq)
511 	int irq;
512 {
513 	u_int oldirqstate;
514 
515 	oldirqstate = disable_interrupts(I32_bit);
516 	current_mask |= (1 << irq);
517 	irq_setmasks();
518 	restore_interrupts(oldirqstate);
519 }
520 
521 
522 /*
523  * void stray_irqhandler(u_int mask)
524  *
525  * Handler for stray interrupts. This gets called if a handler cannot be
526  * found for an interrupt.
527  */
528 
529 void
530 stray_irqhandler(mask)
531 	u_int mask;
532 {
533 	static u_int stray_irqs = 0;
534 
535 	if (++stray_irqs <= 8)
536 		log(LOG_ERR, "Stray interrupt %08x%s\n", mask,
537 		    stray_irqs >= 8 ? ": stopped logging" : "");
538 }
539