intr_machdep.c revision 210601
1/*- 2 * Copyright (c) 1991 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * This code is derived from software contributed to Berkeley by 6 * William Jolitz. 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 * 4. Neither the name of the University nor the names of its contributors 17 * may be used to endorse or promote products derived from this software 18 * without specific prior written permission. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32/*- 33 * Copyright (c) 2001 Jake Burkholder. 34 * All rights reserved. 35 * 36 * Redistribution and use in source and binary forms, with or without 37 * modification, are permitted provided that the following conditions 38 * are met: 39 * 1. Redistributions of source code must retain the above copyright 40 * notice, this list of conditions and the following disclaimer. 41 * 2. Redistributions in binary form must reproduce the above copyright 42 * notice, this list of conditions and the following disclaimer in the 43 * documentation and/or other materials provided with the distribution. 44 * 45 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 46 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 47 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 48 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 49 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 50 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 51 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 52 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 53 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 54 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 55 * SUCH DAMAGE. 56 * 57 * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 58 * form: src/sys/i386/isa/intr_machdep.c,v 1.57 2001/07/20 59 */ 60 61#include <sys/cdefs.h> 62__FBSDID("$FreeBSD: head/sys/sparc64/sparc64/intr_machdep.c 210601 2010-07-29 12:08:46Z mav $"); 63 64#include <sys/param.h> 65#include <sys/systm.h> 66#include <sys/bus.h> 67#include <sys/errno.h> 68#include <sys/interrupt.h> 69#include <sys/kernel.h> 70#include <sys/lock.h> 71#include <sys/mutex.h> 72#include <sys/pcpu.h> 73#include <sys/proc.h> 74#include <sys/smp.h> 75#include <sys/sx.h> 76 77#include <machine/frame.h> 78#include <machine/intr_machdep.h> 79 80#define MAX_STRAY_LOG 5 81 82CTASSERT((1 << IV_SHIFT) == sizeof(struct intr_vector)); 83 84ih_func_t *intr_handlers[PIL_MAX]; 85uint16_t pil_countp[PIL_MAX]; 86 87struct intr_vector intr_vectors[IV_MAX]; 88uint16_t intr_countp[IV_MAX]; 89static u_long intr_stray_count[IV_MAX]; 90 91static const char *const pil_names[] = { 92 "stray", 93 "low", /* PIL_LOW */ 94 "ithrd", /* PIL_ITHREAD */ 95 "rndzvs", /* PIL_RENDEZVOUS */ 96 "ast", /* PIL_AST */ 97 "stop", /* PIL_STOP */ 98 "preempt", /* PIL_PREEMPT */ 99 "hardclock", /* PIL_HARDCLOCK */ 100 "statclock", /* PIL_STATCLOCK */ 101 "stray", "stray", "stray", 102 "filter", /* PIL_FILTER */ 103 "fast", /* PIL_FAST */ 104 "tick", /* PIL_TICK */ 105}; 106 107/* protect the intr_vectors table */ 108static struct sx intr_table_lock; 109/* protect intrcnt_index */ 110static struct mtx intrcnt_lock; 111 112#ifdef SMP 113static int assign_cpu; 114 115static void intr_assign_next_cpu(struct intr_vector *iv); 116static void intr_shuffle_irqs(void *arg __unused); 117#endif 118 119static int intr_assign_cpu(void *arg, u_char cpu); 120static void intr_execute_handlers(void *); 121static void intr_stray_level(struct trapframe *); 122static void intr_stray_vector(void *); 123static int intrcnt_setname(const char *, int); 124static void intrcnt_updatename(int, const char *, int); 125 126static void 127intrcnt_updatename(int vec, const char *name, int ispil) 128{ 129 static int intrcnt_index, stray_pil_index, stray_vec_index; 130 int name_index; 131 132 mtx_lock_spin(&intrcnt_lock); 133 if (intrnames[0] == '\0') { 134 /* for bitbucket */ 135 if (bootverbose) 136 printf("initalizing intr_countp\n"); 137 intrcnt_setname("???", intrcnt_index++); 138 139 stray_vec_index = intrcnt_index++; 140 intrcnt_setname("stray", stray_vec_index); 141 for (name_index = 0; name_index < IV_MAX; name_index++) 142 intr_countp[name_index] = stray_vec_index; 143 144 stray_pil_index = intrcnt_index++; 145 intrcnt_setname("pil", stray_pil_index); 146 for (name_index = 0; name_index < PIL_MAX; name_index++) 147 pil_countp[name_index] = stray_pil_index; 148 } 149 150 if (name == NULL) 151 name = "???"; 152 153 if (!ispil && intr_countp[vec] != stray_vec_index) 154 name_index = intr_countp[vec]; 155 else if (ispil && pil_countp[vec] != stray_pil_index) 156 name_index = pil_countp[vec]; 157 else 158 name_index = intrcnt_index++; 159 160 if (intrcnt_setname(name, name_index)) 161 name_index = 0; 162 163 if (!ispil) 164 intr_countp[vec] = name_index; 165 else 166 pil_countp[vec] = name_index; 167 mtx_unlock_spin(&intrcnt_lock); 168} 169 170static int 171intrcnt_setname(const char *name, int index) 172{ 173 174 if (intrnames + (MAXCOMLEN + 1) * index >= eintrnames) 175 return (E2BIG); 176 snprintf(intrnames + (MAXCOMLEN + 1) * index, MAXCOMLEN + 1, "%-*s", 177 MAXCOMLEN, name); 178 return (0); 179} 180 181void 182intr_setup(int pri, ih_func_t *ihf, int vec, iv_func_t *ivf, void *iva) 183{ 184 char pilname[MAXCOMLEN + 1]; 185 register_t s; 186 187 s = intr_disable(); 188 if (vec != -1) { 189 intr_vectors[vec].iv_func = ivf; 190 intr_vectors[vec].iv_arg = iva; 191 intr_vectors[vec].iv_pri = pri; 192 intr_vectors[vec].iv_vec = vec; 193 } 194 intr_handlers[pri] = ihf; 195 intr_restore(s); 196 snprintf(pilname, MAXCOMLEN + 1, "pil%d: %s", pri, pil_names[pri]); 197 intrcnt_updatename(pri, pilname, 1); 198} 199 200static void 201intr_stray_level(struct trapframe *tf) 202{ 203 204 printf("stray level interrupt %ld\n", tf->tf_level); 205} 206 207static void 208intr_stray_vector(void *cookie) 209{ 210 struct intr_vector *iv; 211 212 iv = cookie; 213 if (intr_stray_count[iv->iv_vec] < MAX_STRAY_LOG) { 214 printf("stray vector interrupt %d\n", iv->iv_vec); 215 intr_stray_count[iv->iv_vec]++; 216 if (intr_stray_count[iv->iv_vec] >= MAX_STRAY_LOG) 217 printf("got %d stray interrupt %d's: not logging " 218 "anymore\n", MAX_STRAY_LOG, iv->iv_vec); 219 } 220} 221 222void 223intr_init1() 224{ 225 int i; 226 227 /* Mark all interrupts as being stray. */ 228 for (i = 0; i < PIL_MAX; i++) 229 intr_handlers[i] = intr_stray_level; 230 for (i = 0; i < IV_MAX; i++) { 231 intr_vectors[i].iv_func = intr_stray_vector; 232 intr_vectors[i].iv_arg = &intr_vectors[i]; 233 intr_vectors[i].iv_pri = PIL_LOW; 234 intr_vectors[i].iv_vec = i; 235 intr_vectors[i].iv_refcnt = 0; 236 } 237 intr_handlers[PIL_LOW] = intr_fast; 238} 239 240void 241intr_init2() 242{ 243 244 sx_init(&intr_table_lock, "intr sources"); 245 mtx_init(&intrcnt_lock, "intrcnt", NULL, MTX_SPIN); 246} 247 248static int 249intr_assign_cpu(void *arg, u_char cpu) 250{ 251#ifdef SMP 252 struct pcpu *pc; 253 struct intr_vector *iv; 254 255 /* 256 * Don't do anything during early boot. We will pick up the 257 * assignment once the APs are started. 258 */ 259 if (assign_cpu && cpu != NOCPU) { 260 pc = pcpu_find(cpu); 261 if (pc == NULL) 262 return (EINVAL); 263 iv = arg; 264 sx_xlock(&intr_table_lock); 265 iv->iv_mid = pc->pc_mid; 266 iv->iv_ic->ic_assign(iv); 267 sx_xunlock(&intr_table_lock); 268 } 269 return (0); 270#else 271 return (EOPNOTSUPP); 272#endif 273} 274 275static void 276intr_execute_handlers(void *cookie) 277{ 278 struct intr_vector *iv; 279 280 iv = cookie; 281 if (__predict_false(intr_event_handle(iv->iv_event, NULL) != 0)) 282 intr_stray_vector(iv); 283} 284 285int 286intr_controller_register(int vec, const struct intr_controller *ic, 287 void *icarg) 288{ 289 struct intr_event *ie; 290 struct intr_vector *iv; 291 int error; 292 293 if (vec < 0 || vec >= IV_MAX) 294 return (EINVAL); 295 sx_xlock(&intr_table_lock); 296 iv = &intr_vectors[vec]; 297 ie = iv->iv_event; 298 sx_xunlock(&intr_table_lock); 299 if (ie != NULL) 300 return (EEXIST); 301 error = intr_event_create(&ie, iv, 0, vec, NULL, ic->ic_clear, 302 ic->ic_clear, intr_assign_cpu, "vec%d:", vec); 303 if (error != 0) 304 return (error); 305 sx_xlock(&intr_table_lock); 306 if (iv->iv_event != NULL) { 307 sx_xunlock(&intr_table_lock); 308 intr_event_destroy(ie); 309 return (EEXIST); 310 } 311 iv->iv_ic = ic; 312 iv->iv_icarg = icarg; 313 iv->iv_event = ie; 314 iv->iv_mid = PCPU_GET(mid); 315 sx_xunlock(&intr_table_lock); 316 return (0); 317} 318 319int 320inthand_add(const char *name, int vec, driver_filter_t *filt, 321 driver_intr_t *handler, void *arg, int flags, void **cookiep) 322{ 323 const struct intr_controller *ic; 324 struct intr_event *ie; 325 struct intr_handler *ih; 326 struct intr_vector *iv; 327 int error, filter; 328 329 if (vec < 0 || vec >= IV_MAX) 330 return (EINVAL); 331 /* 332 * INTR_FAST filters/handlers are special purpose only, allowing 333 * them to be shared just would complicate things unnecessarily. 334 */ 335 if ((flags & INTR_FAST) != 0 && (flags & INTR_EXCL) == 0) 336 return (EINVAL); 337 sx_xlock(&intr_table_lock); 338 iv = &intr_vectors[vec]; 339 ic = iv->iv_ic; 340 ie = iv->iv_event; 341 sx_xunlock(&intr_table_lock); 342 if (ic == NULL || ie == NULL) 343 return (EINVAL); 344 error = intr_event_add_handler(ie, name, filt, handler, arg, 345 intr_priority(flags), flags, cookiep); 346 if (error != 0) 347 return (error); 348 sx_xlock(&intr_table_lock); 349 /* Disable the interrupt while we fiddle with it. */ 350 ic->ic_disable(iv); 351 iv->iv_refcnt++; 352 if (iv->iv_refcnt == 1) 353 intr_setup((flags & INTR_FAST) != 0 ? PIL_FAST : 354 filt != NULL ? PIL_FILTER : PIL_ITHREAD, intr_fast, 355 vec, intr_execute_handlers, iv); 356 else if (filt != NULL) { 357 /* 358 * Check if we need to upgrade from PIL_ITHREAD to PIL_FILTER. 359 * Given that apart from the on-board SCCs and UARTs shared 360 * interrupts are rather uncommon on sparc64 this sould be 361 * pretty rare in practice. 362 */ 363 filter = 0; 364 TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) { 365 if (ih->ih_filter != NULL && ih->ih_filter != filt) { 366 filter = 1; 367 break; 368 } 369 } 370 if (filter == 0) 371 intr_setup(PIL_FILTER, intr_fast, vec, 372 intr_execute_handlers, iv); 373 } 374 intr_stray_count[vec] = 0; 375 intrcnt_updatename(vec, ie->ie_fullname, 0); 376#ifdef SMP 377 if (assign_cpu) 378 intr_assign_next_cpu(iv); 379#endif 380 ic->ic_enable(iv); 381 /* Ensure the interrupt is cleared, it might have triggered before. */ 382 if (ic->ic_clear != NULL) 383 ic->ic_clear(iv); 384 sx_xunlock(&intr_table_lock); 385 return (0); 386} 387 388int 389inthand_remove(int vec, void *cookie) 390{ 391 struct intr_vector *iv; 392 int error; 393 394 if (vec < 0 || vec >= IV_MAX) 395 return (EINVAL); 396 error = intr_event_remove_handler(cookie); 397 if (error == 0) { 398 /* 399 * XXX: maybe this should be done regardless of whether 400 * intr_event_remove_handler() succeeded? 401 */ 402 sx_xlock(&intr_table_lock); 403 iv = &intr_vectors[vec]; 404 iv->iv_refcnt--; 405 if (iv->iv_refcnt == 0) { 406 /* 407 * Don't disable the interrupt for now, so that 408 * stray interrupts get detected... 409 */ 410 intr_setup(PIL_LOW, intr_fast, vec, 411 intr_stray_vector, iv); 412 } 413 sx_xunlock(&intr_table_lock); 414 } 415 return (error); 416} 417 418/* Add a description to an active interrupt handler. */ 419int 420intr_describe(int vec, void *ih, const char *descr) 421{ 422 struct intr_vector *iv; 423 int error; 424 425 if (vec < 0 || vec >= IV_MAX) 426 return (EINVAL); 427 sx_xlock(&intr_table_lock); 428 iv = &intr_vectors[vec]; 429 if (iv == NULL) { 430 sx_xunlock(&intr_table_lock); 431 return (EINVAL); 432 } 433 error = intr_event_describe_handler(iv->iv_event, ih, descr); 434 if (error) { 435 sx_xunlock(&intr_table_lock); 436 return (error); 437 } 438 intrcnt_updatename(vec, iv->iv_event->ie_fullname, 0); 439 sx_xunlock(&intr_table_lock); 440 return (error); 441} 442 443#ifdef SMP 444/* 445 * Support for balancing interrupt sources across CPUs. For now we just 446 * allocate CPUs round-robin. 447 */ 448 449/* The BSP is always a valid target. */ 450static cpumask_t intr_cpus = (1 << 0); 451static int current_cpu; 452 453static void 454intr_assign_next_cpu(struct intr_vector *iv) 455{ 456 struct pcpu *pc; 457 458 sx_assert(&intr_table_lock, SA_XLOCKED); 459 460 /* 461 * Assign this source to a CPU in a round-robin fashion. 462 */ 463 pc = pcpu_find(current_cpu); 464 if (pc == NULL) 465 return; 466 iv->iv_mid = pc->pc_mid; 467 iv->iv_ic->ic_assign(iv); 468 do { 469 current_cpu++; 470 if (current_cpu > mp_maxid) 471 current_cpu = 0; 472 } while (!(intr_cpus & (1 << current_cpu))); 473} 474 475/* Attempt to bind the specified IRQ to the specified CPU. */ 476int 477intr_bind(int vec, u_char cpu) 478{ 479 struct intr_vector *iv; 480 int error; 481 482 if (vec < 0 || vec >= IV_MAX) 483 return (EINVAL); 484 sx_xlock(&intr_table_lock); 485 iv = &intr_vectors[vec]; 486 if (iv == NULL) { 487 sx_xunlock(&intr_table_lock); 488 return (EINVAL); 489 } 490 error = intr_event_bind(iv->iv_event, cpu); 491 sx_xunlock(&intr_table_lock); 492 return (error); 493} 494 495/* 496 * Add a CPU to our mask of valid CPUs that can be destinations of 497 * interrupts. 498 */ 499void 500intr_add_cpu(u_int cpu) 501{ 502 503 if (cpu >= MAXCPU) 504 panic("%s: Invalid CPU ID", __func__); 505 if (bootverbose) 506 printf("INTR: Adding CPU %d as a target\n", cpu); 507 508 intr_cpus |= (1 << cpu); 509} 510 511/* 512 * Distribute all the interrupt sources among the available CPUs once the 513 * APs have been launched. 514 */ 515static void 516intr_shuffle_irqs(void *arg __unused) 517{ 518 struct pcpu *pc; 519 struct intr_vector *iv; 520 int i; 521 522 /* Don't bother on UP. */ 523 if (mp_ncpus == 1) 524 return; 525 526 sx_xlock(&intr_table_lock); 527 assign_cpu = 1; 528 for (i = 0; i < IV_MAX; i++) { 529 iv = &intr_vectors[i]; 530 if (iv != NULL && iv->iv_refcnt > 0) { 531 /* 532 * If this event is already bound to a CPU, 533 * then assign the source to that CPU instead 534 * of picking one via round-robin. 535 */ 536 if (iv->iv_event->ie_cpu != NOCPU && 537 (pc = pcpu_find(iv->iv_event->ie_cpu)) != NULL) { 538 iv->iv_mid = pc->pc_mid; 539 iv->iv_ic->ic_assign(iv); 540 } else 541 intr_assign_next_cpu(iv); 542 } 543 } 544 sx_xunlock(&intr_table_lock); 545} 546SYSINIT(intr_shuffle_irqs, SI_SUB_SMP, SI_ORDER_SECOND, intr_shuffle_irqs, 547 NULL); 548#endif 549