footbridge_irqhandler.c revision 1.3 1 /* $NetBSD: footbridge_irqhandler.c,v 1.3 2002/01/05 22:41:48 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
51 irqhandler_t *irqhandlers[NIRQS];
52
53 int current_intr_depth; /* Depth of interrupt nesting */
54 u_int intr_claimed_mask; /* Interrupts that are claimed */
55 u_int intr_disabled_mask; /* Interrupts that are temporarily disabled */
56 u_int intr_current_mask; /* Interrupts currently allowable */
57 u_int spl_mask;
58 u_int irqmasks[IPL_LEVELS];
59 u_int irqblock[NIRQS];
60
61 extern u_int soft_interrupts; /* Only so we can initialise it */
62
63 extern char *_intrnames;
64 extern void set_spl_masks __P((void));
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