1 /* $NetBSD: intr.c,v 1.23 2010/12/20 00:25:30 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, Jason R. Thorpe, and Leo Weppelman. 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 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.23 2010/12/20 00:25:30 matt Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/kernel.h> 38 #include <sys/malloc.h> 39 #include <sys/vmmeter.h> 40 #include <sys/queue.h> 41 #include <sys/device.h> 42 #include <sys/cpu.h> 43 44 #include <machine/intr.h> 45 46 #define AVEC_MIN 1 47 #define AVEC_MAX 7 48 #define AVEC_LOC 25 49 #define UVEC_MIN 0 50 #define UVEC_MAX 191 51 #define UVEC_LOC 64 52 53 typedef LIST_HEAD(, intrhand) ih_list_t; 54 ih_list_t autovec_list[AVEC_MAX - AVEC_MIN + 1]; 55 ih_list_t uservec_list[UVEC_MAX - UVEC_MIN + 1]; 56 int idepth; 57 volatile int ssir; 58 59 void 60 intr_init(void) 61 { 62 int i; 63 64 for (i = 0; i < (AVEC_MAX - AVEC_MIN + 1); ++i) { 65 LIST_INIT(&autovec_list[i]); 66 } 67 for (i = 0; i < (UVEC_MAX - UVEC_MIN + 1); ++i) { 68 LIST_INIT(&uservec_list[i]); 69 } 70 } 71 72 /* 73 * Establish an interrupt vector. 74 * - vector 75 * The vector numer the interrupt should be hooked on. It can either 76 * be an auto-vector or a user-vector. 77 * - type 78 * A bit-wise of: 79 * - AUTO_VEC (mutually exclusive with USER_VEC) 80 * Attach to one of the 7 auto vectors 81 * - USER_VEC (mutually exclusive with AUTO_VEC) 82 * Attach to one of the 192 user vectors 83 * - FAST_VEC 84 * The interrupt function 'ih_fun' will be 85 * put into the 'real' interrupt table. This 86 * means: 87 * - This vector can't be shared 88 * - 'ih_fun' must save registers 89 * - 'ih_fun' must do it's own interrupt accounting 90 * - The argument to 'ih_fun' is a standard 91 * interrupt frame. 92 * - ARG_CLOCKRAME 93 * The 'ih_fun' function will be called with 94 * a standard clock-frame instead of 'ih_arg'. 95 * 96 * - pri 97 * When multiple interrupts are established on the same vector, 98 * interrupts with the highest priority will be called first. The 99 * basic ordering is the order of establishment. 100 * - ih_fun 101 * The interrupt function to be called 102 * - ih_arg 103 * The argument given to 'ih_fun' when ARG_CLOCKFRAME is not 104 * specified. 105 */ 106 107 struct intrhand * 108 intr_establish(int vector, int type, int pri, hw_ifun_t ih_fun, void *ih_arg) 109 { 110 struct intrhand *ih, *cur_vec; 111 ih_list_t *vec_list; 112 u_long *hard_vec; 113 int s; 114 115 /* no point in sleeping unless someone can free memory. */ 116 ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); 117 if (ih == NULL) 118 panic("intr_establish: can't malloc handler info"); 119 120 /* 121 * Initialize vector info 122 */ 123 ih->ih_fun = ih_fun; 124 ih->ih_arg = ih_arg; 125 ih->ih_type = type; 126 ih->ih_pri = pri; 127 ih->ih_vector = vector; 128 129 /* 130 * Do some validity checking on the 'vector' argument and determine 131 * vector list this interrupt should be on. 132 */ 133 switch (type & (AUTO_VEC|USER_VEC)) { 134 case AUTO_VEC: 135 if (vector < AVEC_MIN || vector > AVEC_MAX) 136 return NULL; 137 vec_list = &autovec_list[vector-1]; 138 hard_vec = &autovects[vector-1]; 139 ih->ih_intrcnt = &intrcnt_auto[vector-1]; 140 break; 141 case USER_VEC: 142 if (vector < UVEC_MIN || vector > UVEC_MAX) 143 return NULL; 144 vec_list = &uservec_list[vector]; 145 hard_vec = &uservects[vector]; 146 ih->ih_intrcnt = &intrcnt_user[vector]; 147 break; 148 default: 149 printf("intr_establish: bogus vector type\n"); 150 free(ih, M_DEVBUF); 151 return NULL; 152 } 153 154 /* 155 * If the vec_list is empty, we insert ourselves at the head of the 156 * list and we re-route the 'hard-vector' to the appropriate handler. 157 */ 158 if (vec_list->lh_first == NULL) { 159 160 s = splhigh(); 161 LIST_INSERT_HEAD(vec_list, ih, ih_link); 162 if (type & FAST_VEC) 163 *hard_vec = (u_long)ih->ih_fun; 164 else if (*hard_vec != (u_long)intr_glue) { 165 /* 166 * Normally, all settable vectors are already 167 * re-routed to the intr_glue() function. The 168 * marvelous exception to these are the HBL/VBL 169 * interrupts. They happen *very* often and 170 * can't be turned off on the Falcon. So they 171 * are normally vectored to an 'rte' instruction. 172 */ 173 *hard_vec = (u_long)intr_glue; 174 } 175 176 splx(s); 177 178 return ih; 179 } 180 181 /* 182 * Check for FAST_VEC botches 183 */ 184 cur_vec = vec_list->lh_first; 185 if (cur_vec->ih_type & FAST_VEC) { 186 free(ih, M_DEVBUF); 187 printf("intr_establish: vector cannot be shared\n"); 188 return NULL; 189 } 190 191 /* 192 * We traverse the list and place ourselves after any handlers with 193 * our current (or higher) priority level. 194 */ 195 for (cur_vec = vec_list->lh_first; cur_vec->ih_link.le_next != NULL; 196 cur_vec = cur_vec->ih_link.le_next) { 197 if (ih->ih_pri > cur_vec->ih_pri) { 198 199 s = splhigh(); 200 LIST_INSERT_BEFORE(cur_vec, ih, ih_link); 201 splx(s); 202 203 return ih; 204 } 205 } 206 207 /* 208 * We're the least important entry, it seems. We just go 209 * on the end. 210 */ 211 s = splhigh(); 212 LIST_INSERT_AFTER(cur_vec, ih, ih_link); 213 splx(s); 214 215 return ih; 216 } 217 218 int 219 intr_disestablish(struct intrhand *ih) 220 { 221 ih_list_t *vec_list; 222 u_long *hard_vec; 223 int vector, s; 224 struct intrhand *cur_vec; 225 226 vector = ih->ih_vector; 227 switch (ih->ih_type & (AUTO_VEC|USER_VEC)) { 228 case AUTO_VEC: 229 if (vector < AVEC_MIN || vector > AVEC_MAX) 230 return 0; 231 vec_list = &autovec_list[vector-1]; 232 hard_vec = &autovects[vector-1]; 233 break; 234 case USER_VEC: 235 if (vector < UVEC_MIN || vector > UVEC_MAX) 236 return 0; 237 vec_list = &uservec_list[vector]; 238 hard_vec = &uservects[vector]; 239 break; 240 default: 241 printf("intr_disestablish: bogus vector type\n"); 242 return 0; 243 } 244 245 /* 246 * Check if the vector is really in the list we think it's in.... 247 */ 248 for (cur_vec = vec_list->lh_first; cur_vec->ih_link.le_next != NULL; 249 cur_vec = cur_vec->ih_link.le_next) { 250 if (ih == cur_vec) 251 break; 252 } 253 if (ih != cur_vec) { 254 printf("intr_disestablish: 'ih' has inconsistent data\n"); 255 return 0; 256 } 257 258 s = splhigh(); 259 LIST_REMOVE(ih, ih_link); 260 if ((vec_list->lh_first == NULL) && (ih->ih_type & FAST_VEC)) 261 *hard_vec = (u_long)intr_glue; 262 splx(s); 263 264 free(ih, M_DEVBUF); 265 return 1; 266 } 267 268 /* 269 * This is the dispatcher called by the low-level 270 * assembly language interrupt-glue routine. 271 */ 272 void 273 intr_dispatch(struct clockframe frame) 274 { 275 static int unexpected, straycount; 276 int vector; 277 int handled = 0; 278 ih_list_t *vec_list; 279 struct intrhand *ih; 280 281 curcpu()->ci_data.cpu_nintr++; 282 vector = (frame.cf_vo & 0xfff) >> 2; 283 if (vector < (AVEC_LOC+AVEC_MAX) && vector >= AVEC_LOC) 284 vec_list = &autovec_list[vector - AVEC_LOC]; 285 else if (vector <= (UVEC_LOC+UVEC_MAX) && vector >= UVEC_LOC) 286 vec_list = &uservec_list[vector - UVEC_LOC]; 287 else 288 panic("intr_dispatch: Bogus vector %d", vector); 289 290 if ((ih = vec_list->lh_first) == NULL) { 291 printf("intr_dispatch: vector %d unexpected\n", vector); 292 if (++unexpected > 10) 293 panic("intr_dispatch: too many unexpected interrupts"); 294 return; 295 } 296 ih->ih_intrcnt[0]++; 297 298 /* Give all the handlers a chance. */ 299 for (; ih != NULL; ih = ih->ih_link.le_next) 300 handled |= (*ih->ih_fun)((ih->ih_type & ARG_CLOCKFRAME) ? 301 &frame : ih->ih_arg, frame.cf_sr); 302 303 if (handled) 304 straycount = 0; 305 else if (++straycount > 50) 306 panic("intr_dispatch: too many stray interrupts"); 307 else 308 printf("intr_dispatch: stray level %d interrupt\n", vector); 309 } 310 311 bool 312 cpu_intr_p(void) 313 { 314 315 return idepth != 0; 316 } 317 318 const uint16_t ipl2psl_table[NIPL] = { 319 [IPL_NONE] = PSL_S | PSL_IPL0, 320 [IPL_SOFTCLOCK] = PSL_S | PSL_IPL1, 321 [IPL_SOFTBIO] = PSL_S | PSL_IPL1, 322 [IPL_SOFTNET] = PSL_S | PSL_IPL1, 323 [IPL_SOFTSERIAL] = PSL_S | PSL_IPL1, 324 [IPL_VM] = PSL_S | PSL_IPL4, 325 [IPL_SCHED] = PSL_S | PSL_IPL6, 326 [IPL_HIGH] = PSL_S | PSL_IPL7, 327 }; 328