1 /* $NetBSD: m68k_intr.c,v 1.13 2024/01/19 20:55:42 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 1996, 2023, 2024 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 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 /* 33 * Common interrupt handling for m68k platforms. 34 */ 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(0, "$NetBSD: m68k_intr.c,v 1.13 2024/01/19 20:55:42 thorpej Exp $"); 38 39 #define _M68K_INTR_PRIVATE 40 41 #include <sys/param.h> 42 #include <sys/systm.h> 43 #include <sys/kmem.h> 44 #include <sys/vmmeter.h> 45 #include <sys/device.h> 46 #include <sys/cpu.h> 47 #include <sys/bus.h> 48 #include <sys/once.h> 49 #include <sys/intr.h> 50 51 #include <machine/vectors.h> 52 53 #include <uvm/uvm_extern.h> 54 55 #ifdef __HAVE_M68K_INTR_VECTORED 56 #ifndef MACHINE_USERVEC_START 57 #define MACHINE_USERVEC_START VECI_USRVEC_START 58 #endif 59 60 #define NVECHANDS (NVECTORS - MACHINE_USERVEC_START) 61 62 #ifndef INTR_FREEVEC 63 #define INTR_FREEVEC badtrap 64 #endif 65 66 extern char INTR_FREEVEC[]; 67 extern char intrstub_vectored[]; 68 #endif /* __HAVE_M68K_INTR_VECTORED */ 69 70 /* A dummy event counter where interrupt stats go to die. */ 71 static struct evcnt bitbucket; 72 73 volatile unsigned int intr_depth; /* updated in assembly glue */ 74 75 static struct m68k_intrhand_list m68k_intrhands_autovec[NAUTOVECTORS]; 76 #ifdef __HAVE_M68K_INTR_VECTORED 77 static struct m68k_intrhand *m68k_intrhands_vectored[NVECHANDS]; 78 #endif 79 80 #ifndef __HAVE_LEGACY_INTRCNT 81 #ifndef MACHINE_INTREVCNT_NAMES 82 #define MACHINE_INTREVCNT_NAMES \ 83 { "spur", "lev1", "lev2", "lev3", "lev4", "lev5", "lev6", "nmi" } 84 #endif 85 static const char * const m68k_intr_evcnt_names[] = MACHINE_INTREVCNT_NAMES; 86 __CTASSERT(__arraycount(m68k_intr_evcnt_names) == NAUTOVECTORS); 87 static const char m68k_intr_evcnt_group[] = "cpu"; 88 89 #define INTRCNT_INIT(x) \ 90 EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, m68k_intr_evcnt_group, \ 91 m68k_intr_evcnt_names[(x)]) 92 93 struct evcnt m68k_intr_evcnt[] = { 94 INTRCNT_INIT(0), INTRCNT_INIT(1), INTRCNT_INIT(2), INTRCNT_INIT(3), 95 INTRCNT_INIT(4), INTRCNT_INIT(5), INTRCNT_INIT(6), INTRCNT_INIT(7), 96 }; 97 __CTASSERT(__arraycount(m68k_intr_evcnt) == NAUTOVECTORS); 98 99 #undef INTRCNT_INIT 100 #endif /* __HAVE_LEGACY_INTRCNT */ 101 102 const uint16_t ipl2psl_table[NIPL] = { 103 [IPL_NONE] = PSL_S | PSL_IPL0, 104 [IPL_SOFTBIO] = PSL_S | MACHINE_PSL_IPL_SOFTBIO, 105 [IPL_SOFTCLOCK] = PSL_S | MACHINE_PSL_IPL_SOFTCLOCK, 106 [IPL_SOFTNET] = PSL_S | MACHINE_PSL_IPL_SOFTNET, 107 [IPL_SOFTSERIAL] = PSL_S | MACHINE_PSL_IPL_SOFTSERIAL, 108 [IPL_VM] = PSL_S | MACHINE_PSL_IPL_VM, 109 [IPL_SCHED] = PSL_S | MACHINE_PSL_IPL_SCHED, 110 [IPL_HIGH] = PSL_S | PSL_IPL7, 111 }; 112 113 /* 114 * m68k_spurintr -- 115 * Interrupt handler for the "spurious interrupt" that comes in 116 * on auto-vector level 0. All we do is claim it; it gets counted 117 * during the normal course of auto-vector interrupt handling. 118 */ 119 static int 120 m68k_spurintr(void *arg) 121 { 122 return 1; 123 } 124 125 static struct m68k_intrhand m68k_spurintr_ih = { 126 .ih_func = m68k_spurintr, 127 .ih_arg = m68k_spurintr, 128 .ih_evcnt = &bitbucket, 129 }; 130 131 static struct m68k_intrhand * 132 m68k_ih_stdalloc(int km_flag) 133 { 134 return kmem_zalloc(sizeof(struct m68k_intrhand), km_flag); 135 } 136 137 static void 138 m68k_ih_stdfree(struct m68k_intrhand *ih) 139 { 140 kmem_free(ih, sizeof(*ih)); 141 } 142 143 static const struct m68k_ih_allocfuncs m68k_ih_stdallocfuncs = { 144 .alloc = m68k_ih_stdalloc, 145 .free = m68k_ih_stdfree, 146 }; 147 148 static const struct m68k_ih_allocfuncs *ih_allocfuncs; 149 150 static struct m68k_intrhand * 151 m68k_ih_alloc(void) 152 { 153 KASSERT(ih_allocfuncs != NULL); 154 return ih_allocfuncs->alloc(KM_SLEEP); 155 } 156 157 static void 158 m68k_ih_free(struct m68k_intrhand *ih) 159 { 160 KASSERT(ih_allocfuncs != NULL); 161 if (__predict_true(ih != &m68k_spurintr_ih)) { 162 ih_allocfuncs->free(ih); 163 } 164 } 165 166 #ifdef __HAVE_M68K_INTR_VECTORED 167 168 #define INTRVEC_SLOT(vec) \ 169 (&m68k_intrhands_vectored[(vec) - MACHINE_USERVEC_START]) 170 171 #define m68k_intrvec_handler(vec) (*INTRVEC_SLOT(vec)) 172 173 static bool 174 m68k_intrvec_add(struct m68k_intrhand *ih) 175 { 176 if (ih->ih_vec < MACHINE_USERVEC_START || ih->ih_vec >= NVECTORS) { 177 aprint_error("%s: vector=0x%x (invalid)\n", __func__, 178 ih->ih_vec); 179 return false; 180 } 181 182 struct m68k_intrhand **slot = 183 &m68k_intrhands_vectored[ih->ih_vec - MACHINE_USERVEC_START]; 184 185 if (*slot != NULL) { 186 aprint_error("%s: vector=0x%x (in use)\n", __func__, 187 ih->ih_vec); 188 return false; 189 } 190 191 if (vec_get_entry(ih->ih_vec) != INTR_FREEVEC) { 192 aprint_error("%s: vector=0x%x (unavailable)\n", __func__, 193 ih->ih_vec); 194 return false; 195 } 196 197 *slot = ih; 198 vec_set_entry(ih->ih_vec, intrstub_vectored); 199 return true; 200 } 201 202 static void 203 m68k_intrvec_remove(struct m68k_intrhand *ih) 204 { 205 KASSERT(ih->ih_vec >= MACHINE_USERVEC_START); 206 KASSERT(ih->ih_vec < NVECTORS); 207 208 struct m68k_intrhand **slot = 209 &m68k_intrhands_vectored[ih->ih_vec - MACHINE_USERVEC_START]; 210 211 KASSERT(*slot == ih); 212 KASSERT(vec_get_entry(ih->ih_vec) == intrstub_vectored); 213 214 vec_set_entry(ih->ih_vec, INTR_FREEVEC); 215 *slot = NULL; 216 } 217 218 /* XXX This is horrible and should burn to the ground. */ 219 void * 220 m68k_intrvec_intrhand(int vec) 221 { 222 KASSERT(vec >= MACHINE_USERVEC_START); 223 KASSERT(vec < NVECTORS); 224 225 return m68k_intrhands_vectored[vec - MACHINE_USERVEC_START]; 226 } 227 228 #endif /* __HAVE_M68K_INTR_VECTORED */ 229 230 /* 231 * m68k_intr_init -- 232 * Initialize the interrupt subsystem. 233 */ 234 void 235 m68k_intr_init(const struct m68k_ih_allocfuncs *allocfuncs) 236 { 237 int i; 238 239 KASSERT(ih_allocfuncs == NULL); 240 if (allocfuncs == NULL) { 241 ih_allocfuncs = &m68k_ih_stdallocfuncs; 242 } else { 243 ih_allocfuncs = allocfuncs; 244 } 245 246 for (i = 0; i < NAUTOVECTORS; i++) { 247 LIST_INIT(&m68k_intrhands_autovec[i]); 248 #ifndef __HAVE_LEGACY_INTRCNT 249 evcnt_attach_static(&m68k_intr_evcnt[i]); 250 #endif 251 } 252 LIST_INSERT_HEAD(&m68k_intrhands_autovec[0], 253 &m68k_spurintr_ih, ih_link); 254 } 255 256 /* 257 * m68k_intr_establish -- 258 * Establish an interrupt handler at the specified vector. 259 * XXX We don't do anything with the flags yet. 260 */ 261 void * 262 m68k_intr_establish(int (*func)(void *), void *arg, struct evcnt *ev, 263 int vec, int ipl, int isrpri, int flags __unused) 264 { 265 struct m68k_intrhand *ih; 266 int s; 267 268 /* 269 * If a platform doesn't want special behavior, we don't 270 * require them to call m68k_intr_init(); we just handle 271 * it here. 272 * 273 * XXX m68k_intr_init() might be called really early, so 274 * XXX can't use a once control. 275 */ 276 if (__predict_false(ih_allocfuncs == NULL)) { 277 m68k_intr_init(NULL); 278 } 279 280 /* These are m68k IPLs, not IPL_* values. */ 281 if (ipl < 0 || ipl > 7) { 282 return NULL; 283 } 284 285 #ifdef __HAVE_M68K_INTR_VECTORED 286 KASSERT(vec >= 0); 287 KASSERT(vec < NVECTORS); 288 #else 289 KASSERT(vec == 0); 290 #endif /* __HAVE_M68K_INTR_VECTORED */ 291 292 ih = m68k_ih_alloc(); 293 ih->ih_func = func; 294 ih->ih_arg = arg; 295 ih->ih_vec = vec; 296 ih->ih_ipl = ipl; 297 ih->ih_isrpri = isrpri; 298 if ((ih->ih_evcnt = ev) == NULL) { 299 ih->ih_evcnt = &bitbucket; 300 } 301 302 #ifdef __HAVE_M68K_INTR_VECTORED 303 if (vec != 0) { 304 if (vec_get_entry(vec) != INTR_FREEVEC) { 305 m68k_ih_free(ih); 306 return NULL; 307 } 308 if (! m68k_intrvec_add(ih)) { 309 m68k_ih_free(ih); 310 return NULL; 311 } 312 return ih; 313 } 314 #endif 315 316 /* 317 * Some devices are particularly sensitive to interrupt 318 * handling latency. Unbuffered serial ports, for example, 319 * can lose data if their interrupts aren't handled with 320 * reasonable speed. For this reason, we sort interrupt 321 * handlers by an abstract "ISR" priority, inserting higher- 322 * priority interrupts before lower-priority interrupts. 323 */ 324 struct m68k_intrhand_list * const list = &m68k_intrhands_autovec[ipl]; 325 struct m68k_intrhand *curih; 326 327 s = splhigh(); 328 if (LIST_EMPTY(list)) { 329 LIST_INSERT_HEAD(list, ih, ih_link); 330 goto done; 331 } 332 for (curih = LIST_FIRST(list); 333 LIST_NEXT(curih, ih_link) != NULL; 334 curih = LIST_NEXT(curih, ih_link)) { 335 if (ih->ih_isrpri > curih->ih_isrpri) { 336 LIST_INSERT_BEFORE(curih, ih, ih_link); 337 goto done; 338 } 339 } 340 LIST_INSERT_AFTER(curih, ih, ih_link); 341 done: 342 splx(s); 343 344 return ih; 345 } 346 347 /* 348 * m68k_intr_disestablish -- 349 * Remove an interrupt handler. Returns true if the handler 350 * list for this vector is now empty. 351 */ 352 bool 353 m68k_intr_disestablish(void *v) 354 { 355 struct m68k_intrhand *ih = v; 356 int s; 357 bool empty; 358 359 #ifdef __HAVE_M68K_INTR_VECTORED 360 if (ih->ih_vec != 0) { 361 KASSERT(vec_get_entry(ih->ih_vec) == intrstub_vectored); 362 m68k_intrvec_remove(ih); 363 empty = true; 364 } else 365 #endif 366 { 367 s = splhigh(); 368 LIST_REMOVE(ih, ih_link); 369 empty = LIST_EMPTY(&m68k_intrhands_autovec[ih->ih_ipl]); 370 splx(s); 371 } 372 373 m68k_ih_free(ih); 374 375 return empty; 376 } 377 378 void m68k_intr_autovec(struct clockframe); 379 380 #ifndef MACHINE_AUTOVEC_IGNORE_STRAY 381 #define MACHINE_AUTOVEC_IGNORE_STRAY(ipl) 0 382 #endif 383 384 /* 385 * m68k_intr_autovec -- 386 * Run the interrupt handlers for an auto-vectored interrupt. 387 * Called from the assembly glue in m68k_intr_stubs.s 388 */ 389 void 390 m68k_intr_autovec(struct clockframe frame) 391 { 392 const int ipl = VECO_TO_VECI(frame.cf_vo) - VECI_INTRAV0; 393 struct m68k_intrhand *ih; 394 bool rv = false; 395 396 m68k_count_intr(ipl); 397 398 LIST_FOREACH(ih, &m68k_intrhands_autovec[ipl], ih_link) { 399 void *arg = ih->ih_arg ? ih->ih_arg : &frame; 400 if (ih->ih_func(arg)) { 401 ih->ih_evcnt->ev_count++; 402 rv = true; 403 } 404 } 405 if (!rv && !MACHINE_AUTOVEC_IGNORE_STRAY(ipl)) { 406 printf("Stray level %d interrupt\n", ipl); 407 } 408 409 ATOMIC_CAS_CHECK(&frame); 410 } 411 412 #ifdef __HAVE_M68K_INTR_VECTORED 413 void m68k_intr_vectored(struct clockframe); 414 415 /* 416 * m68k_intr_vectored -- 417 * Run a vectored interrupt handler. 418 * Called from the assembly glue in m68k_intr_stubs.s 419 */ 420 void 421 m68k_intr_vectored(struct clockframe frame) 422 { 423 const int vec = VECO_TO_VECI(frame.cf_vo); 424 const int ipl = (getsr() >> 8) & 7; 425 struct m68k_intrhand *ih; 426 427 m68k_count_intr(ipl); 428 429 #ifdef DIAGNOSTIC 430 if (vec < MACHINE_USERVEC_START || vec >= NVECTORS) { 431 printf("%s: vector=0x%x (invalid)\n", __func__, vec); 432 goto out; 433 } 434 #endif 435 ih = m68k_intrvec_handler(vec); 436 if (ih == NULL) { 437 printf("%s: vector=0x%x (no handler?)\n", __func__, vec); 438 vec_set_entry(vec, INTR_FREEVEC); 439 } 440 441 if (__predict_true((*ih->ih_func)(ih->ih_arg ? ih->ih_arg 442 : &frame) != 0)) { 443 ih->ih_evcnt->ev_count++; 444 } else { 445 printf("Stray level %d interrupt vector=0x%x\n", 446 ipl, vec); 447 } 448 #ifdef DIAGNOSTIC 449 out: 450 #endif 451 ATOMIC_CAS_CHECK(&frame); 452 } 453 #endif /* __HAVE_M68K_INTR_VECTORED */ 454