interrupt.c revision 268198
1/*- 2 * Copyright (c) 2010-2011 Marcel Moolenaar 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27#include "opt_ddb.h" 28 29#include <sys/cdefs.h> 30__FBSDID("$FreeBSD: stable/10/sys/ia64/ia64/interrupt.c 268198 2014-07-02 23:33:07Z marcel $"); 31 32#include <sys/param.h> 33#include <sys/systm.h> 34#include <sys/kernel.h> 35#include <sys/proc.h> 36#include <sys/vmmeter.h> 37#include <sys/bus.h> 38#include <sys/interrupt.h> 39#include <sys/malloc.h> 40#include <sys/ktr.h> 41#include <sys/lock.h> 42#include <sys/mutex.h> 43#include <sys/sched.h> 44#include <sys/smp.h> 45#include <sys/sysctl.h> 46#include <sys/syslog.h> 47 48#include <machine/cpu.h> 49#include <machine/fpu.h> 50#include <machine/frame.h> 51#include <machine/intr.h> 52#include <machine/intrcnt.h> 53#include <machine/md_var.h> 54#include <machine/pcb.h> 55#include <machine/reg.h> 56#include <machine/smp.h> 57 58#ifdef DDB 59#include <ddb/ddb.h> 60#endif 61 62struct ia64_intr { 63 struct intr_event *event; /* interrupt event */ 64 volatile long *cntp; /* interrupt counter */ 65 struct sapic *sapic; 66 u_int irq; 67}; 68 69ia64_ihtype *ia64_handler[IA64_NXIVS]; 70 71static enum ia64_xiv_use ia64_xiv[IA64_NXIVS]; 72static struct ia64_intr *ia64_intrs[IA64_NXIVS]; 73 74static ia64_ihtype ia64_ih_invalid; 75static ia64_ihtype ia64_ih_irq; 76 77void 78ia64_xiv_init(void) 79{ 80 u_int xiv; 81 82 for (xiv = 0; xiv < IA64_NXIVS; xiv++) { 83 ia64_handler[xiv] = ia64_ih_invalid; 84 ia64_xiv[xiv] = IA64_XIV_FREE; 85 ia64_intrs[xiv] = NULL; 86 } 87 (void)ia64_xiv_reserve(15, IA64_XIV_ARCH, NULL); 88} 89 90int 91ia64_xiv_free(u_int xiv, enum ia64_xiv_use what) 92{ 93 94 if (xiv >= IA64_NXIVS) 95 return (EINVAL); 96 if (what == IA64_XIV_FREE || what == IA64_XIV_ARCH) 97 return (EINVAL); 98 if (ia64_xiv[xiv] != what) 99 return (ENXIO); 100 ia64_xiv[xiv] = IA64_XIV_FREE; 101 ia64_handler[xiv] = ia64_ih_invalid; 102 return (0); 103} 104 105int 106ia64_xiv_reserve(u_int xiv, enum ia64_xiv_use what, ia64_ihtype ih) 107{ 108 109 if (xiv >= IA64_NXIVS) 110 return (EINVAL); 111 if (what == IA64_XIV_FREE) 112 return (EINVAL); 113 if (ia64_xiv[xiv] != IA64_XIV_FREE) 114 return (EBUSY); 115 ia64_xiv[xiv] = what; 116 ia64_handler[xiv] = (ih == NULL) ? ia64_ih_invalid: ih; 117 if (bootverbose) 118 printf("XIV %u: use=%u, IH=%p\n", xiv, what, ih); 119 return (0); 120} 121 122u_int 123ia64_xiv_alloc(u_int prio, enum ia64_xiv_use what, ia64_ihtype ih) 124{ 125 u_int hwprio; 126 u_int xiv0, xiv; 127 128 hwprio = prio >> 2; 129 if (hwprio > IA64_MAX_HWPRIO) 130 hwprio = IA64_MAX_HWPRIO; 131 132 xiv0 = IA64_NXIVS - (hwprio + 1) * 16; 133 134 KASSERT(xiv0 >= IA64_MIN_XIV, ("%s: min XIV", __func__)); 135 KASSERT(xiv0 < IA64_NXIVS, ("%s: max XIV", __func__)); 136 137 xiv = xiv0; 138 while (xiv < IA64_NXIVS && ia64_xiv_reserve(xiv, what, ih)) 139 xiv++; 140 141 if (xiv < IA64_NXIVS) 142 return (xiv); 143 144 xiv = xiv0; 145 while (xiv >= IA64_MIN_XIV && ia64_xiv_reserve(xiv, what, ih)) 146 xiv--; 147 148 return ((xiv >= IA64_MIN_XIV) ? xiv : 0); 149} 150 151static void 152ia64_intr_eoi(void *arg) 153{ 154 u_int xiv = (uintptr_t)arg; 155 struct ia64_intr *i; 156 157 i = ia64_intrs[xiv]; 158 KASSERT(i != NULL, ("%s", __func__)); 159 sapic_eoi(i->sapic, xiv); 160} 161 162static void 163ia64_intr_mask(void *arg) 164{ 165 u_int xiv = (uintptr_t)arg; 166 struct ia64_intr *i; 167 168 i = ia64_intrs[xiv]; 169 KASSERT(i != NULL, ("%s", __func__)); 170 sapic_mask(i->sapic, i->irq); 171 sapic_eoi(i->sapic, xiv); 172} 173 174static void 175ia64_intr_unmask(void *arg) 176{ 177 u_int xiv = (uintptr_t)arg; 178 struct ia64_intr *i; 179 180 i = ia64_intrs[xiv]; 181 KASSERT(i != NULL, ("%s", __func__)); 182 sapic_unmask(i->sapic, i->irq); 183} 184 185int 186ia64_setup_intr(const char *name, int irq, driver_filter_t filter, 187 driver_intr_t handler, void *arg, enum intr_type flags, void **cookiep) 188{ 189 struct ia64_intr *i; 190 struct sapic *sa; 191 char *intrname; 192 u_int prio, xiv; 193 int error; 194 195 prio = intr_priority(flags); 196 if (prio > PRI_MAX_ITHD) 197 return (EINVAL); 198 199 /* XXX lock */ 200 201 /* Get the I/O SAPIC and XIV that corresponds to the IRQ. */ 202 sa = sapic_lookup(irq, &xiv); 203 if (sa == NULL) { 204 /* XXX unlock */ 205 return (EINVAL); 206 } 207 208 if (xiv == 0) { 209 /* XXX unlock */ 210 i = malloc(sizeof(struct ia64_intr), M_DEVBUF, 211 M_ZERO | M_WAITOK); 212 /* XXX lock */ 213 sa = sapic_lookup(irq, &xiv); 214 KASSERT(sa != NULL, ("sapic_lookup")); 215 if (xiv != 0) 216 free(i, M_DEVBUF); 217 } 218 219 /* 220 * If the IRQ has no XIV assigned to it yet, assign one based 221 * on the priority. 222 */ 223 if (xiv == 0) { 224 xiv = ia64_xiv_alloc(prio, IA64_XIV_IRQ, ia64_ih_irq); 225 if (xiv == 0) { 226 /* XXX unlock */ 227 free(i, M_DEVBUF); 228 return (ENOSPC); 229 } 230 231 error = intr_event_create(&i->event, (void *)(uintptr_t)xiv, 232 0, irq, ia64_intr_mask, ia64_intr_unmask, ia64_intr_eoi, 233 NULL, "irq%u:", irq); 234 if (error) { 235 ia64_xiv_free(xiv, IA64_XIV_IRQ); 236 /* XXX unlock */ 237 free(i, M_DEVBUF); 238 return (error); 239 } 240 241 i->sapic = sa; 242 i->irq = irq; 243 i->cntp = intrcnt + xiv; 244 ia64_intrs[xiv] = i; 245 246 /* XXX unlock */ 247 248 sapic_enable(sa, irq, xiv); 249 250 if (name != NULL && *name != '\0') { 251 /* XXX needs abstraction. Too error prone. */ 252 intrname = intrnames + xiv * INTRNAME_LEN; 253 memset(intrname, ' ', INTRNAME_LEN - 1); 254 bcopy(name, intrname, strlen(name)); 255 } 256 } else { 257 i = ia64_intrs[xiv]; 258 /* XXX unlock */ 259 } 260 261 KASSERT(i != NULL, ("XIV mapping bug")); 262 263 error = intr_event_add_handler(i->event, name, filter, handler, arg, 264 prio, flags, cookiep); 265 return (error); 266} 267 268int 269ia64_teardown_intr(void *cookie) 270{ 271 272 return (intr_event_remove_handler(cookie)); 273} 274 275void 276ia64_bind_intr(void) 277{ 278 struct ia64_intr *i; 279 struct pcpu *pc; 280 u_int xiv; 281 int cpu; 282 283 cpu = MAXCPU; 284 for (xiv = IA64_NXIVS - 1; xiv >= IA64_MIN_XIV; xiv--) { 285 if (ia64_xiv[xiv] != IA64_XIV_IRQ) 286 continue; 287 i = ia64_intrs[xiv]; 288 do { 289 cpu = (cpu == 0) ? MAXCPU - 1 : cpu - 1; 290 pc = cpuid_to_pcpu[cpu]; 291 } while (pc == NULL || !pc->pc_md.awake); 292 sapic_bind_intr(i->irq, pc); 293 } 294} 295 296/* 297 * Interrupt handlers. 298 */ 299 300void 301ia64_handle_intr(struct trapframe *tf) 302{ 303 struct thread *td; 304 u_int xiv; 305 306 td = curthread; 307 ia64_set_fpsr(IA64_FPSR_DEFAULT); 308 PCPU_INC(cnt.v_intr); 309 310 xiv = ia64_get_ivr(); 311 ia64_srlz_d(); 312 if (xiv == 15) { 313 PCPU_INC(md.stats.pcs_nstrays); 314 goto out; 315 } 316 317 critical_enter(); 318 do { 319 CTR3(KTR_INTR, "INTR: XIV=%u, #%u: frame=%p", xiv, 320 PCPU_GET(cnt.v_intr), tf); 321 if (!(ia64_handler[xiv])(td, xiv, tf)) { 322 ia64_set_eoi(0); 323 ia64_srlz_d(); 324 } 325 xiv = ia64_get_ivr(); 326 ia64_srlz_d(); 327 } while (xiv != 15); 328 critical_exit(); 329 330 out: 331 if (TRAPF_USERMODE(tf)) { 332 while (td->td_flags & (TDF_ASTPENDING|TDF_NEEDRESCHED)) { 333 ia64_enable_intr(); 334 ast(tf); 335 ia64_disable_intr(); 336 } 337 } 338} 339 340static u_int 341ia64_ih_invalid(struct thread *td, u_int xiv, struct trapframe *tf) 342{ 343 344 panic("invalid XIV: %u", xiv); 345 return (0); 346} 347 348static u_int 349ia64_ih_irq(struct thread *td, u_int xiv, struct trapframe *tf) 350{ 351 struct ia64_intr *i; 352 struct intr_event *ie; /* our interrupt event */ 353 354 PCPU_INC(md.stats.pcs_nhwints); 355 356 /* Find the interrupt thread for this XIV. */ 357 i = ia64_intrs[xiv]; 358 KASSERT(i != NULL, ("%s: unassigned XIV", __func__)); 359 360 (*i->cntp)++; 361 362 ie = i->event; 363 KASSERT(ie != NULL, ("%s: interrupt without event", __func__)); 364 365 if (intr_event_handle(ie, tf) != 0) { 366 ia64_intr_mask((void *)(uintptr_t)xiv); 367 log(LOG_ERR, "stray irq%u\n", i->irq); 368 } 369 370 return (0); 371} 372 373#ifdef DDB 374 375static void 376db_print_xiv(u_int xiv, int always) 377{ 378 struct ia64_intr *i; 379 380 i = ia64_intrs[xiv]; 381 if (i != NULL) { 382 db_printf("XIV %u (%p): ", xiv, i); 383 sapic_print(i->sapic, i->irq); 384 } else if (always) 385 db_printf("XIV %u: unassigned\n", xiv); 386} 387 388DB_SHOW_COMMAND(xiv, db_show_xiv) 389{ 390 u_int xiv; 391 392 if (have_addr) { 393 xiv = ((addr >> 4) % 16) * 10 + (addr % 16); 394 if (xiv >= IA64_NXIVS) 395 db_printf("error: XIV %u not in range [0..%u]\n", 396 xiv, IA64_NXIVS - 1); 397 else 398 db_print_xiv(xiv, 1); 399 } else { 400 for (xiv = 0; xiv < IA64_NXIVS; xiv++) 401 db_print_xiv(xiv, 0); 402 } 403} 404 405#endif 406