1 1.23 thorpej /* $NetBSD: ofw_irqhandler.c,v 1.23 2020/11/20 18:26:26 thorpej Exp $ */ 2 1.1 thorpej 3 1.1 thorpej /* 4 1.1 thorpej * Copyright (c) 1994-1998 Mark Brinicombe. 5 1.1 thorpej * Copyright (c) 1994 Brini. 6 1.1 thorpej * All rights reserved. 7 1.1 thorpej * 8 1.1 thorpej * This code is derived from software written for Brini by Mark Brinicombe 9 1.1 thorpej * 10 1.1 thorpej * Redistribution and use in source and binary forms, with or without 11 1.1 thorpej * modification, are permitted provided that the following conditions 12 1.1 thorpej * are met: 13 1.1 thorpej * 1. Redistributions of source code must retain the above copyright 14 1.1 thorpej * notice, this list of conditions and the following disclaimer. 15 1.1 thorpej * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 thorpej * notice, this list of conditions and the following disclaimer in the 17 1.1 thorpej * documentation and/or other materials provided with the distribution. 18 1.1 thorpej * 3. All advertising materials mentioning features or use of this software 19 1.1 thorpej * must display the following acknowledgement: 20 1.1 thorpej * This product includes software developed by Mark Brinicombe 21 1.1 thorpej * for the NetBSD Project. 22 1.1 thorpej * 4. The name of the company nor the name of the author may be used to 23 1.1 thorpej * endorse or promote products derived from this software without specific 24 1.1 thorpej * prior written permission. 25 1.1 thorpej * 26 1.1 thorpej * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 27 1.1 thorpej * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 28 1.1 thorpej * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 29 1.1 thorpej * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 30 1.1 thorpej * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 31 1.1 thorpej * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 1.1 thorpej * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 1.1 thorpej * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 1.1 thorpej * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 35 1.1 thorpej * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 1.1 thorpej * 37 1.1 thorpej * from: irqhandler.c 38 1.1 thorpej * 39 1.1 thorpej * IRQ/FIQ initialisation, claim, release and handler routines 40 1.1 thorpej * 41 1.1 thorpej * Created : 30/09/94 42 1.1 thorpej */ 43 1.3 lukem 44 1.3 lukem #include <sys/cdefs.h> 45 1.23 thorpej __KERNEL_RCSID(0, "$NetBSD: ofw_irqhandler.c,v 1.23 2020/11/20 18:26:26 thorpej Exp $"); 46 1.1 thorpej 47 1.1 thorpej #include <sys/param.h> 48 1.1 thorpej #include <sys/systm.h> 49 1.1 thorpej #include <sys/syslog.h> 50 1.23 thorpej #include <sys/kmem.h> 51 1.1 thorpej 52 1.19 chs #include <sys/intr.h> 53 1.10 matt #include <machine/irqhandler.h> 54 1.1 thorpej #include <machine/cpu.h> 55 1.1 thorpej 56 1.1 thorpej irqhandler_t *irqhandlers[NIRQS]; 57 1.1 thorpej 58 1.1 thorpej u_int current_mask; 59 1.1 thorpej u_int actual_mask; 60 1.1 thorpej u_int disabled_mask; 61 1.19 chs u_int irqmasks[NIPL]; 62 1.1 thorpej 63 1.1 thorpej /* Prototypes */ 64 1.1 thorpej 65 1.14 dsl int podule_irqhandler(void); 66 1.14 dsl extern void set_spl_masks(void); 67 1.1 thorpej 68 1.1 thorpej /* 69 1.1 thorpej * void irq_init(void) 70 1.1 thorpej * 71 1.1 thorpej * Initialise the IRQ/FIQ sub system 72 1.1 thorpej */ 73 1.1 thorpej 74 1.1 thorpej void 75 1.17 cegger irq_init(void) 76 1.1 thorpej { 77 1.1 thorpej int loop; 78 1.1 thorpej 79 1.1 thorpej /* Clear all the IRQ handlers and the irq block masks */ 80 1.1 thorpej for (loop = 0; loop < NIRQS; ++loop) { 81 1.1 thorpej irqhandlers[loop] = NULL; 82 1.1 thorpej } 83 1.1 thorpej 84 1.1 thorpej /* 85 1.1 thorpej * Setup the irqmasks for the different Interrupt Priority Levels 86 1.1 thorpej * We will start with no bits set and these will be updated as handlers 87 1.1 thorpej * are installed at different IPL's. 88 1.1 thorpej */ 89 1.19 chs for (loop = 0; loop < NIPL; ++loop) 90 1.1 thorpej irqmasks[loop] = 0; 91 1.1 thorpej 92 1.1 thorpej current_mask = 0x00000000; 93 1.1 thorpej disabled_mask = 0x00000000; 94 1.1 thorpej actual_mask = 0x00000000; 95 1.1 thorpej 96 1.1 thorpej set_spl_masks(); 97 1.1 thorpej 98 1.1 thorpej /* Enable IRQ's and FIQ's */ 99 1.1 thorpej enable_interrupts(I32_bit | F32_bit); 100 1.1 thorpej } 101 1.1 thorpej 102 1.1 thorpej 103 1.1 thorpej /* 104 1.1 thorpej * int irq_claim(int irq, irqhandler_t *handler) 105 1.1 thorpej * 106 1.1 thorpej * Enable an IRQ and install a handler for it. 107 1.1 thorpej */ 108 1.1 thorpej 109 1.1 thorpej int 110 1.15 dsl irq_claim(int irq, irqhandler_t *handler, const char *group, const char *name) 111 1.1 thorpej { 112 1.1 thorpej int level; 113 1.1 thorpej 114 1.1 thorpej #ifdef DIAGNOSTIC 115 1.1 thorpej /* Sanity check */ 116 1.1 thorpej if (handler == NULL) 117 1.2 provos panic("NULL interrupt handler"); 118 1.1 thorpej if (handler->ih_func == NULL) 119 1.2 provos panic("Interrupt handler does not have a function"); 120 1.1 thorpej #endif /* DIAGNOSTIC */ 121 1.1 thorpej 122 1.1 thorpej /* 123 1.1 thorpej * IRQ_INSTRUCT indicates that we should get the irq number 124 1.1 thorpej * from the irq structure 125 1.1 thorpej */ 126 1.1 thorpej if (irq == IRQ_INSTRUCT) 127 1.1 thorpej irq = handler->ih_num; 128 1.1 thorpej 129 1.1 thorpej /* Make sure the irq number is valid */ 130 1.1 thorpej if (irq < 0 || irq >= NIRQS) 131 1.1 thorpej return(-1); 132 1.1 thorpej 133 1.1 thorpej /* Make sure the level is valid */ 134 1.19 chs if (handler->ih_level < 0 || handler->ih_level >= NIPL) 135 1.1 thorpej return(-1); 136 1.1 thorpej 137 1.10 matt evcnt_attach_dynamic(&handler->ih_ev, EVCNT_TYPE_INTR, NULL, 138 1.10 matt group, name); 139 1.10 matt 140 1.1 thorpej /* Attach handler at top of chain */ 141 1.1 thorpej handler->ih_next = irqhandlers[irq]; 142 1.1 thorpej irqhandlers[irq] = handler; 143 1.1 thorpej 144 1.1 thorpej /* 145 1.1 thorpej * Reset the flags for this handler. 146 1.1 thorpej * As the handler is now in the chain mark it as active. 147 1.1 thorpej */ 148 1.1 thorpej handler->ih_flags = 0 | IRQ_FLAG_ACTIVE; 149 1.1 thorpej 150 1.1 thorpej /* 151 1.1 thorpej * Record the interrupt number for accounting. 152 1.1 thorpej * Done here as the accounting number may not be the same as the 153 1.1 thorpej * IRQ number though for the moment they are 154 1.1 thorpej */ 155 1.1 thorpej handler->ih_num = irq; 156 1.1 thorpej 157 1.1 thorpej /* 158 1.1 thorpej * Update the irq masks. 159 1.1 thorpej * Find the lowest interrupt priority on the irq chain. 160 1.1 thorpej * Interrupt is allowable at priorities lower than this. 161 1.1 thorpej * If ih_level is out of range then don't bother to update 162 1.1 thorpej * the masks. 163 1.1 thorpej */ 164 1.19 chs if (handler->ih_level >= 0 && handler->ih_level < NIPL) { 165 1.1 thorpej irqhandler_t *ptr; 166 1.1 thorpej 167 1.1 thorpej /* 168 1.1 thorpej * Find the lowest interrupt priority on the irq chain. 169 1.1 thorpej * Interrupt is allowable at priorities lower than this. 170 1.1 thorpej */ 171 1.1 thorpej ptr = irqhandlers[irq]; 172 1.1 thorpej if (ptr) { 173 1.1 thorpej level = ptr->ih_level - 1; 174 1.1 thorpej while (ptr) { 175 1.1 thorpej if (ptr->ih_level - 1 < level) 176 1.1 thorpej level = ptr->ih_level - 1; 177 1.1 thorpej ptr = ptr->ih_next; 178 1.1 thorpej } 179 1.1 thorpej while (level >= 0) { 180 1.1 thorpej irqmasks[level] |= (1 << irq); 181 1.1 thorpej --level; 182 1.1 thorpej } 183 1.1 thorpej } 184 1.1 thorpej 185 1.1 thorpej #include "sl.h" 186 1.1 thorpej #include "ppp.h" 187 1.1 thorpej #if NSL > 0 || NPPP > 0 188 1.1 thorpej /* In the presence of SLIP or PPP, splimp > spltty. */ 189 1.1 thorpej irqmasks[IPL_NET] &= irqmasks[IPL_TTY]; 190 1.1 thorpej #endif 191 1.1 thorpej } 192 1.1 thorpej 193 1.1 thorpej enable_irq(irq); 194 1.1 thorpej set_spl_masks(); 195 1.1 thorpej 196 1.1 thorpej return(0); 197 1.1 thorpej } 198 1.1 thorpej 199 1.1 thorpej 200 1.1 thorpej /* 201 1.1 thorpej * int irq_release(int irq, irqhandler_t *handler) 202 1.1 thorpej * 203 1.1 thorpej * Disable an IRQ and remove a handler for it. 204 1.1 thorpej */ 205 1.1 thorpej 206 1.1 thorpej int 207 1.15 dsl irq_release(int irq, irqhandler_t *handler) 208 1.1 thorpej { 209 1.1 thorpej int level; 210 1.1 thorpej irqhandler_t *irqhand; 211 1.1 thorpej irqhandler_t **prehand; 212 1.1 thorpej 213 1.1 thorpej /* 214 1.1 thorpej * IRQ_INSTRUCT indicates that we should get the irq number 215 1.1 thorpej * from the irq structure 216 1.1 thorpej */ 217 1.1 thorpej if (irq == IRQ_INSTRUCT) 218 1.1 thorpej irq = handler->ih_num; 219 1.1 thorpej 220 1.1 thorpej /* Make sure the irq number is valid */ 221 1.1 thorpej if (irq < 0 || irq >= NIRQS) 222 1.1 thorpej return(-1); 223 1.1 thorpej 224 1.1 thorpej /* Locate the handler */ 225 1.1 thorpej irqhand = irqhandlers[irq]; 226 1.1 thorpej prehand = &irqhandlers[irq]; 227 1.1 thorpej 228 1.1 thorpej while (irqhand && handler != irqhand) { 229 1.1 thorpej prehand = &irqhand; 230 1.1 thorpej irqhand = irqhand->ih_next; 231 1.1 thorpej } 232 1.1 thorpej 233 1.1 thorpej /* Remove the handler if located */ 234 1.1 thorpej if (irqhand) 235 1.1 thorpej *prehand = irqhand->ih_next; 236 1.1 thorpej else 237 1.1 thorpej return(-1); 238 1.1 thorpej 239 1.1 thorpej /* Now the handler has been removed from the chain mark is as inactive */ 240 1.1 thorpej irqhand->ih_flags &= ~IRQ_FLAG_ACTIVE; 241 1.1 thorpej 242 1.1 thorpej /* Make sure the head of the handler list is active */ 243 1.1 thorpej if (irqhandlers[irq]) 244 1.1 thorpej irqhandlers[irq]->ih_flags |= IRQ_FLAG_ACTIVE; 245 1.1 thorpej 246 1.1 thorpej /* 247 1.1 thorpej * Update the irq masks. 248 1.1 thorpej * If ih_level is out of range then don't bother to update 249 1.1 thorpej * the masks. 250 1.1 thorpej */ 251 1.19 chs if (handler->ih_level >= 0 && handler->ih_level < NIPL) { 252 1.1 thorpej irqhandler_t *ptr; 253 1.1 thorpej 254 1.1 thorpej /* Clean the bit from all the masks */ 255 1.19 chs for (level = 0; level < NIPL; ++level) 256 1.1 thorpej irqmasks[level] &= ~(1 << irq); 257 1.1 thorpej 258 1.1 thorpej /* 259 1.1 thorpej * Find the lowest interrupt priority on the irq chain. 260 1.1 thorpej * Interrupt is allowable at priorities lower than this. 261 1.1 thorpej */ 262 1.1 thorpej ptr = irqhandlers[irq]; 263 1.1 thorpej if (ptr) { 264 1.1 thorpej level = ptr->ih_level - 1; 265 1.1 thorpej while (ptr) { 266 1.1 thorpej if (ptr->ih_level - 1 < level) 267 1.1 thorpej level = ptr->ih_level - 1; 268 1.1 thorpej ptr = ptr->ih_next; 269 1.1 thorpej } 270 1.1 thorpej while (level >= 0) { 271 1.1 thorpej irqmasks[level] |= (1 << irq); 272 1.1 thorpej --level; 273 1.1 thorpej } 274 1.1 thorpej } 275 1.1 thorpej } 276 1.1 thorpej 277 1.1 thorpej /* 278 1.1 thorpej * Disable the appropriate mask bit if there are no handlers left for 279 1.1 thorpej * this IRQ. 280 1.1 thorpej */ 281 1.1 thorpej if (irqhandlers[irq] == NULL) 282 1.1 thorpej disable_irq(irq); 283 1.1 thorpej 284 1.1 thorpej set_spl_masks(); 285 1.1 thorpej 286 1.1 thorpej return(0); 287 1.1 thorpej } 288 1.1 thorpej 289 1.1 thorpej 290 1.1 thorpej void * 291 1.16 dsl intr_claim(int irq, int level, int (*ih_func)(void *), void *ih_arg, const char *group, const char *name) 292 1.1 thorpej { 293 1.1 thorpej irqhandler_t *ih; 294 1.1 thorpej 295 1.23 thorpej ih = kmem_zalloc(sizeof(*ih), KM_SLEEP); 296 1.1 thorpej ih->ih_level = level; 297 1.1 thorpej ih->ih_func = ih_func; 298 1.1 thorpej ih->ih_arg = ih_arg; 299 1.1 thorpej ih->ih_flags = 0; 300 1.1 thorpej 301 1.21 christos if (irq_claim(irq, ih, group, name) != 0) { 302 1.23 thorpej kmem_free(ih, sizeof(*ih)); 303 1.1 thorpej return(NULL); 304 1.21 christos } 305 1.1 thorpej return(ih); 306 1.1 thorpej } 307 1.1 thorpej 308 1.1 thorpej 309 1.1 thorpej int 310 1.15 dsl intr_release(void *arg) 311 1.1 thorpej { 312 1.1 thorpej irqhandler_t *ih = (irqhandler_t *)arg; 313 1.1 thorpej 314 1.1 thorpej if (irq_release(ih->ih_num, ih) == 0) { 315 1.23 thorpej kmem_free(ih, sizeof(*ih)); 316 1.1 thorpej return(0); 317 1.1 thorpej } 318 1.1 thorpej return(1); 319 1.1 thorpej } 320 1.1 thorpej 321 1.1 thorpej 322 1.1 thorpej /* 323 1.1 thorpej * void disable_irq(int irq) 324 1.1 thorpej * 325 1.1 thorpej * Disables a specific irq. The irq is removed from the master irq mask 326 1.1 thorpej */ 327 1.1 thorpej 328 1.1 thorpej void 329 1.15 dsl disable_irq(int irq) 330 1.1 thorpej { 331 1.1 thorpej register int oldirqstate; 332 1.1 thorpej 333 1.1 thorpej oldirqstate = disable_interrupts(I32_bit); 334 1.1 thorpej current_mask &= ~(1 << irq); 335 1.1 thorpej irq_setmasks(); 336 1.1 thorpej restore_interrupts(oldirqstate); 337 1.1 thorpej } 338 1.1 thorpej 339 1.1 thorpej 340 1.1 thorpej /* 341 1.1 thorpej * void enable_irq(int irq) 342 1.1 thorpej * 343 1.1 thorpej * Enables a specific irq. The irq is added to the master irq mask 344 1.1 thorpej * This routine should be used with caution. A handler should already 345 1.1 thorpej * be installed. 346 1.1 thorpej */ 347 1.1 thorpej 348 1.1 thorpej void 349 1.15 dsl enable_irq(int irq) 350 1.1 thorpej { 351 1.1 thorpej register u_int oldirqstate; 352 1.1 thorpej 353 1.1 thorpej oldirqstate = disable_interrupts(I32_bit); 354 1.1 thorpej current_mask |= (1 << irq); 355 1.1 thorpej irq_setmasks(); 356 1.1 thorpej restore_interrupts(oldirqstate); 357 1.1 thorpej } 358 1.1 thorpej 359 1.1 thorpej 360 1.1 thorpej /* 361 1.1 thorpej * void stray_irqhandler(u_int mask) 362 1.1 thorpej * 363 1.1 thorpej * Handler for stray interrupts. This gets called if a handler cannot be 364 1.1 thorpej * found for an interrupt. 365 1.1 thorpej */ 366 1.1 thorpej 367 1.4 thorpej void stray_irqhandler(u_int); /* called from assembly */ 368 1.4 thorpej 369 1.1 thorpej void 370 1.15 dsl stray_irqhandler(u_int mask) 371 1.1 thorpej { 372 1.1 thorpej static u_int stray_irqs = 0; 373 1.1 thorpej 374 1.1 thorpej if (++stray_irqs <= 8) 375 1.1 thorpej log(LOG_ERR, "Stray interrupt %08x%s\n", mask, 376 1.1 thorpej stray_irqs >= 8 ? ": stopped logging" : ""); 377 1.1 thorpej } 378