1 /* $NetBSD: isr.c,v 1.33 2010/12/20 00:25:39 matt Exp $ */ 2 3 /*- 4 * Copyright (c) 1996 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Adam Glass, Gordon W. Ross, and Jason R. Thorpe. 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Link and dispatch interrupts. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: isr.c,v 1.33 2010/12/20 00:25:39 matt Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/systm.h> 41 #include <sys/malloc.h> 42 #include <sys/vmmeter.h> 43 #include <sys/device.h> 44 #include <sys/cpu.h> 45 46 #include <uvm/uvm_extern.h> 47 48 #include <mvme68k/mvme68k/isr.h> 49 50 volatile unsigned int interrupt_depth; 51 isr_autovec_list_t isr_autovec[NISRAUTOVEC]; 52 struct isr_vectored isr_vectored[NISRVECTORED]; 53 static const char irqgroupname[] = "hard irqs"; 54 struct evcnt mvme68k_irq_evcnt[] = { 55 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "spur"), 56 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev1"), 57 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev2"), 58 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev3"), 59 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev4"), 60 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev5"), 61 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "lev6"), 62 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, irqgroupname, "nmi") 63 }; 64 static int idepth; 65 66 extern int intrcnt[]; /* from locore.s. XXXSCW: will go away soon */ 67 extern void (*vectab[])(void); 68 extern void badtrap(void); 69 extern void intrhand_vectored(void); 70 71 static int spurintr(void *); 72 73 74 void 75 isrinit(void) 76 { 77 int i; 78 79 /* Initialize the autovector lists. */ 80 for (i = 0; i < NISRAUTOVEC; ++i) 81 LIST_INIT(&isr_autovec[i]); 82 83 /* Initialise the interrupt event counts */ 84 for (i = 0; i < (sizeof(mvme68k_irq_evcnt) / sizeof(struct evcnt)); i++) 85 evcnt_attach_static(&mvme68k_irq_evcnt[i]); 86 87 /* Arrange to trap Spurious and NMI auto-vectored Interrupts */ 88 isrlink_autovec(spurintr, NULL, 0, 0, NULL); 89 isrlink_autovec(nmihand, NULL, 7, 0, NULL); 90 } 91 92 /* 93 * Establish an autovectored interrupt handler. 94 * Called by driver attach functions. 95 */ 96 void 97 isrlink_autovec(int (*func)(void *), void *arg, int ipl, int priority, 98 struct evcnt *evcnt) 99 { 100 struct isr_autovec *newisr, *curisr; 101 isr_autovec_list_t *list; 102 103 #ifdef DIAGNOSTIC 104 if ((ipl < 0) || (ipl >= NISRAUTOVEC)) 105 panic("%s: bad ipl %d", __func__, ipl); 106 #endif 107 108 newisr = malloc(sizeof(struct isr_autovec), M_DEVBUF, M_NOWAIT); 109 if (newisr == NULL) 110 panic("%s: can't allocate space for isr", __func__); 111 112 /* Fill in the new entry. */ 113 newisr->isr_func = func; 114 newisr->isr_arg = arg; 115 newisr->isr_ipl = ipl; 116 newisr->isr_priority = priority; 117 newisr->isr_evcnt = evcnt; 118 119 /* 120 * Some devices are particularly sensitive to interrupt 121 * handling latency. The SCC, for example, can lose many 122 * characters if its interrupt isn't handled with reasonable 123 * speed. 124 * 125 * To work around this problem, each device can give itself a 126 * "priority". An unbuffered SCC would give itself a higher 127 * priority than a SCSI device, for example. 128 * 129 * This solution was originally developed for the hp300, which 130 * has a flat spl scheme (by necessity). Thankfully, the 131 * MVME systems don't have this problem, though this may serve 132 * a useful purpose in any case. 133 */ 134 135 /* 136 * Get the appropriate ISR list. If the list is empty, no 137 * additional work is necessary; we simply insert ourselves 138 * at the head of the list. 139 */ 140 list = &isr_autovec[ipl]; 141 if (list->lh_first == NULL) { 142 LIST_INSERT_HEAD(list, newisr, isr_link); 143 return; 144 } 145 146 /* 147 * A little extra work is required. We traverse the list 148 * and place ourselves after any ISRs with our current (or 149 * higher) priority. 150 */ 151 for (curisr = list->lh_first; curisr->isr_link.le_next != NULL; 152 curisr = curisr->isr_link.le_next) { 153 if (newisr->isr_priority > curisr->isr_priority) { 154 LIST_INSERT_BEFORE(curisr, newisr, isr_link); 155 return; 156 } 157 } 158 159 /* 160 * We're the least important entry, it seems. We just go 161 * on the end. 162 */ 163 LIST_INSERT_AFTER(curisr, newisr, isr_link); 164 } 165 166 /* 167 * Establish a vectored interrupt handler. 168 * Called by bus interrupt establish functions. 169 */ 170 void 171 isrlink_vectored(int (*func)(void *), void *arg, int ipl, int vec, 172 struct evcnt *evcnt) 173 { 174 struct isr_vectored *isr; 175 176 #ifdef DIAGNOSTIC 177 if ((ipl < 0) || (ipl >= NISRAUTOVEC)) 178 panic("%s: bad ipl %d", __func__, ipl); 179 if ((vec < ISRVECTORED) || (vec >= ISRVECTORED + NISRVECTORED)) 180 panic("%s: bad vec 0x%x", __func__, vec); 181 #endif 182 183 isr = &isr_vectored[vec - ISRVECTORED]; 184 185 #ifdef DIAGNOSTIC 186 if ((vectab[vec] != badtrap) || (isr->isr_func != NULL)) 187 panic("%s: vec 0x%x not available", __func__, vec); 188 #endif 189 190 /* Fill in the new entry. */ 191 isr->isr_func = func; 192 isr->isr_arg = arg; 193 isr->isr_ipl = ipl; 194 isr->isr_evcnt = evcnt; 195 196 /* Hook into the vector table. */ 197 vectab[vec] = intrhand_vectored; 198 } 199 200 /* 201 * Return a pointer to the evcnt structure for 202 * the specified ipl. 203 */ 204 struct evcnt * 205 isrlink_evcnt(int ipl) 206 { 207 208 #ifdef DIAGNOSTIC 209 if (ipl < 0 || 210 ipl >= (sizeof(mvme68k_irq_evcnt) / sizeof(struct evcnt))) 211 panic("%s: bad ipl %d", __func__, ipl); 212 #endif 213 214 return &mvme68k_irq_evcnt[ipl]; 215 } 216 217 /* 218 * Unhook a vectored interrupt. 219 */ 220 void 221 isrunlink_vectored(int vec) 222 { 223 224 #ifdef DIAGNOSTIC 225 if ((vec < ISRVECTORED) || (vec >= ISRVECTORED + NISRVECTORED)) 226 panic("%s: bad vec 0x%x", __func__, vec); 227 228 if (vectab[vec] != intrhand_vectored) 229 panic("%s: not vectored interrupt", __func__); 230 #endif 231 232 vectab[vec] = badtrap; 233 memset(&isr_vectored[vec - ISRVECTORED], 0, 234 sizeof(struct isr_vectored)); 235 } 236 237 /* 238 * This is the dispatcher called by the low-level 239 * assembly language autovectored interrupt routine. 240 */ 241 void 242 isrdispatch_autovec(struct clockframe *frame) 243 { 244 struct isr_autovec *isr; 245 isr_autovec_list_t *list; 246 int handled, ipl; 247 void *arg; 248 static int straycount, unexpected; 249 250 idepth++; 251 ipl = (frame->vec >> 2) - ISRAUTOVEC; 252 253 #ifdef DIAGNOSTIC 254 if ((ipl < 0) || (ipl >= NISRAUTOVEC)) 255 panic("%s: bad vec 0x%x", __func__, frame->vec); 256 #endif 257 258 intrcnt[ipl]++; /* XXXSCW: Will go away soon */ 259 mvme68k_irq_evcnt[ipl].ev_count++; 260 curcpu()->ci_data.cpu_nintr++; 261 262 list = &isr_autovec[ipl]; 263 if (list->lh_first == NULL) { 264 printf("%s: ipl %d unexpected\n", __func__, ipl); 265 if (++unexpected > 10) 266 panic("too many unexpected interrupts"); 267 idepth--; 268 return; 269 } 270 271 /* Give all the handlers a chance. */ 272 handled = 0; 273 for (isr = list->lh_first ; isr != NULL; isr = isr->isr_link.le_next) { 274 arg = isr->isr_arg ? isr->isr_arg : frame; 275 if ((*isr->isr_func)(arg) != 0) { 276 if (isr->isr_evcnt) 277 isr->isr_evcnt->ev_count++; 278 handled++; 279 } 280 } 281 282 if (handled) 283 straycount = 0; 284 else if (++straycount > 50) 285 panic("%s: too many stray interrupts", __func__); 286 else 287 printf("%s: stray level %d interrupt\n", __func__, ipl); 288 289 idepth--; 290 } 291 292 /* 293 * This is the dispatcher called by the low-level 294 * assembly language vectored interrupt routine. 295 */ 296 void 297 isrdispatch_vectored(int ipl, struct clockframe *frame) 298 { 299 struct isr_vectored *isr; 300 int vec; 301 302 idepth++; 303 vec = (frame->vec >> 2) - ISRVECTORED; 304 305 #ifdef DIAGNOSTIC 306 if ((vec < 0) || (vec >= NISRVECTORED)) 307 panic("%s: bad vec 0x%x", __func__, frame->vec); 308 #endif 309 310 isr = &isr_vectored[vec]; 311 312 intrcnt[ipl]++; /* XXXSCW: Will go away soon */ 313 mvme68k_irq_evcnt[ipl].ev_count++; 314 curcpu()->ci_data.cpu_nintr++; 315 316 if (isr->isr_func == NULL) { 317 printf("%s: no handler for vec 0x%x\n", __func__, frame->vec); 318 vectab[vec + ISRVECTORED] = badtrap; 319 idepth--; 320 return; 321 } 322 323 /* 324 * Handler gets exception frame if argument is NULL. 325 */ 326 if ((*isr->isr_func)(isr->isr_arg ? isr->isr_arg : frame) == 0) 327 printf("%s: vec 0x%x not claimed\n", __func__, frame->vec); 328 else if (isr->isr_evcnt) 329 isr->isr_evcnt->ev_count++; 330 idepth--; 331 } 332 333 bool 334 cpu_intr_p(void) 335 { 336 337 return idepth != 0; 338 } 339 340 /* ARGSUSED */ 341 static int 342 spurintr(void *arg) 343 { 344 345 return 1; 346 } 347