ofw_irqhandler.c revision 1.12
1/* $NetBSD: ofw_irqhandler.c,v 1.12 2007/10/17 19:53:42 garbled 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 * from: irqhandler.c 38 * 39 * IRQ/FIQ initialisation, claim, release and handler routines 40 * 41 * Created : 30/09/94 42 */ 43 44#include <sys/cdefs.h> 45__KERNEL_RCSID(0, "$NetBSD: ofw_irqhandler.c,v 1.12 2007/10/17 19:53:42 garbled Exp $"); 46 47#include <sys/param.h> 48#include <sys/systm.h> 49#include <sys/syslog.h> 50#include <sys/malloc.h> 51 52#include <uvm/uvm_extern.h> 53 54#include <machine/intr.h> 55#include <machine/irqhandler.h> 56#include <machine/cpu.h> 57 58irqhandler_t *irqhandlers[NIRQS]; 59 60int current_intr_depth; 61u_int current_mask; 62u_int actual_mask; 63u_int disabled_mask; 64u_int irqmasks[IPL_LEVELS]; 65extern u_int intrcnt[]; 66 67extern char *_intrnames; 68 69/* Prototypes */ 70 71int podule_irqhandler __P((void)); 72extern void set_spl_masks __P((void)); 73 74/* 75 * void irq_init(void) 76 * 77 * Initialise the IRQ/FIQ sub system 78 */ 79 80void 81irq_init() 82{ 83 int loop; 84 85 /* Clear all the IRQ handlers and the irq block masks */ 86 for (loop = 0; loop < NIRQS; ++loop) { 87 irqhandlers[loop] = NULL; 88 } 89 90 /* 91 * Setup the irqmasks for the different Interrupt Priority Levels 92 * We will start with no bits set and these will be updated as handlers 93 * are installed at different IPL's. 94 */ 95 for (loop = 0; loop < IPL_LEVELS; ++loop) 96 irqmasks[loop] = 0; 97 98 current_intr_depth = 0; 99 current_mask = 0x00000000; 100 disabled_mask = 0x00000000; 101 actual_mask = 0x00000000; 102 103 set_spl_masks(); 104 105 /* Enable IRQ's and FIQ's */ 106 enable_interrupts(I32_bit | F32_bit); 107} 108 109 110/* 111 * int irq_claim(int irq, irqhandler_t *handler) 112 * 113 * Enable an IRQ and install a handler for it. 114 */ 115 116int 117irq_claim(irq, handler, group, name) 118 int irq; 119 irqhandler_t *handler; 120 const char *group; 121 const char *name; 122{ 123 int level; 124 125#ifdef DIAGNOSTIC 126 /* Sanity check */ 127 if (handler == NULL) 128 panic("NULL interrupt handler"); 129 if (handler->ih_func == NULL) 130 panic("Interrupt handler does not have a function"); 131#endif /* DIAGNOSTIC */ 132 133 /* 134 * IRQ_INSTRUCT indicates that we should get the irq number 135 * from the irq structure 136 */ 137 if (irq == IRQ_INSTRUCT) 138 irq = handler->ih_num; 139 140 /* Make sure the irq number is valid */ 141 if (irq < 0 || irq >= NIRQS) 142 return(-1); 143 144 /* Make sure the level is valid */ 145 if (handler->ih_level < 0 || handler->ih_level >= IPL_LEVELS) 146 return(-1); 147 148 evcnt_attach_dynamic(&handler->ih_ev, EVCNT_TYPE_INTR, NULL, 149 group, name); 150 151 /* Attach handler at top of chain */ 152 handler->ih_next = irqhandlers[irq]; 153 irqhandlers[irq] = handler; 154 155 /* 156 * Reset the flags for this handler. 157 * As the handler is now in the chain mark it as active. 158 */ 159 handler->ih_flags = 0 | IRQ_FLAG_ACTIVE; 160 161 /* 162 * Record the interrupt number for accounting. 163 * Done here as the accounting number may not be the same as the 164 * IRQ number though for the moment they are 165 */ 166 handler->ih_num = irq; 167 168 /* 169 * Update the irq masks. 170 * Find the lowest interrupt priority on the irq chain. 171 * Interrupt is allowable at priorities lower than this. 172 * If ih_level is out of range then don't bother to update 173 * the masks. 174 */ 175 if (handler->ih_level >= 0 && handler->ih_level < IPL_LEVELS) { 176 irqhandler_t *ptr; 177 178 /* 179 * Find the lowest interrupt priority on the irq chain. 180 * Interrupt is allowable at priorities lower than this. 181 */ 182 ptr = irqhandlers[irq]; 183 if (ptr) { 184 level = ptr->ih_level - 1; 185 while (ptr) { 186 if (ptr->ih_level - 1 < level) 187 level = ptr->ih_level - 1; 188 ptr = ptr->ih_next; 189 } 190 while (level >= 0) { 191 irqmasks[level] |= (1 << irq); 192 --level; 193 } 194 } 195 196#include "sl.h" 197#include "ppp.h" 198#if NSL > 0 || NPPP > 0 199 /* In the presence of SLIP or PPP, splimp > spltty. */ 200 irqmasks[IPL_NET] &= irqmasks[IPL_TTY]; 201#endif 202 } 203 204 enable_irq(irq); 205 set_spl_masks(); 206 207 return(0); 208} 209 210 211/* 212 * int irq_release(int irq, irqhandler_t *handler) 213 * 214 * Disable an IRQ and remove a handler for it. 215 */ 216 217int 218irq_release(irq, handler) 219 int irq; 220 irqhandler_t *handler; 221{ 222 int level; 223 irqhandler_t *irqhand; 224 irqhandler_t **prehand; 225 226 /* 227 * IRQ_INSTRUCT indicates that we should get the irq number 228 * from the irq structure 229 */ 230 if (irq == IRQ_INSTRUCT) 231 irq = handler->ih_num; 232 233 /* Make sure the irq number is valid */ 234 if (irq < 0 || irq >= NIRQS) 235 return(-1); 236 237 /* Locate the handler */ 238 irqhand = irqhandlers[irq]; 239 prehand = &irqhandlers[irq]; 240 241 while (irqhand && handler != irqhand) { 242 prehand = &irqhand; 243 irqhand = irqhand->ih_next; 244 } 245 246 /* Remove the handler if located */ 247 if (irqhand) 248 *prehand = irqhand->ih_next; 249 else 250 return(-1); 251 252 /* Now the handler has been removed from the chain mark is as inactive */ 253 irqhand->ih_flags &= ~IRQ_FLAG_ACTIVE; 254 255 /* Make sure the head of the handler list is active */ 256 if (irqhandlers[irq]) 257 irqhandlers[irq]->ih_flags |= IRQ_FLAG_ACTIVE; 258 259 /* 260 * Update the irq masks. 261 * If ih_level is out of range then don't bother to update 262 * the masks. 263 */ 264 if (handler->ih_level >= 0 && handler->ih_level < IPL_LEVELS) { 265 irqhandler_t *ptr; 266 267 /* Clean the bit from all the masks */ 268 for (level = 0; level < IPL_LEVELS; ++level) 269 irqmasks[level] &= ~(1 << irq); 270 271 /* 272 * Find the lowest interrupt priority on the irq chain. 273 * Interrupt is allowable at priorities lower than this. 274 */ 275 ptr = irqhandlers[irq]; 276 if (ptr) { 277 level = ptr->ih_level - 1; 278 while (ptr) { 279 if (ptr->ih_level - 1 < level) 280 level = ptr->ih_level - 1; 281 ptr = ptr->ih_next; 282 } 283 while (level >= 0) { 284 irqmasks[level] |= (1 << irq); 285 --level; 286 } 287 } 288 } 289 290 /* 291 * Disable the appropriate mask bit if there are no handlers left for 292 * this IRQ. 293 */ 294 if (irqhandlers[irq] == NULL) 295 disable_irq(irq); 296 297 set_spl_masks(); 298 299 return(0); 300} 301 302 303void * 304intr_claim(irq, level, ih_func, ih_arg, group, name) 305 int irq; 306 int level; 307 int (*ih_func) __P((void *)); 308 void *ih_arg; 309 const char *group; 310 const char *name; 311{ 312 irqhandler_t *ih; 313 314 ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT|M_ZERO); 315 if (!ih) 316 panic("intr_claim(): Cannot malloc handler memory"); 317 318 ih->ih_level = level; 319 ih->ih_func = ih_func; 320 ih->ih_arg = ih_arg; 321 ih->ih_flags = 0; 322 323 if (irq_claim(irq, ih, group, name) != 0) 324 return(NULL); 325 return(ih); 326} 327 328 329int 330intr_release(arg) 331 void *arg; 332{ 333 irqhandler_t *ih = (irqhandler_t *)arg; 334 335 if (irq_release(ih->ih_num, ih) == 0) { 336 free(ih, M_DEVBUF); 337 return(0); 338 } 339 return(1); 340} 341 342 343/* 344 * void disable_irq(int irq) 345 * 346 * Disables a specific irq. The irq is removed from the master irq mask 347 */ 348 349void 350disable_irq(irq) 351 int irq; 352{ 353 register int oldirqstate; 354 355 oldirqstate = disable_interrupts(I32_bit); 356 current_mask &= ~(1 << irq); 357 irq_setmasks(); 358 restore_interrupts(oldirqstate); 359} 360 361 362/* 363 * void enable_irq(int irq) 364 * 365 * Enables a specific irq. The irq is added to the master irq mask 366 * This routine should be used with caution. A handler should already 367 * be installed. 368 */ 369 370void 371enable_irq(irq) 372 int irq; 373{ 374 register u_int oldirqstate; 375 376 oldirqstate = disable_interrupts(I32_bit); 377 current_mask |= (1 << irq); 378 irq_setmasks(); 379 restore_interrupts(oldirqstate); 380} 381 382 383/* 384 * void stray_irqhandler(u_int mask) 385 * 386 * Handler for stray interrupts. This gets called if a handler cannot be 387 * found for an interrupt. 388 */ 389 390void stray_irqhandler(u_int); /* called from assembly */ 391 392void 393stray_irqhandler(mask) 394 u_int mask; 395{ 396 static u_int stray_irqs = 0; 397 398 if (++stray_irqs <= 8) 399 log(LOG_ERR, "Stray interrupt %08x%s\n", mask, 400 stray_irqs >= 8 ? ": stopped logging" : ""); 401} 402