1 /* $NetBSD: intr.c,v 1.22 2002/03/15 05:52:55 gmcgarry Exp $ */ 2 3 /*- 4 * Copyright (c) 1996, 1997, 1999 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 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the NetBSD 21 * Foundation, Inc. and its contributors. 22 * 4. Neither the name of The NetBSD Foundation nor the names of its 23 * contributors may be used to endorse or promote products derived 24 * from this software without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 36 * POSSIBILITY OF SUCH DAMAGE. 37 */ 38 39 /* 40 * Link and dispatch interrupts. 41 */ 42 43 #include <sys/cdefs.h> 44 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.22 2002/03/15 05:52:55 gmcgarry Exp $"); 45 46 #define _HP300_INTR_H_PRIVATE 47 48 #include <sys/param.h> 49 #include <sys/systm.h> 50 #include <sys/malloc.h> 51 #include <sys/vmmeter.h> 52 53 #include <uvm/uvm_extern.h> 54 55 #include <net/netisr.h> 56 57 #include <machine/cpu.h> 58 #include <machine/intr.h> 59 60 /* 61 * The location and size of the autovectored interrupt portion 62 * of the vector table. 63 */ 64 #define ISRLOC 0x18 65 #define NISR 8 66 67 struct hp300_intr hp300_intr_list[NISR]; 68 static const char *hp300_intr_names[NISR] = { 69 "spurious", 70 "lev1", 71 "lev2", 72 "lev3", 73 "lev4", 74 "lev5", 75 "clock", 76 "nmi", 77 }; 78 79 u_short hp300_ipls[HP300_NIPLS]; 80 81 void intr_computeipl __P((void)); 82 void netintr __P((void)); 83 84 void 85 intr_init() 86 { 87 struct hp300_intr *hi; 88 int i; 89 90 /* Initialize the ISR lists. */ 91 for (i = 0; i < NISR; ++i) { 92 hi = &hp300_intr_list[i]; 93 LIST_INIT(&hi->hi_q); 94 evcnt_attach_dynamic(&hi->hi_evcnt, EVCNT_TYPE_INTR, 95 NULL, hp300_intr_names[i], "intr"); 96 } 97 98 /* Default interrupt priorities. */ 99 hp300_ipls[HP300_IPL_SOFT] = PSL_S|PSL_IPL1; 100 hp300_ipls[HP300_IPL_BIO] = PSL_S|PSL_IPL3; 101 hp300_ipls[HP300_IPL_NET] = PSL_S|PSL_IPL3; 102 hp300_ipls[HP300_IPL_TTY] = PSL_S|PSL_IPL3; 103 hp300_ipls[HP300_IPL_VM] = PSL_S|PSL_IPL3; 104 hp300_ipls[HP300_IPL_CLOCK] = PSL_S|PSL_IPL6; 105 hp300_ipls[HP300_IPL_HIGH] = PSL_S|PSL_IPL7; 106 } 107 108 /* 109 * Scan all of the ISRs, recomputing the interrupt levels for the spl*() 110 * calls. This doesn't have to be fast. 111 */ 112 void 113 intr_computeipl() 114 { 115 struct hp300_intrhand *ih; 116 int ipl; 117 118 /* Start with low values. */ 119 hp300_ipls[HP300_IPL_BIO] = 120 hp300_ipls[HP300_IPL_NET] = 121 hp300_ipls[HP300_IPL_TTY] = 122 hp300_ipls[HP300_IPL_VM] = PSL_S|PSL_IPL3; 123 124 for (ipl = 0; ipl < NISR; ipl++) { 125 for (ih = LIST_FIRST(&hp300_intr_list[ipl].hi_q); ih != NULL; 126 ih = LIST_NEXT(ih, ih_q)) { 127 /* 128 * Bump up the level for a given priority, 129 * if necessary. 130 */ 131 switch (ih->ih_priority) { 132 case IPL_BIO: 133 if (ipl > PSLTOIPL(hp300_ipls[HP300_IPL_BIO])) 134 hp300_ipls[HP300_IPL_BIO] = 135 IPLTOPSL(ipl); 136 break; 137 138 case IPL_NET: 139 if (ipl > PSLTOIPL(hp300_ipls[HP300_IPL_NET])) 140 hp300_ipls[HP300_IPL_NET] = 141 IPLTOPSL(ipl); 142 break; 143 144 case IPL_TTY: 145 case IPL_TTYNOBUF: 146 if (ipl > PSLTOIPL(hp300_ipls[HP300_IPL_TTY])) 147 hp300_ipls[HP300_IPL_TTY] = 148 IPLTOPSL(ipl); 149 break; 150 151 default: 152 printf("priority = %d\n", ih->ih_priority); 153 panic("intr_computeipl: bad priority"); 154 } 155 } 156 } 157 158 /* 159 * Enforce `bio <= net <= tty <= imp' 160 */ 161 162 if (hp300_ipls[HP300_IPL_NET] < hp300_ipls[HP300_IPL_BIO]) 163 hp300_ipls[HP300_IPL_NET] = hp300_ipls[HP300_IPL_BIO]; 164 165 if (hp300_ipls[HP300_IPL_TTY] < hp300_ipls[HP300_IPL_NET]) 166 hp300_ipls[HP300_IPL_TTY] = hp300_ipls[HP300_IPL_NET]; 167 168 if (hp300_ipls[HP300_IPL_VM] < hp300_ipls[HP300_IPL_TTY]) 169 hp300_ipls[HP300_IPL_VM] = hp300_ipls[HP300_IPL_TTY]; 170 } 171 172 void 173 intr_printlevels() 174 { 175 176 #ifdef DEBUG 177 printf("psl: bio = 0x%x, net = 0x%x, tty = 0x%x, imp = 0x%x\n", 178 hp300_ipls[HP300_IPL_BIO], hp300_ipls[HP300_IPL_NET], 179 hp300_ipls[HP300_IPL_TTY], hp300_ipls[HP300_IPL_VM]); 180 #endif 181 182 printf("interrupt levels: bio = %d, net = %d, tty = %d\n", 183 PSLTOIPL(hp300_ipls[HP300_IPL_BIO]), 184 PSLTOIPL(hp300_ipls[HP300_IPL_NET]), 185 PSLTOIPL(hp300_ipls[HP300_IPL_TTY])); 186 } 187 188 /* 189 * Establish an interrupt handler. 190 * Called by driver attach functions. 191 */ 192 void * 193 intr_establish(func, arg, ipl, priority) 194 int (*func) __P((void *)); 195 void *arg; 196 int ipl; 197 int priority; 198 { 199 struct hp300_intrhand *newih, *curih; 200 201 if ((ipl < 0) || (ipl >= NISR)) 202 panic("intr_establish: bad ipl %d", ipl); 203 204 MALLOC(newih, struct hp300_intrhand *, sizeof(struct hp300_intrhand), 205 M_DEVBUF, M_NOWAIT); 206 if (newih == NULL) 207 panic("intr_establish: can't allocate space for handler"); 208 209 /* Fill in the new entry. */ 210 newih->ih_fn = func; 211 newih->ih_arg = arg; 212 newih->ih_ipl = ipl; 213 newih->ih_priority = priority; 214 215 /* 216 * Some devices are particularly sensitive to interrupt 217 * handling latency. The DCA, for example, can lose many 218 * characters if its interrupt isn't handled with reasonable 219 * speed. For this reason, we sort ISRs by IPL_* priority, 220 * inserting higher priority interrupts before lower priority 221 * interrupts. 222 */ 223 224 /* 225 * Get the appropriate ISR list. If the list is empty, no 226 * additional work is necessary; we simply insert ourselves 227 * at the head of the list. 228 */ 229 230 if (LIST_FIRST(&hp300_intr_list[ipl].hi_q) == NULL) { 231 LIST_INSERT_HEAD(&hp300_intr_list[ipl].hi_q, newih, ih_q); 232 goto compute; 233 } 234 235 /* 236 * A little extra work is required. We traverse the list 237 * and place ourselves after any ISRs with our current (or 238 * higher) priority. 239 */ 240 241 for (curih = LIST_FIRST(&hp300_intr_list[ipl].hi_q); 242 LIST_NEXT(curih,ih_q) != NULL; 243 curih = LIST_NEXT(curih,ih_q)) { 244 if (newih->ih_priority > curih->ih_priority) { 245 LIST_INSERT_BEFORE(curih, newih, ih_q); 246 goto compute; 247 } 248 } 249 250 /* 251 * We're the least important entry, it seems. We just go 252 * on the end. 253 */ 254 LIST_INSERT_AFTER(curih, newih, ih_q); 255 256 compute: 257 /* Compute new interrupt levels. */ 258 intr_computeipl(); 259 return (newih); 260 } 261 262 /* 263 * Disestablish an interrupt handler. 264 */ 265 void 266 intr_disestablish(arg) 267 void *arg; 268 { 269 struct hp300_intrhand *ih = arg; 270 271 LIST_REMOVE(ih, ih_q); 272 free(ih, M_DEVBUF); 273 intr_computeipl(); 274 } 275 276 /* 277 * This is the dispatcher called by the low-level 278 * assembly language interrupt routine. 279 */ 280 void 281 intr_dispatch(evec) 282 int evec; /* format | vector offset */ 283 { 284 struct hp300_intrhand *ih; 285 struct hp300_intr *list; 286 int handled, ipl, vec; 287 static int straycount, unexpected; 288 289 vec = (evec & 0xfff) >> 2; 290 if ((vec < ISRLOC) || (vec >= (ISRLOC + NISR))) 291 panic("intr_dispatch: bad vec 0x%x\n", vec); 292 ipl = vec - ISRLOC; 293 294 hp300_intr_list[ipl].hi_evcnt.ev_count++; 295 uvmexp.intrs++; 296 297 list = &hp300_intr_list[ipl]; 298 if (LIST_FIRST(&list->hi_q) == NULL) { 299 printf("intr_dispatch: ipl %d unexpected\n", ipl); 300 if (++unexpected > 10) 301 panic("intr_dispatch: too many unexpected interrupts"); 302 return; 303 } 304 305 handled = 0; 306 /* Give all the handlers a chance. */ 307 for (ih = LIST_FIRST(&list->hi_q) ; ih != NULL; 308 ih = LIST_NEXT(ih, ih_q)) 309 handled |= (*ih->ih_fn)(ih->ih_arg); 310 311 if (handled) 312 straycount = 0; 313 else if (++straycount > 50) 314 panic("intr_dispatch: too many stray interrupts"); 315 else 316 printf("intr_dispatch: stray level %d interrupt\n", ipl); 317 } 318 319 void 320 netintr() 321 { 322 int s, isr; 323 324 for (;;) { 325 s = splhigh(); 326 isr = netisr; 327 netisr = 0; 328 splx(s); 329 330 if (isr == 0) 331 return; 332 333 #define DONETISR(bit, fn) do { \ 334 if (isr & (1 << bit)) \ 335 fn(); \ 336 } while(0) 337 338 s = splsoftnet(); 339 340 #include <net/netisr_dispatch.h> 341 342 #undef DONETISR 343 344 splx(s); 345 } 346 } 347