1 /* $OpenBSD: intr.c,v 1.68 2024/11/08 08:44:07 miod Exp $ */ 2 /* $NetBSD: intr.c,v 1.39 2001/07/19 23:38:11 eeh Exp $ */ 3 4 /* 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This software was developed by the Computer Systems Engineering group 9 * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and 10 * contributed to Berkeley. 11 * 12 * All advertising materials mentioning features or use of this software 13 * must display the following acknowledgement: 14 * This product includes software developed by the University of 15 * California, Lawrence Berkeley Laboratory. 16 * 17 * Redistribution and use in source and binary forms, with or without 18 * modification, are permitted provided that the following conditions 19 * are met: 20 * 1. Redistributions of source code must retain the above copyright 21 * notice, this list of conditions and the following disclaimer. 22 * 2. Redistributions in binary form must reproduce the above copyright 23 * notice, this list of conditions and the following disclaimer in the 24 * documentation and/or other materials provided with the distribution. 25 * 3. Neither the name of the University nor the names of its contributors 26 * may be used to endorse or promote products derived from this software 27 * without specific prior written permission. 28 * 29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT OT LIMITED TO, THE 31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 39 * SUCH DAMAGE. 40 * 41 * @(#)intr.c 8.3 (Berkeley) 11/11/93 42 */ 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/kernel.h> 47 #include <sys/malloc.h> 48 49 #include <dev/cons.h> 50 51 #include <machine/atomic.h> 52 #include <machine/cpu.h> 53 #include <machine/ctlreg.h> 54 #include <machine/instr.h> 55 #include <machine/trap.h> 56 57 /* Grab interrupt map stuff (what is it doing there???) */ 58 #include <sparc64/dev/iommureg.h> 59 60 /* 61 * The following array is to used by locore.s to map interrupt packets 62 * to the proper IPL to send ourselves a softint. It should be filled 63 * in as the devices are probed. We should eventually change this to a 64 * vector table and call these things directly. 65 */ 66 struct intrhand *intrlev[MAXINTNUM]; 67 68 #define INTR_DEVINO 0x8000 69 70 int intr_handler(struct trapframe *, struct intrhand *); 71 int intr_list_handler(void *); 72 void intr_ack(struct intrhand *); 73 74 int 75 intr_handler(struct trapframe *tf, struct intrhand *ih) 76 { 77 struct cpu_info *ci = curcpu(); 78 int rc; 79 #ifdef MULTIPROCESSOR 80 int need_lock; 81 82 if (ih->ih_mpsafe) 83 need_lock = 0; 84 else 85 need_lock = tf->tf_pil < PIL_SCHED && tf->tf_pil != PIL_CLOCK; 86 87 if (need_lock) 88 KERNEL_LOCK(); 89 #endif 90 ci->ci_idepth++; 91 rc = (*ih->ih_fun)(ih->ih_arg ? ih->ih_arg : tf); 92 ci->ci_idepth--; 93 #ifdef MULTIPROCESSOR 94 if (need_lock) 95 KERNEL_UNLOCK(); 96 #endif 97 return rc; 98 } 99 100 /* 101 * PCI devices can share interrupts so we need to have 102 * a handler to hand out interrupts. 103 */ 104 int 105 intr_list_handler(void *arg) 106 { 107 struct cpu_info *ci = curcpu(); 108 struct intrhand *ih = arg; 109 int claimed = 0, rv, ipl = ci->ci_handled_intr_level; 110 111 while (ih) { 112 sparc_wrpr(pil, ih->ih_pil, 0); 113 ci->ci_handled_intr_level = ih->ih_pil; 114 115 rv = ih->ih_fun(ih->ih_arg); 116 if (rv) { 117 ih->ih_count.ec_count++; 118 claimed = 1; 119 if (rv == 1) 120 break; 121 } 122 123 ih = ih->ih_next; 124 } 125 sparc_wrpr(pil, ipl, 0); 126 ci->ci_handled_intr_level = ipl; 127 128 return (claimed); 129 } 130 131 void 132 intr_ack(struct intrhand *ih) 133 { 134 *ih->ih_clr = INTCLR_IDLE; 135 } 136 137 /* 138 * Attach an interrupt handler to the vector chain. 139 */ 140 void 141 intr_establish(struct intrhand *ih) 142 { 143 struct intrhand *q; 144 u_int64_t m, id; 145 int s; 146 147 s = splhigh(); 148 149 ih->ih_pending = NULL; 150 ih->ih_next = NULL; 151 if (ih->ih_cpu == NULL) 152 ih->ih_cpu = curcpu(); 153 else if (!ih->ih_mpsafe) { 154 panic("non-mpsafe interrupt \"%s\" " 155 "established on a specific cpu", ih->ih_name); 156 } 157 if (ih->ih_clr) 158 ih->ih_ack = intr_ack; 159 else 160 ih->ih_ack = NULL; 161 162 if (strlen(ih->ih_name) == 0) 163 evcount_attach(&ih->ih_count, "unknown", NULL); 164 else 165 evcount_attach(&ih->ih_count, ih->ih_name, NULL); 166 167 if (ih->ih_number & INTR_DEVINO) { 168 splx(s); 169 return; 170 } 171 172 /* 173 * Store in fast lookup table 174 */ 175 if (ih->ih_number <= 0 || ih->ih_number >= MAXINTNUM) 176 panic("intr_establish: bad intr number %x", ih->ih_number); 177 178 q = intrlev[ih->ih_number]; 179 if (q == NULL) { 180 /* No interrupt already there, just put handler in place. */ 181 intrlev[ih->ih_number] = ih; 182 } else { 183 struct intrhand *nih, *pih; 184 int ipl; 185 186 /* 187 * Interrupt is already there. We need to create a 188 * new interrupt handler and interpose it. 189 */ 190 #ifdef DEBUG 191 printf("intr_establish: intr reused %x\n", ih->ih_number); 192 #endif 193 if (q->ih_fun != intr_list_handler) { 194 nih = malloc(sizeof(struct intrhand), 195 M_DEVBUF, M_NOWAIT | M_ZERO); 196 if (nih == NULL) 197 panic("intr_establish"); 198 199 nih->ih_fun = intr_list_handler; 200 nih->ih_arg = q; 201 nih->ih_number = q->ih_number; 202 nih->ih_pil = min(q->ih_pil, ih->ih_pil); 203 nih->ih_map = q->ih_map; 204 nih->ih_clr = q->ih_clr; 205 nih->ih_ack = q->ih_ack; 206 q->ih_ack = NULL; 207 nih->ih_bus = q->ih_bus; 208 nih->ih_cpu = q->ih_cpu; 209 210 intrlev[ih->ih_number] = q = nih; 211 } else 212 q->ih_pil = min(q->ih_pil, ih->ih_pil); 213 214 ih->ih_ack = NULL; 215 216 /* Add ih to list in priority order. */ 217 pih = q; 218 nih = pih->ih_arg; 219 ipl = nih->ih_pil; 220 while (nih && ih->ih_pil <= nih->ih_pil) { 221 ipl = nih->ih_pil; 222 pih = nih; 223 nih = nih->ih_next; 224 } 225 #if DEBUG 226 printf("intr_establish: inserting pri %i after %i\n", 227 ih->ih_pil, ipl); 228 #endif 229 if (pih == q) { 230 ih->ih_next = pih->ih_arg; 231 pih->ih_arg = ih; 232 } else { 233 ih->ih_next = pih->ih_next; 234 pih->ih_next = ih; 235 } 236 } 237 238 if (ih->ih_clr != NULL) /* Set interrupt to idle */ 239 *ih->ih_clr = INTCLR_IDLE; 240 241 if (ih->ih_map) { 242 id = ih->ih_cpu->ci_upaid; 243 m = *ih->ih_map; 244 if (INTTID(m) != id) { 245 #ifdef DEBUG 246 printf("\nintr_establish: changing map 0x%llx -> ", m); 247 #endif 248 m = (m & ~INTMAP_TID) | (id << INTTID_SHIFT); 249 #ifdef DEBUG 250 printf("0x%llx (id=%llx) ", m, id); 251 #endif 252 } 253 m |= INTMAP_V; 254 *ih->ih_map = m; 255 } 256 257 #ifdef DEBUG 258 printf("\nintr_establish: vector %x pil %x mapintr %p " 259 "clrintr %p fun %p arg %p target %d", 260 ih->ih_number, ih->ih_pil, (void *)ih->ih_map, 261 (void *)ih->ih_clr, (void *)ih->ih_fun, 262 (void *)ih->ih_arg, (int)(ih->ih_map ? INTTID(*ih->ih_map) : -1)); 263 #endif 264 265 splx(s); 266 } 267 268 int 269 splraise(int ipl) 270 { 271 KASSERT(ipl >= IPL_NONE); 272 return (_splraise(ipl)); 273 } 274 275 void 276 intr_barrier(void *cookie) 277 { 278 struct intrhand *ih = cookie; 279 280 sched_barrier(ih->ih_cpu); 281 } 282 283 void * 284 softintr_establish(int level, void (*fun)(void *), void *arg) 285 { 286 struct intrhand *ih; 287 288 if (level == IPL_TTY) 289 level = IPL_SOFTTTY; 290 291 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK | M_ZERO); 292 ih->ih_fun = (int (*)(void *))fun; /* XXX */ 293 ih->ih_arg = arg; 294 ih->ih_pil = level; 295 ih->ih_pending = NULL; 296 ih->ih_ack = NULL; 297 ih->ih_clr = NULL; 298 return (ih); 299 } 300 301 void 302 softintr_disestablish(void *cookie) 303 { 304 struct intrhand *ih = cookie; 305 306 free(ih, M_DEVBUF, sizeof(*ih)); 307 } 308 309 void 310 softintr_schedule(void *cookie) 311 { 312 struct intrhand *ih = cookie; 313 314 send_softint(ih->ih_pil, ih); 315 } 316 317 #ifdef DIAGNOSTIC 318 void 319 splassert_check(int wantipl, const char *func) 320 { 321 struct cpu_info *ci = curcpu(); 322 int oldipl; 323 324 __asm volatile("rdpr %%pil,%0" : "=r" (oldipl)); 325 326 if (oldipl < wantipl) { 327 splassert_fail(wantipl, oldipl, func); 328 } 329 330 if (ci->ci_handled_intr_level > wantipl) { 331 /* 332 * XXX - need to show difference between what's blocked and 333 * what's running. 334 */ 335 splassert_fail(wantipl, ci->ci_handled_intr_level, func); 336 } 337 } 338 #endif 339 340 #ifdef SUN4V 341 342 #include <machine/hypervisor.h> 343 344 uint64_t sun4v_group_interrupt_major; 345 346 int64_t 347 sun4v_intr_devino_to_sysino(uint64_t devhandle, uint64_t devino, uint64_t *ino) 348 { 349 if (sun4v_group_interrupt_major < 3) 350 return hv_intr_devino_to_sysino(devhandle, devino, ino); 351 352 KASSERT(INTVEC(devino) == devino); 353 *ino = devino | INTR_DEVINO; 354 return H_EOK; 355 } 356 357 int64_t 358 sun4v_intr_setcookie(uint64_t devhandle, uint64_t ino, uint64_t cookie_value) 359 { 360 if (sun4v_group_interrupt_major < 3) 361 return H_EOK; 362 363 return hv_vintr_setcookie(devhandle, ino, cookie_value); 364 } 365 366 int64_t 367 sun4v_intr_setenabled(uint64_t devhandle, uint64_t ino, uint64_t intr_enabled) 368 { 369 if (sun4v_group_interrupt_major < 3) 370 return hv_intr_setenabled(ino, intr_enabled); 371 372 return hv_vintr_setenabled(devhandle, ino, intr_enabled); 373 } 374 375 int64_t 376 sun4v_intr_setstate(uint64_t devhandle, uint64_t ino, uint64_t intr_state) 377 { 378 if (sun4v_group_interrupt_major < 3) 379 return hv_intr_setstate(ino, intr_state); 380 381 return hv_vintr_setstate(devhandle, ino, intr_state); 382 } 383 384 int64_t 385 sun4v_intr_settarget(uint64_t devhandle, uint64_t ino, uint64_t cpuid) 386 { 387 if (sun4v_group_interrupt_major < 3) 388 return hv_intr_settarget(ino, cpuid); 389 390 return hv_vintr_settarget(devhandle, ino, cpuid); 391 } 392 393 #endif 394