1 /* $OpenBSD: intr.c,v 1.10 2015/08/29 23:59:19 deraadt Exp $ */ 2 /* $NetBSD: intr.c,v 1.1 2006/09/01 21:26:18 uwe Exp $ */ 3 4 /*- 5 * Copyright (c) 2005 NONAKA Kimihiro 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/kernel.h> 33 #include <sys/malloc.h> 34 #include <sys/device.h> 35 #include <sys/evcount.h> 36 37 #include <sh/trap.h> 38 39 #include <machine/intr.h> 40 41 #define _N_EXTINTR 8 42 43 #define LANDISK_INTEN 0xb0000005 44 #define INTEN_ALL_MASK 0x00 45 46 struct intrhand { 47 int (*ih_fun)(void *); 48 void *ih_arg; 49 struct intrhand *ih_next; 50 int ih_enable; 51 int ih_level; 52 int ih_irq; 53 struct evcount ih_count; 54 const char *ih_name; 55 }; 56 57 struct extintr_handler { 58 int (*eih_func)(void *eih_arg); 59 void *eih_arg; 60 struct intrhand *eih_ih; 61 int eih_nih; 62 }; 63 64 static struct extintr_handler extintr_handler[_N_EXTINTR]; 65 66 static int fakeintr(void *arg); 67 static int extintr_intr_handler(void *arg); 68 69 void 70 intc_intr(int ssr, int spc, int ssp) 71 { 72 struct intc_intrhand *ih; 73 struct clockframe cf; 74 int evtcode; 75 76 evtcode = _reg_read_4(SH4_INTEVT); 77 ih = EVTCODE_IH(evtcode); 78 KDASSERT(ih->ih_func); 79 80 switch (evtcode) { 81 #if 0 82 #define IRL(irq) (0x200 + ((irq) << 5)) 83 case IRL(5): case IRL(6): case IRL(7): case IRL(8): 84 case IRL(9): case IRL(10): case IRL(11): case IRL(12): 85 { 86 int level; 87 uint8_t inten, bit; 88 89 bit = 1 << (EVTCODE_TO_MAP_INDEX(evtcode) - 5); 90 inten = _reg_read_1(LANDISK_INTEN); 91 _reg_write_1(LANDISK_INTEN, inten & ~bit); 92 level = (_IPL_NSOFT + 1) << 4; /* disable softintr */ 93 ssr &= 0xf0; 94 if (level < ssr) 95 level = ssr; 96 (void)_cpu_intr_resume(level); 97 if ((*ih->ih_func)(ih->ih_arg) != 0) 98 ih->ih_count.ec_count++; 99 _reg_write_1(LANDISK_INTEN, inten); 100 break; 101 } 102 #endif 103 default: 104 (void)_cpu_intr_resume(ih->ih_level); 105 if ((*ih->ih_func)(ih->ih_arg) != 0) 106 ih->ih_count.ec_count++; 107 break; 108 109 case SH_INTEVT_TMU0_TUNI0: 110 (void)_cpu_intr_resume(ih->ih_level); 111 cf.spc = spc; 112 cf.ssr = ssr; 113 cf.ssp = ssp; 114 if ((*ih->ih_func)(&cf) != 0) 115 ih->ih_count.ec_count++; 116 break; 117 118 case SH_INTEVT_NMI: 119 printf("NMI ignored.\n"); 120 break; 121 } 122 } 123 124 void 125 intr_init(void) 126 { 127 _reg_write_1(LANDISK_INTEN, INTEN_ALL_MASK); 128 } 129 130 void * 131 extintr_establish(int irq, int level, int (*ih_fun)(void *), void *ih_arg, 132 const char *ih_name) 133 { 134 static struct intrhand fakehand = {fakeintr}; 135 struct extintr_handler *eih; 136 struct intrhand **p, *q, *ih; 137 int evtcode; 138 int s; 139 140 KDASSERT(irq >= 5 && irq < 13); 141 142 ih = malloc(sizeof(*ih), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK); 143 if (ih == NULL) 144 panic("intr_establish: can't malloc handler info"); 145 146 s = _cpu_intr_suspend(); 147 148 switch (level) { 149 default: 150 #if defined(DEBUG) 151 panic("extintr_establish: unknown level %d", level); 152 /*NOTREACHED*/ 153 #endif 154 case IPL_BIO: 155 case IPL_NET: 156 case IPL_TTY: 157 break; 158 } 159 160 eih = &extintr_handler[irq - 5]; 161 if (eih->eih_func == NULL) { 162 evtcode = 0x200 + (irq << 5); 163 eih->eih_func = intc_intr_establish(evtcode, IST_LEVEL, level, 164 extintr_intr_handler, eih, NULL); 165 } 166 167 /* 168 * Figure out where to put the handler. 169 * This is O(N^2), but we want to preserve the order, and N is 170 * generally small. 171 */ 172 for (p = &eih->eih_ih; (q = *p) != NULL; p = &q->ih_next) 173 continue; 174 175 /* 176 * Actually install a fake handler momentarily, since we might be doing 177 * this with interrupts enabled and don't want the real routine called 178 * until masking is set up. 179 */ 180 fakehand.ih_level = level; 181 *p = &fakehand; 182 183 /* 184 * Poke the real handler in now. 185 */ 186 memset(ih, 0, sizeof(*ih)); 187 ih->ih_fun = ih_fun; 188 ih->ih_arg = ih_arg; 189 ih->ih_next = NULL; 190 ih->ih_enable = 1; 191 ih->ih_level = level; 192 ih->ih_irq = irq; 193 ih->ih_name = ih_name; 194 195 if (ih_name != NULL) 196 evcount_attach(&ih->ih_count, ih_name, &ih->ih_irq); 197 *p = ih; 198 199 if (++eih->eih_nih == 1) { 200 /* Unmask interrupt */ 201 _reg_bset_1(LANDISK_INTEN, (1 << (irq - 5))); 202 } 203 204 _cpu_intr_resume(s); 205 206 return (ih); 207 } 208 209 void 210 extintr_disestablish(void *aux) 211 { 212 struct intrhand *ih = aux; 213 struct intrhand **p, *q; 214 struct extintr_handler *eih; 215 int irq; 216 int s; 217 218 KDASSERT(ih != NULL); 219 220 s = _cpu_intr_suspend(); 221 222 irq = ih->ih_irq - 5; 223 eih = &extintr_handler[irq]; 224 /* 225 * Remove the handler from the chain. 226 * This is O(n^2), too. 227 */ 228 for (p = &eih->eih_ih; (q = *p) != NULL && q != ih; p = &q->ih_next) 229 continue; 230 if (q == NULL) 231 panic("extintr_disestablish: handler not registered"); 232 233 *p = q->ih_next; 234 235 #if 0 236 if (ih->ih_name != NULL) 237 evcount_detach(&ih->ih_count); 238 #endif 239 240 free(ih, M_DEVBUF, sizeof *ih); 241 242 if (--eih->eih_nih == 0) { 243 intc_intr_disestablish(eih->eih_func); 244 245 /* Mask interrupt */ 246 _reg_bclr_1(LANDISK_INTEN, (1 << irq)); 247 } 248 249 _cpu_intr_resume(s); 250 } 251 252 void 253 extintr_enable(void *aux) 254 { 255 struct intrhand *ih = aux; 256 struct intrhand *p, *q; 257 struct extintr_handler *eih; 258 int irq; 259 int cnt; 260 int s; 261 262 KDASSERT(ih != NULL); 263 264 s = _cpu_intr_suspend(); 265 266 irq = ih->ih_irq - 5; 267 KDASSERT(irq >= 0 && irq < 8); 268 eih = &extintr_handler[irq]; 269 for (cnt = 0, p = eih->eih_ih, q = NULL; p != NULL; p = p->ih_next) { 270 if (p->ih_enable) { 271 cnt++; 272 } 273 if (p == ih) { 274 q = p; 275 p->ih_enable = 1; 276 } 277 } 278 KDASSERT(q != NULL); 279 280 if (cnt == 0) { 281 /* Unmask interrupt */ 282 _reg_bset_1(LANDISK_INTEN, (1 << irq)); 283 } 284 285 _cpu_intr_resume(s); 286 } 287 288 void 289 extintr_disable(void *aux) 290 { 291 struct intrhand *ih = aux; 292 struct intrhand *p, *q; 293 struct extintr_handler *eih; 294 int irq; 295 int cnt; 296 int s; 297 298 KDASSERT(ih != NULL); 299 300 s = _cpu_intr_suspend(); 301 302 irq = ih->ih_irq - 5; 303 KDASSERT(irq >= 0 && irq < 8); 304 eih = &extintr_handler[irq]; 305 for (cnt = 0, p = eih->eih_ih, q = NULL; p != NULL; p = p->ih_next) { 306 if (p == ih) { 307 q = p; 308 p->ih_enable = 0; 309 } 310 if (!ih->ih_enable) { 311 cnt++; 312 } 313 } 314 KDASSERT(q != NULL); 315 316 if (cnt == 0) { 317 /* Mask interrupt */ 318 _reg_bclr_1(LANDISK_INTEN, (1 << irq)); 319 } 320 321 _cpu_intr_resume(s); 322 } 323 324 void 325 extintr_disable_by_num(int irq) 326 { 327 struct extintr_handler *eih; 328 struct intrhand *ih; 329 int s; 330 331 irq -= 5; 332 KDASSERT(irq >= 0 && irq < 8); 333 334 s = _cpu_intr_suspend(); 335 eih = &extintr_handler[irq]; 336 for (ih = eih->eih_ih; ih != NULL; ih = ih->ih_next) { 337 ih->ih_enable = 0; 338 } 339 /* Mask interrupt */ 340 _reg_bclr_1(LANDISK_INTEN, (1 << irq)); 341 _cpu_intr_resume(s); 342 } 343 344 static int 345 fakeintr(void *arg) 346 { 347 return 0; 348 } 349 350 static int 351 extintr_intr_handler(void *arg) 352 { 353 struct extintr_handler *eih = arg; 354 struct intrhand *ih; 355 int r; 356 357 if (__predict_true(eih != NULL)) { 358 for (ih = eih->eih_ih; ih != NULL; ih = ih->ih_next) { 359 if (__predict_true(ih->ih_enable)) { 360 r = (*ih->ih_fun)(ih->ih_arg); 361 if (__predict_true(r != 0)) { 362 ih->ih_count.ec_count++; 363 if (r == 1) 364 break; 365 } 366 } 367 } 368 return 1; 369 } 370 return 0; 371 } 372 373 #ifdef DIAGNOSTIC 374 void 375 splassert_check(int wantipl, const char *func) 376 { 377 register_t sr; 378 int oldipl; 379 380 __asm__ volatile ("stc sr,%0" : "=r" (sr)); 381 382 oldipl = (sr & 0xf0) >> 4; 383 if (oldipl < wantipl) { 384 splassert_fail(wantipl, oldipl, func); 385 /* 386 * If the splassert_ctl is set to not panic, raise the ipl 387 * in a feeble attempt to reduce damage. 388 */ 389 _cpu_intr_raise(wantipl << 4); 390 } 391 } 392 #endif 393