interrupt.c revision 153666
1/* $FreeBSD: head/sys/ia64/ia64/interrupt.c 153666 2005-12-22 22:16:09Z jhb $ */ 2/* $NetBSD: interrupt.c,v 1.23 1998/02/24 07:38:01 thorpej Exp $ */ 3 4/*- 5 * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University. 6 * All rights reserved. 7 * 8 * Authors: Keith Bostic, Chris G. Demetriou 9 * 10 * Permission to use, copy, modify and distribute this software and 11 * its documentation is hereby granted, provided that both the copyright 12 * notice and this permission notice appear in all copies of the 13 * software, derivative works or modified versions, and any portions 14 * thereof, and that both notices appear in supporting documentation. 15 * 16 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 17 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 18 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 19 * 20 * Carnegie Mellon requests users of this software to return to 21 * 22 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 23 * School of Computer Science 24 * Carnegie Mellon University 25 * Pittsburgh PA 15213-3890 26 * 27 * any improvements or extensions that they make and grant Carnegie the 28 * rights to redistribute these changes. 29 */ 30/*- 31 * Additional Copyright (c) 1997 by Matthew Jacob for NASA/Ames Research Center. 32 * Redistribute and modify at will, leaving only this additional copyright 33 * notice. 34 */ 35 36#include "opt_ddb.h" 37 38#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 39 40#include <sys/param.h> 41#include <sys/systm.h> 42#include <sys/kernel.h> 43#include <sys/proc.h> 44#include <sys/vmmeter.h> 45#include <sys/bus.h> 46#include <sys/malloc.h> 47#include <sys/ktr.h> 48#include <sys/lock.h> 49#include <sys/mutex.h> 50#include <sys/smp.h> 51#include <sys/sysctl.h> 52 53#include <machine/clock.h> 54#include <machine/cpu.h> 55#include <machine/fpu.h> 56#include <machine/frame.h> 57#include <machine/intr.h> 58#include <machine/md_var.h> 59#include <machine/pcb.h> 60#include <machine/reg.h> 61#include <machine/sapicvar.h> 62#include <machine/smp.h> 63 64#ifdef EVCNT_COUNTERS 65struct evcnt clock_intr_evcnt; /* event counter for clock intrs. */ 66#else 67#include <sys/interrupt.h> 68#include <machine/intrcnt.h> 69#endif 70 71#ifdef DDB 72#include <ddb/ddb.h> 73#endif 74 75#ifdef SMP 76extern int mp_ipi_test; 77#endif 78 79volatile int mc_expected, mc_received; 80 81static void 82dummy_perf(unsigned long vector, struct trapframe *tf) 83{ 84 printf("performance interrupt!\n"); 85} 86 87void (*perf_irq)(unsigned long, struct trapframe *) = dummy_perf; 88 89static unsigned int ints[MAXCPU]; 90SYSCTL_OPAQUE(_debug, OID_AUTO, ints, CTLFLAG_RW, &ints, sizeof(ints), "IU", 91 ""); 92 93static unsigned int clks[MAXCPU]; 94#ifdef SMP 95SYSCTL_OPAQUE(_debug, OID_AUTO, clks, CTLFLAG_RW, &clks, sizeof(clks), "IU", 96 ""); 97#else 98SYSCTL_INT(_debug, OID_AUTO, clks, CTLFLAG_RW, clks, 0, ""); 99#endif 100 101#ifdef SMP 102static unsigned int asts[MAXCPU]; 103SYSCTL_OPAQUE(_debug, OID_AUTO, asts, CTLFLAG_RW, &asts, sizeof(asts), "IU", 104 ""); 105 106static unsigned int rdvs[MAXCPU]; 107SYSCTL_OPAQUE(_debug, OID_AUTO, rdvs, CTLFLAG_RW, &rdvs, sizeof(rdvs), "IU", 108 ""); 109#endif 110 111SYSCTL_NODE(_debug, OID_AUTO, clock, CTLFLAG_RW, 0, "clock statistics"); 112 113static int adjust_edges = 0; 114SYSCTL_INT(_debug_clock, OID_AUTO, adjust_edges, CTLFLAG_RD, 115 &adjust_edges, 0, "Number of times ITC got more than 12.5% behind"); 116 117static int adjust_excess = 0; 118SYSCTL_INT(_debug_clock, OID_AUTO, adjust_excess, CTLFLAG_RD, 119 &adjust_excess, 0, "Total number of ignored ITC interrupts"); 120 121static int adjust_lost = 0; 122SYSCTL_INT(_debug_clock, OID_AUTO, adjust_lost, CTLFLAG_RD, 123 &adjust_lost, 0, "Total number of lost ITC interrupts"); 124 125static int adjust_ticks = 0; 126SYSCTL_INT(_debug_clock, OID_AUTO, adjust_ticks, CTLFLAG_RD, 127 &adjust_ticks, 0, "Total number of ITC interrupts with adjustment"); 128 129int 130interrupt(u_int64_t vector, struct trapframe *tf) 131{ 132 struct thread *td; 133 volatile struct ia64_interrupt_block *ib = IA64_INTERRUPT_BLOCK; 134 uint64_t adj, clk, itc; 135 int64_t delta; 136 int count; 137 138 ia64_set_fpsr(IA64_FPSR_DEFAULT); 139 140 td = curthread; 141 atomic_add_int(&td->td_intr_nesting_level, 1); 142 143 /* 144 * Handle ExtINT interrupts by generating an INTA cycle to 145 * read the vector. 146 */ 147 if (vector == 0) { 148 vector = ib->ib_inta; 149 printf("ExtINT interrupt: vector=%ld\n", vector); 150 if (vector == 15) 151 goto stray; 152 } 153 154 if (vector == CLOCK_VECTOR) {/* clock interrupt */ 155 /* CTR0(KTR_INTR, "clock interrupt"); */ 156 157 PCPU_LAZY_INC(cnt.v_intr); 158#ifdef EVCNT_COUNTERS 159 clock_intr_evcnt.ev_count++; 160#else 161 intrcnt[INTRCNT_CLOCK]++; 162#endif 163 clks[PCPU_GET(cpuid)]++; 164 165 critical_enter(); 166 167 adj = PCPU_GET(clockadj); 168 itc = ia64_get_itc(); 169 ia64_set_itm(itc + ia64_clock_reload - adj); 170 clk = PCPU_GET(clock); 171 delta = itc - clk; 172 count = 0; 173 while (delta >= ia64_clock_reload) { 174 /* Only the BSP runs the real clock */ 175 if (PCPU_GET(cpuid) == 0) 176 hardclock(TRAPF_USERMODE(tf), TRAPF_PC(tf)); 177 else 178 hardclock_cpu(TRAPF_USERMODE(tf)); 179 if (profprocs != 0) 180 profclock(TRAPF_USERMODE(tf), TRAPF_PC(tf)); 181 statclock(TRAPF_USERMODE(tf)); 182 delta -= ia64_clock_reload; 183 clk += ia64_clock_reload; 184 if (adj != 0) 185 adjust_ticks++; 186 count++; 187 } 188 if (count > 0) { 189 adjust_lost += count - 1; 190 if (delta > (ia64_clock_reload >> 3)) { 191 if (adj == 0) 192 adjust_edges++; 193 adj = ia64_clock_reload >> 4; 194 } else 195 adj = 0; 196 } else { 197 adj = 0; 198 adjust_excess++; 199 } 200 PCPU_SET(clock, clk); 201 PCPU_SET(clockadj, adj); 202 203 critical_exit(); 204 205#ifdef SMP 206 } else if (vector == ipi_vector[IPI_AST]) { 207 asts[PCPU_GET(cpuid)]++; 208 CTR1(KTR_SMP, "IPI_AST, cpuid=%d", PCPU_GET(cpuid)); 209 } else if (vector == ipi_vector[IPI_HIGH_FP]) { 210 struct thread *thr = PCPU_GET(fpcurthread); 211 if (thr != NULL) { 212 mtx_lock_spin(&thr->td_md.md_highfp_mtx); 213 save_high_fp(&thr->td_pcb->pcb_high_fp); 214 thr->td_pcb->pcb_fpcpu = NULL; 215 PCPU_SET(fpcurthread, NULL); 216 mtx_unlock_spin(&thr->td_md.md_highfp_mtx); 217 } 218 } else if (vector == ipi_vector[IPI_RENDEZVOUS]) { 219 rdvs[PCPU_GET(cpuid)]++; 220 CTR1(KTR_SMP, "IPI_RENDEZVOUS, cpuid=%d", PCPU_GET(cpuid)); 221 smp_rendezvous_action(); 222 } else if (vector == ipi_vector[IPI_STOP]) { 223 u_int32_t mybit = PCPU_GET(cpumask); 224 225 CTR1(KTR_SMP, "IPI_STOP, cpuid=%d", PCPU_GET(cpuid)); 226 savectx(PCPU_GET(pcb)); 227 stopped_cpus |= mybit; 228 while ((started_cpus & mybit) == 0) 229 /* spin */; 230 started_cpus &= ~mybit; 231 stopped_cpus &= ~mybit; 232 if (PCPU_GET(cpuid) == 0 && cpustop_restartfunc != NULL) { 233 void (*f)(void) = cpustop_restartfunc; 234 cpustop_restartfunc = NULL; 235 (*f)(); 236 } 237 } else if (vector == ipi_vector[IPI_TEST]) { 238 CTR1(KTR_SMP, "IPI_TEST, cpuid=%d", PCPU_GET(cpuid)); 239 mp_ipi_test++; 240#endif 241 } else { 242 ints[PCPU_GET(cpuid)]++; 243 ia64_dispatch_intr(tf, vector); 244 } 245 246stray: 247 atomic_subtract_int(&td->td_intr_nesting_level, 1); 248 return (TRAPF_USERMODE(tf)); 249} 250 251/* 252 * Hardware irqs have vectors starting at this offset. 253 */ 254#define IA64_HARDWARE_IRQ_BASE 0x20 255 256struct ia64_intr { 257 struct intr_event *event; /* interrupt event */ 258 volatile long *cntp; /* interrupt counter */ 259}; 260 261static struct mtx ia64_intrs_lock; 262static struct ia64_intr *ia64_intrs[256]; 263 264extern struct sapic *ia64_sapics[]; 265extern int ia64_sapic_count; 266 267static void 268ithds_init(void *dummy) 269{ 270 271 mtx_init(&ia64_intrs_lock, "intr table", NULL, MTX_SPIN); 272} 273SYSINIT(ithds_init, SI_SUB_INTR, SI_ORDER_SECOND, ithds_init, NULL); 274 275static void 276ia64_send_eoi(uintptr_t vector) 277{ 278 int irq, i; 279 280 irq = vector - IA64_HARDWARE_IRQ_BASE; 281 for (i = 0; i < ia64_sapic_count; i++) { 282 struct sapic *sa = ia64_sapics[i]; 283 if (irq >= sa->sa_base && irq <= sa->sa_limit) 284 sapic_eoi(sa, vector); 285 } 286} 287 288int 289ia64_setup_intr(const char *name, int irq, driver_intr_t handler, void *arg, 290 enum intr_type flags, void **cookiep, volatile long *cntp) 291{ 292 struct ia64_intr *i; 293 int errcode; 294 intptr_t vector = irq + IA64_HARDWARE_IRQ_BASE; 295 char *intrname; 296 297 /* 298 * XXX - Can we have more than one device on a vector? If so, we have 299 * a race condition here that needs to be worked around similar to 300 * the fashion done in the i386 inthand_add() function. 301 */ 302 303 /* First, check for an existing hash table entry for this vector. */ 304 mtx_lock_spin(&ia64_intrs_lock); 305 i = ia64_intrs[vector]; 306 mtx_unlock_spin(&ia64_intrs_lock); 307 308 if (i == NULL) { 309 /* None was found, so create an entry. */ 310 i = malloc(sizeof(struct ia64_intr), M_DEVBUF, M_NOWAIT); 311 if (i == NULL) 312 return ENOMEM; 313 if (cntp == NULL) 314 i->cntp = intrcnt + irq + INTRCNT_ISA_IRQ; 315 else 316 i->cntp = cntp; 317 if (name != NULL && *name != '\0') { 318 /* XXX needs abstraction. Too error phrone. */ 319 intrname = intrnames + (irq + INTRCNT_ISA_IRQ) * 320 INTRNAME_LEN; 321 memset(intrname, ' ', INTRNAME_LEN - 1); 322 bcopy(name, intrname, strlen(name)); 323 } 324 errcode = intr_event_create(&i->event, (void *)vector, 0, 325 (void (*)(void *))ia64_send_eoi, "intr:"); 326 if (errcode) { 327 free(i, M_DEVBUF); 328 return errcode; 329 } 330 331 mtx_lock_spin(&ia64_intrs_lock); 332 ia64_intrs[vector] = i; 333 mtx_unlock_spin(&ia64_intrs_lock); 334 } 335 336 /* Second, add this handler. */ 337 errcode = intr_event_add_handler(i->event, name, handler, arg, 338 intr_priority(flags), flags, cookiep); 339 if (errcode) 340 return errcode; 341 342 return (sapic_enable(irq, vector)); 343} 344 345int 346ia64_teardown_intr(void *cookie) 347{ 348 349 return (intr_event_remove_handler(cookie)); 350} 351 352void 353ia64_dispatch_intr(void *frame, unsigned long vector) 354{ 355 struct ia64_intr *i; 356 struct intr_event *ie; /* our interrupt event */ 357 struct intr_handler *ih; 358 int error, thread; 359 360 /* 361 * Find the interrupt thread for this vector. 362 */ 363 i = ia64_intrs[vector]; 364 if (i == NULL) 365 return; /* no event for this vector */ 366 367 if (i->cntp) 368 atomic_add_long(i->cntp, 1); 369 370 ie = i->event; 371 KASSERT(ie != NULL, ("interrupt vector without an event")); 372 373 /* 374 * As an optimization, if an event has no handlers, don't 375 * schedule it to run. 376 */ 377 if (TAILQ_EMPTY(&ie->ie_handlers)) 378 return; 379 380 /* 381 * Execute all fast interrupt handlers directly without Giant. Note 382 * that this means that any fast interrupt handler must be MP safe. 383 */ 384 thread = 0; 385 critical_enter(); 386 TAILQ_FOREACH(ih, &ie->ie_handlers, ih_next) { 387 if (!(ih->ih_flags & IH_FAST)) { 388 thread = 1; 389 continue; 390 } 391 CTR4(KTR_INTR, "%s: exec %p(%p) for %s", __func__, 392 ih->ih_handler, ih->ih_argument, ih->ih_name); 393 ih->ih_handler(ih->ih_argument); 394 } 395 critical_exit(); 396 397 if (thread) { 398 error = intr_event_schedule_thread(ie); 399 KASSERT(error == 0, ("got an impossible stray interrupt")); 400 } else 401 ia64_send_eoi(vector); 402} 403 404#ifdef DDB 405 406static void 407db_show_vector(int vector) 408{ 409 int irq, i; 410 411 irq = vector - IA64_HARDWARE_IRQ_BASE; 412 for (i = 0; i < ia64_sapic_count; i++) { 413 struct sapic *sa = ia64_sapics[i]; 414 if (irq >= sa->sa_base && irq <= sa->sa_limit) 415 sapic_print(sa, irq - sa->sa_base); 416 } 417} 418 419DB_SHOW_COMMAND(irq, db_show_irq) 420{ 421 int vector; 422 423 if (have_addr) { 424 vector = ((addr >> 4) % 16) * 10 + (addr % 16); 425 db_show_vector(vector); 426 } else { 427 for (vector = IA64_HARDWARE_IRQ_BASE; 428 vector < IA64_HARDWARE_IRQ_BASE + 64; vector++) 429 db_show_vector(vector); 430 } 431} 432 433#endif 434