1/* $NetBSD: shared_intr.c,v 1.20 2009/03/14 14:45:52 dsl 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.20 2009/03/14 14:45:52 dsl 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 47static const char *intr_typename(int); 48 49static const char * 50intr_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 68struct alpha_shared_intr * 69alpha_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 cold ? M_NOWAIT : M_WAITOK); 76 if (intr == NULL) 77 panic("alpha_shared_intr_alloc: couldn't malloc intr"); 78 79 for (i = 0; i < n; i++) { 80 TAILQ_INIT(&intr[i].intr_q); 81 intr[i].intr_sharetype = IST_NONE; 82 intr[i].intr_dfltsharetype = IST_NONE; 83 intr[i].intr_nstrays = 0; 84 intr[i].intr_maxstrays = 5; 85 intr[i].intr_private = NULL; 86 if (namesize != 0) { 87 intr[i].intr_string = malloc(namesize, M_DEVBUF, 88 cold ? M_NOWAIT : M_WAITOK); 89 if (intr[i].intr_string == NULL) 90 panic("alpha_shared_intr_alloc: couldn't " 91 "malloc intr string"); 92 } else 93 intr[i].intr_string = NULL; 94 } 95 96 return (intr); 97} 98 99int 100alpha_shared_intr_dispatch(struct alpha_shared_intr *intr, unsigned int num) 101{ 102 struct alpha_shared_intrhand *ih; 103 int rv, handled; 104 105 atomic_add_long(&intr[num].intr_evcnt.ev_count, 1); 106 107 ih = intr[num].intr_q.tqh_first; 108 handled = 0; 109 while (ih != NULL) { 110 111 /* 112 * The handler returns one of three values: 113 * 0: This interrupt wasn't for me. 114 * 1: This interrupt was for me. 115 * -1: This interrupt might have been for me, but I can't say 116 * for sure. 117 */ 118 rv = (*ih->ih_fn)(ih->ih_arg); 119 120 handled = handled || (rv != 0); 121 ih = ih->ih_q.tqe_next; 122 } 123 124 return (handled); 125} 126 127void * 128alpha_shared_intr_establish(struct alpha_shared_intr *intr, unsigned int num, 129 int type, int level, int (*fn)(void *), void *arg, const char *basename) 130{ 131 struct alpha_shared_intrhand *ih; 132 133 if (intr[num].intr_sharetype == IST_UNUSABLE) { 134 printf("alpha_shared_intr_establish: %s %d: unusable\n", 135 basename, num); 136 return NULL; 137 } 138 139 /* no point in sleeping unless someone can free memory. */ 140 ih = malloc(sizeof *ih, M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); 141 if (ih == NULL) 142 panic("alpha_shared_intr_establish: can't malloc intrhand"); 143 144#ifdef DIAGNOSTIC 145 if (type == IST_NONE) 146 panic("alpha_shared_intr_establish: bogus type"); 147#endif 148 149 switch (intr[num].intr_sharetype) { 150 case IST_EDGE: 151 case IST_LEVEL: 152 if (type == intr[num].intr_sharetype) 153 break; 154 case IST_PULSE: 155 if (type != IST_NONE) { 156 if (intr[num].intr_q.tqh_first == NULL) { 157 printf("alpha_shared_intr_establish: %s %d: warning: using %s on %s\n", 158 basename, num, intr_typename(type), 159 intr_typename(intr[num].intr_sharetype)); 160 type = intr[num].intr_sharetype; 161 } else { 162 panic("alpha_shared_intr_establish: %s %d: can't share %s with %s", 163 basename, num, intr_typename(type), 164 intr_typename(intr[num].intr_sharetype)); 165 } 166 } 167 break; 168 169 case IST_NONE: 170 /* not currently used; safe */ 171 break; 172 } 173 174 ih->ih_intrhead = intr; 175 ih->ih_fn = fn; 176 ih->ih_arg = arg; 177 ih->ih_level = level; 178 ih->ih_num = num; 179 180 intr[num].intr_sharetype = type; 181 TAILQ_INSERT_TAIL(&intr[num].intr_q, ih, ih_q); 182 183 return (ih); 184} 185 186void 187alpha_shared_intr_disestablish(struct alpha_shared_intr *intr, void *cookie, 188 const char *basename) 189{ 190 struct alpha_shared_intrhand *ih = cookie; 191 unsigned int num = ih->ih_num; 192 193 /* 194 * Just remove it from the list and free the entry. We let 195 * the caller deal with resetting the share type, if appropriate. 196 */ 197 TAILQ_REMOVE(&intr[num].intr_q, ih, ih_q); 198} 199 200int 201alpha_shared_intr_get_sharetype(struct alpha_shared_intr *intr, 202 unsigned int num) 203{ 204 205 return (intr[num].intr_sharetype); 206} 207 208int 209alpha_shared_intr_isactive(struct alpha_shared_intr *intr, unsigned int num) 210{ 211 212 return (intr[num].intr_q.tqh_first != NULL); 213} 214 215int 216alpha_shared_intr_firstactive(struct alpha_shared_intr *intr, unsigned int num) 217{ 218 219 return (intr[num].intr_q.tqh_first != NULL && 220 intr[num].intr_q.tqh_first->ih_q.tqe_next == NULL); 221} 222 223void 224alpha_shared_intr_set_dfltsharetype(struct alpha_shared_intr *intr, 225 unsigned int num, int newdfltsharetype) 226{ 227 228#ifdef DIAGNOSTIC 229 if (alpha_shared_intr_isactive(intr, num)) 230 panic("alpha_shared_intr_set_dfltsharetype on active intr"); 231#endif 232 233 intr[num].intr_dfltsharetype = newdfltsharetype; 234 intr[num].intr_sharetype = intr[num].intr_dfltsharetype; 235} 236 237void 238alpha_shared_intr_set_maxstrays(struct alpha_shared_intr *intr, 239 unsigned int num, int newmaxstrays) 240{ 241 int s = splhigh(); 242 intr[num].intr_maxstrays = newmaxstrays; 243 intr[num].intr_nstrays = 0; 244 splx(s); 245} 246 247void 248alpha_shared_intr_reset_strays(struct alpha_shared_intr *intr, 249 unsigned int num) 250{ 251 252 /* 253 * Don't bother blocking interrupts; this doesn't have to be 254 * precise, but it does need to be fast. 255 */ 256 intr[num].intr_nstrays = 0; 257} 258 259void 260alpha_shared_intr_stray(struct alpha_shared_intr *intr, unsigned int num, 261 const char *basename) 262{ 263 264 intr[num].intr_nstrays++; 265 266 if (intr[num].intr_maxstrays == 0) 267 return; 268 269 if (intr[num].intr_nstrays <= intr[num].intr_maxstrays) 270 log(LOG_ERR, "stray %s %d%s\n", basename, num, 271 intr[num].intr_nstrays >= intr[num].intr_maxstrays ? 272 "; stopped logging" : ""); 273} 274 275void 276alpha_shared_intr_set_private(struct alpha_shared_intr *intr, 277 unsigned int num, void *v) 278{ 279 280 intr[num].intr_private = v; 281} 282 283void * 284alpha_shared_intr_get_private(struct alpha_shared_intr *intr, 285 unsigned int num) 286{ 287 288 return (intr[num].intr_private); 289} 290 291struct evcnt * 292alpha_shared_intr_evcnt(struct alpha_shared_intr *intr, 293 unsigned int num) 294{ 295 296 return (&intr[num].intr_evcnt); 297} 298 299char * 300alpha_shared_intr_string(struct alpha_shared_intr *intr, 301 unsigned int num) 302{ 303 304 return (intr[num].intr_string); 305} 306