interrupt.c revision 179256
1/* $FreeBSD: head/sys/ia64/ia64/interrupt.c 179256 2008-05-23 19:53:50Z marcel $ */ 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/sched.h> 51#include <sys/smp.h> 52#include <sys/sysctl.h> 53#include <sys/syslog.h> 54 55#include <machine/clock.h> 56#include <machine/cpu.h> 57#include <machine/fpu.h> 58#include <machine/frame.h> 59#include <machine/intr.h> 60#include <machine/md_var.h> 61#include <machine/pcb.h> 62#include <machine/reg.h> 63#include <machine/sapicvar.h> 64#include <machine/smp.h> 65 66#ifdef EVCNT_COUNTERS 67struct evcnt clock_intr_evcnt; /* event counter for clock intrs. */ 68#else 69#include <sys/interrupt.h> 70#include <machine/intrcnt.h> 71#endif 72 73#ifdef DDB 74#include <ddb/ddb.h> 75#endif 76 77#ifdef SMP 78extern int mp_ipi_test; 79#endif 80 81static void ia64_dispatch_intr(void *, u_int); 82 83static void 84dummy_perf(unsigned long vector, struct trapframe *tf) 85{ 86 printf("performance interrupt!\n"); 87} 88 89void (*perf_irq)(unsigned long, struct trapframe *) = dummy_perf; 90 91static unsigned int ints[MAXCPU]; 92SYSCTL_OPAQUE(_debug, OID_AUTO, ints, CTLFLAG_RW, &ints, sizeof(ints), "IU", 93 ""); 94 95static unsigned int clks[MAXCPU]; 96#ifdef SMP 97SYSCTL_OPAQUE(_debug, OID_AUTO, clks, CTLFLAG_RW, &clks, sizeof(clks), "IU", 98 ""); 99#else 100SYSCTL_INT(_debug, OID_AUTO, clks, CTLFLAG_RW, clks, 0, ""); 101#endif 102 103#ifdef SMP 104static unsigned int asts[MAXCPU]; 105SYSCTL_OPAQUE(_debug, OID_AUTO, asts, CTLFLAG_RW, &asts, sizeof(asts), "IU", 106 ""); 107 108static unsigned int rdvs[MAXCPU]; 109SYSCTL_OPAQUE(_debug, OID_AUTO, rdvs, CTLFLAG_RW, &rdvs, sizeof(rdvs), "IU", 110 ""); 111#endif 112 113SYSCTL_NODE(_debug, OID_AUTO, clock, CTLFLAG_RW, 0, "clock statistics"); 114 115static int adjust_edges = 0; 116SYSCTL_INT(_debug_clock, OID_AUTO, adjust_edges, CTLFLAG_RD, 117 &adjust_edges, 0, "Number of times ITC got more than 12.5% behind"); 118 119static int adjust_excess = 0; 120SYSCTL_INT(_debug_clock, OID_AUTO, adjust_excess, CTLFLAG_RD, 121 &adjust_excess, 0, "Total number of ignored ITC interrupts"); 122 123static int adjust_lost = 0; 124SYSCTL_INT(_debug_clock, OID_AUTO, adjust_lost, CTLFLAG_RD, 125 &adjust_lost, 0, "Total number of lost ITC interrupts"); 126 127static int adjust_ticks = 0; 128SYSCTL_INT(_debug_clock, OID_AUTO, adjust_ticks, CTLFLAG_RD, 129 &adjust_ticks, 0, "Total number of ITC interrupts with adjustment"); 130 131void 132interrupt(struct trapframe *tf) 133{ 134 struct thread *td; 135 volatile struct ia64_interrupt_block *ib = IA64_INTERRUPT_BLOCK; 136 uint64_t adj, clk, itc; 137 int64_t delta; 138 u_int vector; 139 int count; 140 uint8_t inta; 141 142 ia64_set_fpsr(IA64_FPSR_DEFAULT); 143 144 td = curthread; 145 146 vector = tf->tf_special.ifa; 147 148 next: 149 /* 150 * Handle ExtINT interrupts by generating an INTA cycle to 151 * read the vector. 152 */ 153 if (vector == 0) { 154 inta = ib->ib_inta; 155 printf("ExtINT interrupt: vector=%u\n", (int)inta); 156 if (inta == 15) { 157 __asm __volatile("mov cr.eoi = r0;; srlz.d"); 158 goto stray; 159 } 160 vector = (int)inta; 161 } else if (vector == 15) 162 goto stray; 163 164 if (vector == CLOCK_VECTOR) {/* clock interrupt */ 165 /* CTR0(KTR_INTR, "clock interrupt"); */ 166 167 itc = ia64_get_itc(); 168 169 PCPU_INC(cnt.v_intr); 170#ifdef EVCNT_COUNTERS 171 clock_intr_evcnt.ev_count++; 172#else 173 intrcnt[INTRCNT_CLOCK]++; 174#endif 175 clks[PCPU_GET(cpuid)]++; 176 177 critical_enter(); 178 179 adj = PCPU_GET(clockadj); 180 clk = PCPU_GET(clock); 181 delta = itc - clk; 182 count = 0; 183 while (delta >= ia64_clock_reload) { 184 /* Only the BSP runs the real clock */ 185 if (PCPU_GET(cpuid) == 0) 186 hardclock(TRAPF_USERMODE(tf), TRAPF_PC(tf)); 187 else 188 hardclock_cpu(TRAPF_USERMODE(tf)); 189 if (profprocs != 0) 190 profclock(TRAPF_USERMODE(tf), TRAPF_PC(tf)); 191 statclock(TRAPF_USERMODE(tf)); 192 delta -= ia64_clock_reload; 193 clk += ia64_clock_reload; 194 if (adj != 0) 195 adjust_ticks++; 196 count++; 197 } 198 ia64_set_itm(ia64_get_itc() + ia64_clock_reload - adj); 199 if (count > 0) { 200 adjust_lost += count - 1; 201 if (delta > (ia64_clock_reload >> 3)) { 202 if (adj == 0) 203 adjust_edges++; 204 adj = ia64_clock_reload >> 4; 205 } else 206 adj = 0; 207 } else { 208 adj = 0; 209 adjust_excess++; 210 } 211 PCPU_SET(clock, clk); 212 PCPU_SET(clockadj, adj); 213 critical_exit(); 214 ia64_srlz_d(); 215 216#ifdef SMP 217 } else if (vector == ipi_vector[IPI_AST]) { 218 asts[PCPU_GET(cpuid)]++; 219 CTR1(KTR_SMP, "IPI_AST, cpuid=%d", PCPU_GET(cpuid)); 220 } else if (vector == ipi_vector[IPI_HIGH_FP]) { 221 struct thread *thr = PCPU_GET(fpcurthread); 222 if (thr != NULL) { 223 mtx_lock_spin(&thr->td_md.md_highfp_mtx); 224 save_high_fp(&thr->td_pcb->pcb_high_fp); 225 thr->td_pcb->pcb_fpcpu = NULL; 226 PCPU_SET(fpcurthread, NULL); 227 mtx_unlock_spin(&thr->td_md.md_highfp_mtx); 228 } 229 } else if (vector == ipi_vector[IPI_RENDEZVOUS]) { 230 rdvs[PCPU_GET(cpuid)]++; 231 CTR1(KTR_SMP, "IPI_RENDEZVOUS, cpuid=%d", PCPU_GET(cpuid)); 232 enable_intr(); 233 smp_rendezvous_action(); 234 disable_intr(); 235 } else if (vector == ipi_vector[IPI_STOP]) { 236 cpumask_t mybit = PCPU_GET(cpumask); 237 238 savectx(PCPU_PTR(pcb)); 239 atomic_set_int(&stopped_cpus, mybit); 240 while ((started_cpus & mybit) == 0) 241 cpu_spinwait(); 242 atomic_clear_int(&started_cpus, mybit); 243 atomic_clear_int(&stopped_cpus, mybit); 244 } else if (vector == ipi_vector[IPI_TEST]) { 245 CTR1(KTR_SMP, "IPI_TEST, cpuid=%d", PCPU_GET(cpuid)); 246 mp_ipi_test++; 247 } else if (vector == ipi_vector[IPI_PREEMPT]) { 248 CTR1(KTR_SMP, "IPI_PREEMPT, cpuid=%d", PCPU_GET(cpuid)); 249 __asm __volatile("mov cr.eoi = r0;; srlz.d"); 250 enable_intr(); 251 sched_preempt(curthread); 252 disable_intr(); 253 goto stray; 254#endif 255 } else { 256 ints[PCPU_GET(cpuid)]++; 257 atomic_add_int(&td->td_intr_nesting_level, 1); 258 ia64_dispatch_intr(tf, vector); 259 atomic_subtract_int(&td->td_intr_nesting_level, 1); 260 } 261 262 __asm __volatile("mov cr.eoi = r0;; srlz.d"); 263 vector = ia64_get_ivr(); 264 if (vector != 15) 265 goto next; 266 267stray: 268 if (TRAPF_USERMODE(tf)) { 269 enable_intr(); 270 userret(td, tf); 271 mtx_assert(&Giant, MA_NOTOWNED); 272 do_ast(tf); 273 } 274} 275 276/* 277 * Hardware irqs have vectors starting at this offset. 278 */ 279#define IA64_HARDWARE_IRQ_BASE 0x20 280 281struct ia64_intr { 282 struct intr_event *event; /* interrupt event */ 283 volatile long *cntp; /* interrupt counter */ 284 struct sapic *sapic; 285 u_int irq; 286}; 287 288static struct ia64_intr *ia64_intrs[256]; 289 290static void 291ia64_intr_eoi(void *arg) 292{ 293 u_int vector = (uintptr_t)arg; 294 struct ia64_intr *i; 295 296 i = ia64_intrs[vector]; 297 if (i != NULL) 298 sapic_eoi(i->sapic, vector); 299} 300 301static void 302ia64_intr_mask(void *arg) 303{ 304 u_int vector = (uintptr_t)arg; 305 struct ia64_intr *i; 306 307 i = ia64_intrs[vector]; 308 if (i != NULL) { 309 sapic_mask(i->sapic, i->irq); 310 sapic_eoi(i->sapic, vector); 311 } 312} 313 314static void 315ia64_intr_unmask(void *arg) 316{ 317 u_int vector = (uintptr_t)arg; 318 struct ia64_intr *i; 319 320 i = ia64_intrs[vector]; 321 if (i != NULL) 322 sapic_unmask(i->sapic, i->irq); 323} 324 325int 326ia64_setup_intr(const char *name, int irq, driver_filter_t filter, 327 driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep) 328{ 329 struct ia64_intr *i; 330 struct sapic *sa; 331 char *intrname; 332 u_int vector; 333 int error; 334 335 /* Get the I/O SAPIC that corresponds to the IRQ. */ 336 sa = sapic_lookup(irq); 337 if (sa == NULL) 338 return (EINVAL); 339 340 /* 341 * XXX - There's a priority implied by the choice of vector. 342 * We should therefore relate the vector to the interrupt type. 343 */ 344 vector = irq + IA64_HARDWARE_IRQ_BASE; 345 346 i = ia64_intrs[vector]; 347 if (i == NULL) { 348 i = malloc(sizeof(struct ia64_intr), M_DEVBUF, M_NOWAIT); 349 if (i == NULL) 350 return (ENOMEM); 351 352 error = intr_event_create(&i->event, (void *)(uintptr_t)vector, 353 0, irq, ia64_intr_mask, ia64_intr_unmask, ia64_intr_eoi, 354 NULL, "irq%u:", irq); 355 if (error) { 356 free(i, M_DEVBUF); 357 return (error); 358 } 359 360 if (!atomic_cmpset_ptr(&ia64_intrs[vector], NULL, i)) { 361 intr_event_destroy(i->event); 362 free(i, M_DEVBUF); 363 i = ia64_intrs[vector]; 364 } else { 365 i->sapic = sa; 366 i->irq = irq; 367 368 i->cntp = intrcnt + irq + INTRCNT_ISA_IRQ; 369 if (name != NULL && *name != '\0') { 370 /* XXX needs abstraction. Too error prone. */ 371 intrname = intrnames + 372 (irq + INTRCNT_ISA_IRQ) * INTRNAME_LEN; 373 memset(intrname, ' ', INTRNAME_LEN - 1); 374 bcopy(name, intrname, strlen(name)); 375 } 376 377 sapic_enable(i->sapic, irq, vector); 378 } 379 } 380 381 error = intr_event_add_handler(i->event, name, filter, handler, arg, 382 intr_priority(flags), flags, cookiep); 383 return (error); 384} 385 386int 387ia64_teardown_intr(void *cookie) 388{ 389 390 return (intr_event_remove_handler(cookie)); 391} 392 393static void 394ia64_dispatch_intr(void *frame, u_int vector) 395{ 396 struct ia64_intr *i; 397 struct intr_event *ie; /* our interrupt event */ 398 399 /* 400 * Find the interrupt thread for this vector. 401 */ 402 i = ia64_intrs[vector]; 403 KASSERT(i != NULL, ("%s: unassigned vector", __func__)); 404 405 (*i->cntp)++; 406 407 ie = i->event; 408 KASSERT(ie != NULL, ("%s: interrupt without event", __func__)); 409 410 if (intr_event_handle(ie, frame) != 0) { 411 /* 412 * XXX: The pre-INTR_FILTER code didn't mask stray 413 * interrupts. 414 */ 415 ia64_intr_mask((void *)(uintptr_t)vector); 416 log(LOG_ERR, "stray irq%u\n", i->irq); 417 } 418} 419 420#ifdef DDB 421 422static void 423db_print_vector(u_int vector, int always) 424{ 425 struct ia64_intr *i; 426 427 i = ia64_intrs[vector]; 428 if (i != NULL) { 429 db_printf("vector %u (%p): ", vector, i); 430 sapic_print(i->sapic, i->irq); 431 } else if (always) 432 db_printf("vector %u: unassigned\n", vector); 433} 434 435DB_SHOW_COMMAND(vector, db_show_vector) 436{ 437 u_int vector; 438 439 if (have_addr) { 440 vector = ((addr >> 4) % 16) * 10 + (addr % 16); 441 if (vector >= 256) 442 db_printf("error: vector %u not in range [0..255]\n", 443 vector); 444 else 445 db_print_vector(vector, 1); 446 } else { 447 for (vector = 0; vector < 256; vector++) 448 db_print_vector(vector, 0); 449 } 450} 451 452#endif 453