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