1 /* $OpenBSD: shared_intr.c,v 1.15 2006/06/15 20:08:29 brad Exp $ */ 2 /* $NetBSD: shared_intr.c,v 1.13 2000/03/19 01:46:18 thorpej Exp $ */ 3 4 /* 5 * Copyright (c) 1996 Carnegie-Mellon University. 6 * All rights reserved. 7 * 8 * Authors: Chris G. Demetriou 9 * 10 * Permission to use, copy, modify and distribute this software and 11 * its documentation is hereby granted, provided that both the copyright 12 * notice and this permission notice appear in all copies of the 13 * software, derivative works or modified versions, and any portions 14 * thereof, and that both notices appear in supporting documentation. 15 * 16 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 17 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 18 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 19 * 20 * Carnegie Mellon requests users of this software to return to 21 * 22 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 23 * School of Computer Science 24 * Carnegie Mellon University 25 * Pittsburgh PA 15213-3890 26 * 27 * any improvements or extensions that they make and grant Carnegie the 28 * rights to redistribute these changes. 29 */ 30 31 /* 32 * Common shared-interrupt-line functionality. 33 */ 34 35 #include <sys/param.h> 36 #include <sys/kernel.h> 37 #include <sys/systm.h> 38 #include <sys/malloc.h> 39 #include <sys/syslog.h> 40 #include <sys/queue.h> 41 42 #include <machine/intr.h> 43 44 static const char *intr_typename(int); 45 46 static const char * 47 intr_typename(type) 48 int type; 49 { 50 51 switch (type) { 52 case IST_UNUSABLE: 53 return ("disabled"); 54 case IST_NONE: 55 return ("none"); 56 case IST_PULSE: 57 return ("pulsed"); 58 case IST_EDGE: 59 return ("edge-triggered"); 60 case IST_LEVEL: 61 return ("level-triggered"); 62 } 63 panic("intr_typename: unknown type %d", type); 64 } 65 66 struct alpha_shared_intr * 67 alpha_shared_intr_alloc(n) 68 unsigned int n; 69 { 70 struct alpha_shared_intr *intr; 71 unsigned int i; 72 73 intr = malloc(n * sizeof (struct alpha_shared_intr), M_DEVBUF, 74 cold ? M_NOWAIT : M_WAITOK); 75 if (intr == NULL) 76 panic("alpha_shared_intr_alloc: couldn't malloc intr"); 77 78 for (i = 0; i < n; i++) { 79 TAILQ_INIT(&intr[i].intr_q); 80 intr[i].intr_sharetype = IST_NONE; 81 intr[i].intr_dfltsharetype = IST_NONE; 82 intr[i].intr_nstrays = 0; 83 intr[i].intr_maxstrays = 5; 84 intr[i].intr_private = NULL; 85 } 86 87 return (intr); 88 } 89 90 int 91 alpha_shared_intr_dispatch(intr, num) 92 struct alpha_shared_intr *intr; 93 unsigned int num; 94 { 95 struct alpha_shared_intrhand *ih; 96 int rv, handled; 97 98 handled = 0; 99 TAILQ_FOREACH(ih, &intr[num].intr_q, ih_q) { 100 /* 101 * The handler returns one of three values: 102 * 0: This interrupt wasn't for me. 103 * 1: This interrupt was for me. 104 * -1: This interrupt might have been for me, but I can't say 105 * for sure. 106 */ 107 if ((rv = (*ih->ih_fn)(ih->ih_arg))) 108 ih->ih_count.ec_count++; 109 110 handled = handled || (rv != 0); 111 } 112 113 return (handled); 114 } 115 116 void * 117 alpha_shared_intr_establish(intr, num, type, level, fn, arg, basename) 118 struct alpha_shared_intr *intr; 119 unsigned int num; 120 int type, level; 121 int (*fn)(void *); 122 void *arg; 123 const char *basename; 124 { 125 struct alpha_shared_intrhand *ih; 126 127 if (intr[num].intr_sharetype == IST_UNUSABLE) { 128 printf("alpha_shared_intr_establish: %s %d: unusable\n", 129 basename, num); 130 return NULL; 131 } 132 133 /* no point in sleeping unless someone can free memory. */ 134 ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); 135 if (ih == NULL) 136 panic("alpha_shared_intr_establish: can't malloc intrhand"); 137 138 #ifdef DIAGNOSTIC 139 if (type == IST_NONE) 140 panic("alpha_shared_intr_establish: bogus type"); 141 #endif 142 143 switch (intr[num].intr_sharetype) { 144 case IST_EDGE: 145 case IST_LEVEL: 146 if (type == intr[num].intr_sharetype) 147 break; 148 case IST_PULSE: 149 if (type != IST_NONE) { 150 if (TAILQ_EMPTY(&intr[num].intr_q)) { 151 printf("alpha_shared_intr_establish: %s %d: warning: using %s on %s\n", 152 basename, num, intr_typename(type), 153 intr_typename(intr[num].intr_sharetype)); 154 type = intr[num].intr_sharetype; 155 } else { 156 panic("alpha_shared_intr_establish: %s %d: can't share %s with %s", 157 basename, num, intr_typename(type), 158 intr_typename(intr[num].intr_sharetype)); 159 } 160 } 161 break; 162 163 case IST_NONE: 164 /* not currently used; safe */ 165 break; 166 } 167 168 ih->ih_intrhead = intr; 169 ih->ih_fn = fn; 170 ih->ih_arg = arg; 171 ih->ih_level = level; 172 ih->ih_num = num; 173 evcount_attach(&ih->ih_count, basename, (void *)&ih->ih_num, 174 &evcount_intr); 175 176 intr[num].intr_sharetype = type; 177 TAILQ_INSERT_TAIL(&intr[num].intr_q, ih, ih_q); 178 179 return (ih); 180 } 181 182 void 183 alpha_shared_intr_disestablish(intr, cookie, basename) 184 struct alpha_shared_intr *intr; 185 void *cookie; 186 const char *basename; 187 { 188 struct alpha_shared_intrhand *ih = cookie; 189 unsigned int num = ih->ih_num; 190 191 /* 192 * Just remove it from the list and free the entry. We let 193 * the caller deal with resetting the share type, if appropriate. 194 */ 195 evcount_detach(&ih->ih_count); 196 TAILQ_REMOVE(&intr[num].intr_q, ih, ih_q); 197 free(ih, M_DEVBUF); 198 } 199 200 int 201 alpha_shared_intr_get_sharetype(intr, num) 202 struct alpha_shared_intr *intr; 203 unsigned int num; 204 { 205 206 return (intr[num].intr_sharetype); 207 } 208 209 int 210 alpha_shared_intr_isactive(intr, num) 211 struct alpha_shared_intr *intr; 212 unsigned int num; 213 { 214 215 return (!TAILQ_EMPTY(&intr[num].intr_q)); 216 } 217 218 int 219 alpha_shared_intr_firstactive(struct alpha_shared_intr *intr, unsigned int num) 220 { 221 222 return (!TAILQ_EMPTY(&intr[num].intr_q) && 223 TAILQ_NEXT(intr[num].intr_q.tqh_first, ih_q) == NULL); 224 } 225 226 void 227 alpha_shared_intr_set_dfltsharetype(intr, num, newdfltsharetype) 228 struct alpha_shared_intr *intr; 229 unsigned int num; 230 int newdfltsharetype; 231 { 232 233 #ifdef DIAGNOSTIC 234 if (alpha_shared_intr_isactive(intr, num)) 235 panic("alpha_shared_intr_set_dfltsharetype on active intr"); 236 #endif 237 238 intr[num].intr_dfltsharetype = newdfltsharetype; 239 intr[num].intr_sharetype = intr[num].intr_dfltsharetype; 240 } 241 242 void 243 alpha_shared_intr_set_maxstrays(intr, num, newmaxstrays) 244 struct alpha_shared_intr *intr; 245 unsigned int num; 246 int newmaxstrays; 247 { 248 int s = splhigh(); 249 intr[num].intr_maxstrays = newmaxstrays; 250 intr[num].intr_nstrays = 0; 251 splx(s); 252 } 253 254 void 255 alpha_shared_intr_reset_strays(intr, num) 256 struct alpha_shared_intr *intr; 257 unsigned int num; 258 { 259 260 /* 261 * Don't bother blocking interrupts; this doesn't have to be 262 * precise, but it does need to be fast. 263 */ 264 intr[num].intr_nstrays = 0; 265 } 266 267 void 268 alpha_shared_intr_stray(intr, num, basename) 269 struct alpha_shared_intr *intr; 270 unsigned int num; 271 const char *basename; 272 { 273 274 intr[num].intr_nstrays++; 275 276 if (intr[num].intr_maxstrays == 0) 277 return; 278 279 if (intr[num].intr_nstrays <= intr[num].intr_maxstrays) 280 log(LOG_ERR, "stray %s %d%s\n", basename, num, 281 intr[num].intr_nstrays >= intr[num].intr_maxstrays ? 282 "; stopped logging" : ""); 283 } 284 285 void 286 alpha_shared_intr_set_private(intr, num, v) 287 struct alpha_shared_intr *intr; 288 unsigned int num; 289 void *v; 290 { 291 292 intr[num].intr_private = v; 293 } 294 295 void * 296 alpha_shared_intr_get_private(intr, num) 297 struct alpha_shared_intr *intr; 298 unsigned int num; 299 { 300 301 return (intr[num].intr_private); 302 } 303