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$"); 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 struct trapframe *stf; 305 u_int xiv; 306 307 td = curthread; 308 ia64_set_fpsr(IA64_FPSR_DEFAULT); 309 PCPU_INC(cnt.v_intr); 310 311 xiv = ia64_get_ivr(); 312 ia64_srlz_d(); 313 if (xiv == 15) { 314 PCPU_INC(md.stats.pcs_nstrays); 315 goto out; 316 } 317 318 critical_enter(); 319 stf = td->td_intr_frame; 320 td->td_intr_frame = tf; 321 322 do { 323 CTR2(KTR_INTR, "INTR: ITC=%u, XIV=%u", 324 (u_int)tf->tf_special.ifa, xiv); 325 if (!(ia64_handler[xiv])(td, xiv, tf)) { 326 ia64_set_eoi(0); 327 ia64_srlz_d(); 328 } 329 xiv = ia64_get_ivr(); 330 ia64_srlz_d(); 331 } while (xiv != 15); 332 333 td->td_intr_frame = stf; 334 critical_exit(); 335 336 out: 337 if (TRAPF_USERMODE(tf)) { 338 while (td->td_flags & (TDF_ASTPENDING|TDF_NEEDRESCHED)) { 339 ia64_enable_intr(); 340 ast(tf); 341 ia64_disable_intr(); 342 } 343 } 344} 345 346static u_int 347ia64_ih_invalid(struct thread *td, u_int xiv, struct trapframe *tf) 348{ 349 350 panic("invalid XIV: %u", xiv); 351 return (0); 352} 353 354static u_int 355ia64_ih_irq(struct thread *td, u_int xiv, struct trapframe *tf) 356{ 357 struct ia64_intr *i; 358 struct intr_event *ie; /* our interrupt event */ 359 360 PCPU_INC(md.stats.pcs_nhwints); 361 362 /* Find the interrupt thread for this XIV. */ 363 i = ia64_intrs[xiv]; 364 KASSERT(i != NULL, ("%s: unassigned XIV", __func__)); 365 366 (*i->cntp)++; 367 368 ie = i->event; 369 KASSERT(ie != NULL, ("%s: interrupt without event", __func__)); 370 371 if (intr_event_handle(ie, tf) != 0) { 372 ia64_intr_mask((void *)(uintptr_t)xiv); 373 log(LOG_ERR, "stray irq%u\n", i->irq); 374 } 375 376 return (0); 377} 378 379#ifdef DDB 380 381static void 382db_print_xiv(u_int xiv, int always) 383{ 384 struct ia64_intr *i; 385 386 i = ia64_intrs[xiv]; 387 if (i != NULL) { 388 db_printf("XIV %u (%p): ", xiv, i); 389 sapic_print(i->sapic, i->irq); 390 } else if (always) 391 db_printf("XIV %u: unassigned\n", xiv); 392} 393 394DB_SHOW_COMMAND(xiv, db_show_xiv) 395{ 396 u_int xiv; 397 398 if (have_addr) { 399 xiv = ((addr >> 4) % 16) * 10 + (addr % 16); 400 if (xiv >= IA64_NXIVS) 401 db_printf("error: XIV %u not in range [0..%u]\n", 402 xiv, IA64_NXIVS - 1); 403 else 404 db_print_xiv(xiv, 1); 405 } else { 406 for (xiv = 0; xiv < IA64_NXIVS; xiv++) 407 db_print_xiv(xiv, 0); 408 } 409} 410 411#endif 412