1 /* $OpenBSD: intr.c,v 1.12 2025/01/12 21:54:07 kettenis Exp $ */ 2 3 /* 4 * Copyright (c) 2020 Mark Kettenis <kettenis@openbsd.org> 5 * Copyright (c) 2011 Dale Rahn <drahn@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/param.h> 21 #include <sys/malloc.h> 22 #include <sys/systm.h> 23 24 #include <machine/atomic.h> 25 #include <machine/intr.h> 26 27 #include <dev/ofw/openfirm.h> 28 29 /* Dummy implementations. */ 30 void dummy_exi(struct trapframe *); 31 void dummy_hvi(struct trapframe *); 32 void *dummy_intr_establish(uint32_t, int, int, struct cpu_info *, 33 int (*)(void *), void *, const char *); 34 void dummy_intr_send_ipi(void *); 35 void dummy_setipl(int); 36 37 /* 38 * The function pointers are overridden when the driver for the real 39 * interrupt controller attaches. 40 */ 41 void (*_exi)(struct trapframe *) = dummy_exi; 42 void (*_hvi)(struct trapframe *) = dummy_hvi; 43 void *(*_intr_establish)(uint32_t, int, int, struct cpu_info *, 44 int (*)(void *), void *, const char *) = dummy_intr_establish; 45 void (*_intr_send_ipi)(void *) = dummy_intr_send_ipi; 46 void (*_setipl)(int) = dummy_setipl; 47 48 void 49 exi_intr(struct trapframe *frame) 50 { 51 (*_exi)(frame); 52 } 53 54 void 55 hvi_intr(struct trapframe *frame) 56 { 57 (*_hvi)(frame); 58 } 59 60 void * 61 intr_establish(uint32_t girq, int type, int level, struct cpu_info *ci, 62 int (*func)(void *), void *arg, const char *name) 63 { 64 return (*_intr_establish)(girq, type, level, ci, func, arg, name); 65 } 66 67 #define SI_TO_IRQBIT(x) (1 << (x)) 68 uint32_t intr_smask[NIPL]; 69 70 void 71 intr_init(void) 72 { 73 int i; 74 75 for (i = IPL_NONE; i <= IPL_HIGH; i++) { 76 intr_smask[i] = 0; 77 if (i < IPL_SOFT) 78 intr_smask[i] |= SI_TO_IRQBIT(SIR_SOFT); 79 if (i < IPL_SOFTCLOCK) 80 intr_smask[i] |= SI_TO_IRQBIT(SIR_CLOCK); 81 if (i < IPL_SOFTNET) 82 intr_smask[i] |= SI_TO_IRQBIT(SIR_NET); 83 if (i < IPL_SOFTTTY) 84 intr_smask[i] |= SI_TO_IRQBIT(SIR_TTY); 85 } 86 } 87 88 void 89 intr_do_pending(int new) 90 { 91 struct cpu_info *ci = curcpu(); 92 u_long msr; 93 94 msr = intr_disable(); 95 96 #define DO_SOFTINT(si, ipl) \ 97 if ((ci->ci_ipending & intr_smask[new]) & SI_TO_IRQBIT(si)) { \ 98 ci->ci_ipending &= ~SI_TO_IRQBIT(si); \ 99 _setipl(ipl); \ 100 intr_restore(msr); \ 101 softintr_dispatch(si); \ 102 msr = intr_disable(); \ 103 } 104 105 do { 106 DO_SOFTINT(SIR_TTY, IPL_SOFTTTY); 107 DO_SOFTINT(SIR_NET, IPL_SOFTNET); 108 DO_SOFTINT(SIR_CLOCK, IPL_SOFTCLOCK); 109 DO_SOFTINT(SIR_SOFT, IPL_SOFT); 110 } while (ci->ci_ipending & intr_smask[new]); 111 112 intr_restore(msr); 113 } 114 115 int 116 splraise(int new) 117 { 118 struct cpu_info *ci = curcpu(); 119 int old = ci->ci_cpl; 120 121 if (new > old) 122 (*_setipl)(new); 123 return old; 124 } 125 126 int 127 spllower(int new) 128 { 129 struct cpu_info *ci = curcpu(); 130 int old = ci->ci_cpl; 131 132 if (new < old) 133 (*_setipl)(new); 134 return old; 135 } 136 137 void 138 splx(int new) 139 { 140 struct cpu_info *ci = curcpu(); 141 142 if (ci->ci_dec_deferred && new < IPL_CLOCK) { 143 mtdec(0); 144 mtdec(UINT32_MAX); /* raise DEC exception */ 145 } 146 147 if (ci->ci_ipending & intr_smask[new]) 148 intr_do_pending(new); 149 150 if (ci->ci_cpl != new) 151 (*_setipl)(new); 152 } 153 154 #ifdef DIAGNOSTIC 155 void 156 splassert_check(int wantipl, const char *func) 157 { 158 int oldipl = curcpu()->ci_cpl; 159 160 if (oldipl < wantipl) { 161 splassert_fail(wantipl, oldipl, func); 162 /* 163 * If the splassert_ctl is set to not panic, raise the ipl 164 * in a feeble attempt to reduce damage. 165 */ 166 (*_setipl)(wantipl); 167 } 168 169 if (wantipl == IPL_NONE && curcpu()->ci_idepth != 0) { 170 splassert_fail(-1, curcpu()->ci_idepth, func); 171 } 172 } 173 #endif 174 175 void 176 dummy_exi(struct trapframe *frame) 177 { 178 panic("Unhandled external interrupt"); 179 } 180 181 void 182 dummy_hvi(struct trapframe *frame) 183 { 184 panic("Unhandled hypervisor virtualization interrupt"); 185 } 186 187 void * 188 dummy_intr_establish(uint32_t girq, int type, int level, struct cpu_info *ci, 189 int (*func)(void *), void *arg, const char *name) 190 { 191 return NULL; 192 } 193 194 void 195 dummy_setipl(int new) 196 { 197 struct cpu_info *ci = curcpu(); 198 ci->ci_cpl = new; 199 } 200 201 void 202 dummy_intr_send_ipi(void *cookie) 203 { 204 } 205 206 /* 207 * FDT interrupt support. 208 */ 209 210 #define MAX_INTERRUPT_CELLS 4 211 212 struct fdt_intr_handle { 213 struct interrupt_controller *ih_ic; 214 void *ih_ih; 215 }; 216 217 LIST_HEAD(, interrupt_controller) interrupt_controllers = 218 LIST_HEAD_INITIALIZER(interrupt_controllers); 219 220 void 221 interrupt_controller_register(struct interrupt_controller *ic) 222 { 223 ic->ic_cells = OF_getpropint(ic->ic_node, "#interrupt-cells", 0); 224 ic->ic_phandle = OF_getpropint(ic->ic_node, "phandle", 0); 225 if (ic->ic_phandle == 0) 226 return; 227 KASSERT(ic->ic_cells <= MAX_INTERRUPT_CELLS); 228 229 LIST_INSERT_HEAD(&interrupt_controllers, ic, ic_list); 230 } 231 232 /* 233 * Find the interrupt parent by walking up the tree. 234 */ 235 uint32_t 236 fdt_intr_get_parent(int node) 237 { 238 uint32_t phandle = 0; 239 240 while (node && !phandle) { 241 phandle = OF_getpropint(node, "interrupt-parent", 0); 242 node = OF_parent(node); 243 } 244 245 return phandle; 246 } 247 248 void * 249 fdt_intr_establish_idx_cpu(int node, int idx, int level, struct cpu_info *ci, 250 int (*func)(void *), void *cookie, char *name) 251 { 252 struct interrupt_controller *ic; 253 int i, len, ncells, extended = 1; 254 uint32_t *cell, *cells, phandle; 255 struct fdt_intr_handle *ih; 256 void *val = NULL; 257 258 len = OF_getproplen(node, "interrupts-extended"); 259 if (len <= 0) { 260 len = OF_getproplen(node, "interrupts"); 261 extended = 0; 262 } 263 if (len <= 0 || (len % sizeof(uint32_t) != 0)) 264 return NULL; 265 266 /* Old style. */ 267 if (!extended) { 268 phandle = fdt_intr_get_parent(node); 269 LIST_FOREACH(ic, &interrupt_controllers, ic_list) { 270 if (ic->ic_phandle == phandle) 271 break; 272 } 273 274 if (ic == NULL) 275 return NULL; 276 } 277 278 cell = cells = malloc(len, M_TEMP, M_WAITOK); 279 if (extended) 280 OF_getpropintarray(node, "interrupts-extended", cells, len); 281 else 282 OF_getpropintarray(node, "interrupts", cells, len); 283 ncells = len / sizeof(uint32_t); 284 285 for (i = 0; i <= idx && ncells > 0; i++) { 286 if (extended) { 287 phandle = cell[0]; 288 289 /* Handle "empty" phandle reference. */ 290 if (phandle == 0) { 291 cell++; 292 ncells--; 293 continue; 294 } 295 296 LIST_FOREACH(ic, &interrupt_controllers, ic_list) { 297 if (ic->ic_phandle == phandle) 298 break; 299 } 300 301 if (ic == NULL) 302 break; 303 304 cell++; 305 ncells--; 306 } 307 308 if (i == idx && ncells >= ic->ic_cells && ic->ic_establish) { 309 val = ic->ic_establish(ic->ic_cookie, cell, level, 310 ci, func, cookie, name); 311 break; 312 } 313 314 cell += ic->ic_cells; 315 ncells -= ic->ic_cells; 316 } 317 318 free(cells, M_TEMP, len); 319 320 if (val == NULL) 321 return NULL; 322 323 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 324 ih->ih_ic = ic; 325 ih->ih_ih = val; 326 327 return ih; 328 } 329 330 void * 331 fdt_intr_establish_imap(int node, int *reg, int nreg, int level, 332 int (*func)(void *), void *cookie, char *name) 333 { 334 return fdt_intr_establish_imap_cpu(node, reg, nreg, level, NULL, 335 func, cookie, name); 336 } 337 338 void * 339 fdt_intr_establish_imap_cpu(int node, int *reg, int nreg, int level, 340 struct cpu_info *ci, int (*func)(void *), void *cookie, char *name) 341 { 342 struct interrupt_controller *ic; 343 struct fdt_intr_handle *ih; 344 uint32_t *cell; 345 uint32_t map_mask[4], *map; 346 int len, acells, ncells; 347 void *val = NULL; 348 349 if (nreg != sizeof(map_mask)) 350 return NULL; 351 352 if (OF_getpropintarray(node, "interrupt-map-mask", map_mask, 353 sizeof(map_mask)) != sizeof(map_mask)) 354 return NULL; 355 356 len = OF_getproplen(node, "interrupt-map"); 357 if (len <= 0) 358 return NULL; 359 360 map = malloc(len, M_DEVBUF, M_WAITOK); 361 OF_getpropintarray(node, "interrupt-map", map, len); 362 363 cell = map; 364 ncells = len / sizeof(uint32_t); 365 while (ncells > 5) { 366 LIST_FOREACH(ic, &interrupt_controllers, ic_list) { 367 if (ic->ic_phandle == cell[4]) 368 break; 369 } 370 371 if (ic == NULL) 372 break; 373 374 acells = OF_getpropint(ic->ic_node, "#address-cells", 0); 375 if (ncells >= (5 + acells + ic->ic_cells) && 376 (reg[0] & map_mask[0]) == cell[0] && 377 (reg[1] & map_mask[1]) == cell[1] && 378 (reg[2] & map_mask[2]) == cell[2] && 379 (reg[3] & map_mask[3]) == cell[3] && 380 ic->ic_establish) { 381 val = ic->ic_establish(ic->ic_cookie, &cell[5 + acells], 382 level, ci, func, cookie, name); 383 break; 384 } 385 386 cell += (5 + acells + ic->ic_cells); 387 ncells -= (5 + acells + ic->ic_cells); 388 } 389 390 if (val == NULL) { 391 free(map, M_DEVBUF, len); 392 return NULL; 393 } 394 395 ih = malloc(sizeof(*ih), M_DEVBUF, M_WAITOK); 396 ih->ih_ic = ic; 397 ih->ih_ih = val; 398 399 free(map, M_DEVBUF, len); 400 return ih; 401 } 402 403 void 404 fdt_intr_disestablish(void *cookie) 405 { 406 panic("%s: not implemented", __func__); 407 } 408 409 #ifdef MULTIPROCESSOR 410 411 void 412 intr_send_ipi(struct cpu_info *ci, int reason) 413 { 414 struct fdt_intr_handle *ih = ci->ci_ipi; 415 416 if (ci == curcpu() && reason == IPI_NOP) 417 return; 418 419 if (reason != IPI_NOP) 420 atomic_setbits_int(&ci->ci_ipi_reason, reason); 421 422 if (ih && ih->ih_ic) 423 ih->ih_ic->ic_send_ipi(ih->ih_ih); 424 } 425 426 #endif 427