1/* $NetBSD: interrupt.c,v 1.5 2010/12/20 00:25:35 matt Exp $ */ 2 3/*- 4 * Copyright (c) 1994, 1995, 1996 Carnegie-Mellon University. 5 * All rights reserved. 6 * 7 * Authors: Keith Bostic, Chris G. Demetriou 8 * 9 * Permission to use, copy, modify and distribute this software and 10 * its documentation is hereby granted, provided that both the copyright 11 * notice and this permission notice appear in all copies of the 12 * software, derivative works or modified versions, and any portions 13 * thereof, and that both notices appear in supporting documentation. 14 * 15 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS" 16 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND 17 * FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE. 18 * 19 * Carnegie Mellon requests users of this software to return to 20 * 21 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU 22 * School of Computer Science 23 * Carnegie Mellon University 24 * Pittsburgh PA 15213-3890 25 * 26 * any improvements or extensions that they make and grant Carnegie the 27 * rights to redistribute these changes. 28 */ 29/*- 30 * Additional Copyright (c) 1997 by Matthew Jacob for NASA/Ames Research Center. 31 * Redistribute and modify at will, leaving only this additional copyright 32 * notice. 33 */ 34 35#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */ 36__KERNEL_RCSID(0, "$NetBSD: interrupt.c,v 1.5 2010/12/20 00:25:35 matt Exp $"); 37 38#include "opt_ddb.h" 39 40#include <sys/param.h> 41#include <sys/evcnt.h> 42#include <sys/lwp.h> 43#include <sys/proc.h> 44#include <sys/malloc.h> 45#include <sys/sched.h> 46 47#include <machine/clock.h> 48#include <machine/cpu.h> 49#include <machine/cpufunc.h> 50#include <machine/fpu.h> 51#include <machine/frame.h> 52#include <machine/intr.h> 53#include <machine/md_var.h> 54#include <machine/sapicvar.h> 55#include <machine/smp.h> 56#include <machine/userret.h> 57 58#ifdef DDB 59#include <ddb/ddb.h> 60#endif 61 62 63static void ia64_intr_eoi(void *); 64static void ia64_intr_mask(void *); 65#if 0 66static void ia64_intr_unmask(void *); 67#endif 68static int ia64_dispatch_intr(void *, u_int); 69 70#ifdef DDB 71void db_print_vector(u_int, int); 72#endif 73 74 75int 76interrupt(uint64_t vector, struct trapframe *tf) 77{ 78 struct cpu_info *ci = curcpu(); 79 volatile struct ia64_interrupt_block *ib = IA64_INTERRUPT_BLOCK; 80 uint64_t adj, clk, itc; 81 int64_t delta; 82 uint8_t inta; 83 int count, handled = 0; 84 85 ia64_set_fpsr(IA64_FPSR_DEFAULT); 86 87 ci->ci_intrdepth++; 88 ci->ci_data.cpu_nintr++; 89 90 next: 91 /* 92 * Handle ExtINT interrupts by generating an INTA cycle to 93 * read the vector. 94 */ 95 if (vector == 0) { 96 inta = ib->ib_inta; 97 printf("ExtINT interrupt: vector=%u\n", (int)inta); 98 if (inta == 15) { 99 __asm __volatile("mov cr.eoi = r0;; srlz.d"); 100 goto stray; 101 } 102 vector = (int)inta; 103 } else if (vector == 15) 104 goto stray; 105 106 if (vector == CLOCK_VECTOR) {/* clock interrupt */ 107 itc = ia64_get_itc(); 108 109 adj = ci->ci_clockadj; 110 clk = ci->ci_clock; 111 delta = itc - clk; 112 count = 0; 113 while (delta >= ia64_clock_reload) { 114 /* Only the BSP runs the real clock */ 115 if (ci->ci_cpuid == 0) 116 hardclock((struct clockframe *)tf); 117 else 118 panic("CLOCK_VECTOR occur on not cpu0"); 119 delta -= ia64_clock_reload; 120 clk += ia64_clock_reload; 121 count++; 122 handled = 1; 123 } 124 ia64_set_itm(ia64_get_itc() + ia64_clock_reload - adj); 125 if (count > 0) { 126 if (delta > (ia64_clock_reload >> 3)) { 127 adj = ia64_clock_reload >> 4; 128 } else 129 adj = 0; 130 } else 131 adj = 0; 132 ci->ci_clock = clk; 133 ci->ci_clockadj = adj; 134 ia64_srlz_d(); 135 136#ifdef MULTIPROCESSOR 137 } else if (vector == ipi_vector[IPI_AST]) { 138 asts[PCPU_GET(cpuid)]++; 139 CTR1(KTR_SMP, "IPI_AST, cpuid=%d", PCPU_GET(cpuid)); 140 } else if (vector == ipi_vector[IPI_HIGH_FP]) { 141 struct thread *thr = PCPU_GET(fpcurthread); 142 143 if (thr != NULL) { 144 mtx_lock_spin(&thr->td_md.md_highfp_mtx); 145 save_high_fp(&thr->td_pcb->pcb_high_fp); 146 thr->td_pcb->pcb_fpcpu = NULL; 147 PCPU_SET(fpcurthread, NULL); 148 mtx_unlock_spin(&thr->td_md.md_highfp_mtx); 149 } 150 } else if (vector == ipi_vector[IPI_RENDEZVOUS]) { 151 rdvs[PCPU_GET(cpuid)]++; 152 CTR1(KTR_SMP, "IPI_RENDEZVOUS, cpuid=%d", PCPU_GET(cpuid)); 153 enable_intr(); 154 smp_rendezvous_action(); 155 disable_intr(); 156 } else if (vector == ipi_vector[IPI_STOP]) { 157 cpumask_t mybit = PCPU_GET(cpumask); 158 159 savectx(PCPU_PTR(pcb)); 160 atomic_set_int(&stopped_cpus, mybit); 161 while ((started_cpus & mybit) == 0) 162 cpu_spinwait(); 163 atomic_clear_int(&started_cpus, mybit); 164 atomic_clear_int(&stopped_cpus, mybit); 165 } else if (vector == ipi_vector[IPI_PREEMPT]) { 166 CTR1(KTR_SMP, "IPI_PREEMPT, cpuid=%d", PCPU_GET(cpuid)); 167 __asm __volatile("mov cr.eoi = r0;; srlz.d"); 168 enable_intr(); 169 sched_preempt(curthread); 170 disable_intr(); 171 goto stray; 172#endif 173 } else { 174 ci->ci_intrdepth++; 175 handled = ia64_dispatch_intr(tf, vector); 176 ci->ci_intrdepth--; 177 } 178 179 __asm __volatile("mov cr.eoi = r0;; srlz.d"); 180 vector = ia64_get_ivr(); 181 if (vector != 15) 182 goto next; 183 184stray: 185 if (TRAPF_USERMODE(tf)) { 186 enable_intr(); 187 userret(curlwp); 188 do_ast(tf); 189 } 190 ci->ci_intrdepth--; 191 return handled; 192} 193 194 195/* 196 * Hardware irqs have vectors starting at this offset. 197 */ 198#define IA64_HARDWARE_IRQ_BASE 0x20 199 200struct ia64_intrhand { 201 int (*ih_func)(void *); 202 void *ih_arg; 203 LIST_ENTRY(ia64_intrhand) ih_q; 204 int ih_level; 205 int ih_irq; 206}; 207struct ia64_intr { 208 u_int irq; 209 struct sapic *sapic; 210 int type; 211 212 LIST_HEAD(, ia64_intrhand) intr_q; 213 214 char evname[32]; 215 struct evcnt evcnt; 216}; 217 218static struct ia64_intr *ia64_intrs[256]; 219 220 221static void 222ia64_intr_eoi(void *arg) 223{ 224 u_int vector = (uintptr_t)arg; 225 struct ia64_intr *i; 226 227 i = ia64_intrs[vector]; 228 if (i != NULL) 229 sapic_eoi(i->sapic, vector); 230} 231 232static void 233ia64_intr_mask(void *arg) 234{ 235 u_int vector = (uintptr_t)arg; 236 struct ia64_intr *i; 237 238 i = ia64_intrs[vector]; 239 if (i != NULL) { 240 sapic_mask(i->sapic, i->irq); 241 sapic_eoi(i->sapic, vector); 242 } 243} 244 245#if 0 246static void 247ia64_intr_unmask(void *arg) 248{ 249 u_int vector = (uintptr_t)arg; 250 struct ia64_intr *i; 251 252 i = ia64_intrs[vector]; 253 if (i != NULL) 254 sapic_unmask(i->sapic, i->irq); 255} 256#endif 257 258void * 259intr_establish(int irq, int type, int level, int (*func)(void *), void *arg) 260{ 261 struct ia64_intr *i; 262 struct ia64_intrhand *ih; 263 struct sapic *sa; 264 u_int vector; 265 266 /* Get the I/O SAPIC that corresponds to the IRQ. */ 267 sa = sapic_lookup(irq); 268 if (sa == NULL) 269 return NULL; 270 271 switch (type) { 272 case IST_EDGE: 273 case IST_LEVEL: 274 break; 275 276 default: 277 return NULL; 278 } 279 280 /* 281 * XXX - There's a priority implied by the choice of vector. 282 * We should therefore relate the vector to the interrupt type. 283 */ 284 vector = irq + IA64_HARDWARE_IRQ_BASE; 285 286 i = ia64_intrs[vector]; 287 if (i == NULL) { 288 i = malloc(sizeof(struct ia64_intr), M_DEVBUF, M_NOWAIT); 289 if (i == NULL) 290 return NULL; 291 i->irq = irq; 292 i->sapic = sa; 293 i->type = type; 294 LIST_INIT(&i->intr_q); 295 snprintf(i->evname, sizeof(i->evname), "irq %d", irq); 296 evcnt_attach_dynamic(&i->evcnt, EVCNT_TYPE_INTR, NULL, 297 "iosapic", i->evname); 298 ia64_intrs[vector] = i; 299 300 sapic_config_intr(irq, type); 301 sapic_enable(i->sapic, irq, vector); 302 } else 303 if (i->type != type) 304 return NULL; 305 306 ih = malloc(sizeof(*ih), M_DEVBUF, M_NOWAIT); 307 if (ih == NULL) 308 return NULL; 309 ih->ih_func = func; 310 ih->ih_arg = arg; 311 ih->ih_level = level; 312 ih->ih_irq = irq; 313 LIST_INSERT_HEAD(&i->intr_q, ih, ih_q); 314 315 return ih; 316} 317 318void 319intr_disestablish(void *cookie) 320{ 321 struct ia64_intr *i; 322 struct ia64_intrhand *ih = cookie; 323 u_int vector = ih->ih_irq + IA64_HARDWARE_IRQ_BASE; 324 325 i = ia64_intrs[vector]; 326 327 LIST_REMOVE(ih, ih_q); 328 if (LIST_FIRST(&i->intr_q) == NULL) { 329 ia64_intr_mask((void *)(uintptr_t)vector); 330 331 ia64_intrs[vector] = NULL; 332 evcnt_detach(&i->evcnt); 333 free(i, M_DEVBUF); 334 } 335 336 free(ih, M_DEVBUF); 337} 338 339static int 340ia64_dispatch_intr(void *frame, u_int vector) 341{ 342 struct ia64_intr *i; 343 struct ia64_intrhand *ih; 344 int handled = 0; 345 346 /* 347 * Find the interrupt thread for this vector. 348 */ 349 i = ia64_intrs[vector]; 350 KASSERT(i != NULL); 351 352 i->evcnt.ev_count++; 353 354 LIST_FOREACH(ih, &i->intr_q, ih_q) { 355 if (__predict_false(ih->ih_func == NULL)) 356 printf("%s: spurious interrupt (irq = %d)\n", 357 __func__, ih->ih_irq); 358 else if (__predict_true((*ih->ih_func)(ih->ih_arg))) 359 handled = 1; 360 } 361 ia64_intr_eoi((void *)(uintptr_t)vector); 362 363 return handled; 364} 365 366#ifdef DDB 367void 368db_print_vector(u_int vector, int always) 369{ 370 struct ia64_intr *i; 371 372 i = ia64_intrs[vector]; 373 if (i != NULL) { 374 db_printf("vector %u (%p): ", vector, i); 375 sapic_print(i->sapic, i->irq); 376 } else if (always) 377 db_printf("vector %u: unassigned\n", vector); 378} 379#endif 380