1 1.8 skrll /* $NetBSD: intc_fdt.c,v 1.8 2025/02/09 10:08:37 skrll Exp $ */ 2 1.1 skrll 3 1.1 skrll /*- 4 1.1 skrll * Copyright (c) 2023 The NetBSD Foundation, Inc. 5 1.1 skrll * All rights reserved. 6 1.1 skrll * 7 1.1 skrll * This code is derived from software contributed to The NetBSD Foundation 8 1.1 skrll * by Nick Hudson 9 1.1 skrll * 10 1.1 skrll * Redistribution and use in source and binary forms, with or without 11 1.1 skrll * modification, are permitted provided that the following conditions 12 1.1 skrll * are met: 13 1.1 skrll * 1. Redistributions of source code must retain the above copyright 14 1.1 skrll * notice, this list of conditions and the following disclaimer. 15 1.1 skrll * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 skrll * notice, this list of conditions and the following disclaimer in the 17 1.1 skrll * documentation and/or other materials provided with the distribution. 18 1.1 skrll * 19 1.1 skrll * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 skrll * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 skrll * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 skrll * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 skrll * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 skrll * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 skrll * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 skrll * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 skrll * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 skrll * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 skrll * POSSIBILITY OF SUCH DAMAGE. 30 1.1 skrll */ 31 1.1 skrll 32 1.2 skrll #include "opt_multiprocessor.h" 33 1.2 skrll 34 1.1 skrll #include <sys/cdefs.h> 35 1.8 skrll __KERNEL_RCSID(0, "$NetBSD: intc_fdt.c,v 1.8 2025/02/09 10:08:37 skrll Exp $"); 36 1.1 skrll 37 1.1 skrll #include <sys/param.h> 38 1.1 skrll 39 1.1 skrll #include <sys/bus.h> 40 1.1 skrll #include <sys/cpu.h> 41 1.1 skrll #include <sys/device.h> 42 1.1 skrll #include <sys/evcnt.h> 43 1.1 skrll #include <sys/kmem.h> 44 1.1 skrll #include <sys/intr.h> 45 1.1 skrll 46 1.1 skrll #include <dev/fdt/fdtvar.h> 47 1.1 skrll 48 1.1 skrll #include <machine/frame.h> 49 1.1 skrll #include <machine/machdep.h> 50 1.1 skrll #include <machine/sysreg.h> 51 1.1 skrll 52 1.1 skrll static const struct device_compatible_entry compat_data[] = { 53 1.1 skrll { .compat = "riscv,cpu-intc" }, 54 1.1 skrll DEVICE_COMPAT_EOL 55 1.1 skrll }; 56 1.1 skrll 57 1.1 skrll 58 1.1 skrll struct intc_irqhandler; 59 1.1 skrll struct intc_irq; 60 1.1 skrll 61 1.1 skrll typedef int (*intcih_t)(void *); 62 1.1 skrll 63 1.1 skrll struct intc_irqhandler { 64 1.1 skrll struct intc_irq *ih_irq; 65 1.1 skrll intcih_t ih_fn; 66 1.1 skrll void *ih_arg; 67 1.1 skrll TAILQ_ENTRY(intc_irqhandler) 68 1.1 skrll ih_next; 69 1.1 skrll }; 70 1.1 skrll 71 1.1 skrll struct intc_irq { 72 1.1 skrll struct intc_fdt_softc *intr_sc; 73 1.1 skrll void *intr_ih; 74 1.1 skrll void *intr_arg; 75 1.1 skrll int intr_refcnt; 76 1.1 skrll int intr_ipl; 77 1.1 skrll int intr_source; 78 1.1 skrll int intr_istflags; 79 1.1 skrll struct evcnt *pcpu_evs; 80 1.1 skrll TAILQ_HEAD(, intc_irqhandler) 81 1.1 skrll intr_handlers; 82 1.1 skrll }; 83 1.1 skrll 84 1.1 skrll 85 1.1 skrll struct intc_fdt_softc { 86 1.1 skrll device_t sc_dev; 87 1.1 skrll bus_space_tag_t sc_bst; 88 1.1 skrll bus_space_handle_t sc_bsh; 89 1.1 skrll 90 1.1 skrll struct intc_irq *sc_irq[IRQ_NSOURCES]; 91 1.1 skrll 92 1.3 skrll struct evcnt sc_evs[IRQ_NSOURCES]; 93 1.1 skrll 94 1.1 skrll struct cpu_info *sc_ci; 95 1.1 skrll cpuid_t sc_hartid; 96 1.1 skrll }; 97 1.1 skrll 98 1.3 skrll static const char * const intc_sources[IRQ_NSOURCES] = { 99 1.3 skrll /* Software interrupts */ 100 1.3 skrll "(reserved)", 101 1.3 skrll "Supervisor software", 102 1.3 skrll "Virtual Supervisor software", 103 1.3 skrll "Machine software", 104 1.3 skrll 105 1.3 skrll /* Timer interrupts */ 106 1.3 skrll "(reserved)", 107 1.3 skrll "Supervisor timer", 108 1.3 skrll "Virtual Supervisor timer", 109 1.3 skrll "Machine timer", 110 1.3 skrll 111 1.3 skrll /* External interrupts */ 112 1.3 skrll "(reserved)", 113 1.3 skrll "Supervisor external", 114 1.3 skrll "Virtual Supervisor external", 115 1.3 skrll "Machine external", 116 1.3 skrll 117 1.3 skrll "(reserved)", 118 1.3 skrll "Supervisor guest external.", 119 1.3 skrll "(reserved)", 120 1.3 skrll "(reserved)" 121 1.3 skrll }; 122 1.1 skrll 123 1.6 skrll #ifndef MULTIPROCESSOR 124 1.6 skrll struct intc_fdt_softc *intc_sc; 125 1.6 skrll #endif 126 1.6 skrll 127 1.6 skrll 128 1.6 skrll static inline struct intc_fdt_softc * 129 1.6 skrll intc_getsc(struct cpu_info *ci) 130 1.6 skrll { 131 1.6 skrll #ifdef MULTIPROCESSOR 132 1.6 skrll return ci->ci_intcsoftc; 133 1.6 skrll #else 134 1.6 skrll return intc_sc; 135 1.6 skrll #endif 136 1.6 skrll } 137 1.1 skrll 138 1.1 skrll static void * 139 1.1 skrll intc_intr_establish(struct intc_fdt_softc *sc, u_int source, u_int ipl, 140 1.1 skrll u_int istflags, int (*func)(void *), void *arg, const char *xname) 141 1.1 skrll { 142 1.1 skrll if (source > IRQ_NSOURCES) 143 1.1 skrll return NULL; 144 1.1 skrll 145 1.1 skrll const device_t dev = sc->sc_dev; 146 1.1 skrll struct intc_irq *irq = sc->sc_irq[source]; 147 1.1 skrll if (irq == NULL) { 148 1.1 skrll irq = kmem_alloc(sizeof(*irq), KM_SLEEP); 149 1.1 skrll irq->intr_sc = sc; 150 1.1 skrll irq->intr_refcnt = 0; 151 1.1 skrll irq->intr_arg = arg; 152 1.1 skrll irq->intr_ipl = ipl; 153 1.1 skrll irq->intr_istflags = istflags; 154 1.1 skrll irq->intr_source = source; 155 1.1 skrll TAILQ_INIT(&irq->intr_handlers); 156 1.1 skrll sc->sc_irq[source] = irq; 157 1.3 skrll 158 1.3 skrll evcnt_attach_dynamic(&sc->sc_evs[source], EVCNT_TYPE_INTR, NULL, 159 1.3 skrll device_xname(sc->sc_dev), intc_sources[source]); 160 1.1 skrll } else { 161 1.1 skrll if (irq->intr_arg == NULL || arg == NULL) { 162 1.1 skrll device_printf(dev, 163 1.1 skrll "cannot share irq with NULL-arg handler\n"); 164 1.1 skrll return NULL; 165 1.1 skrll } 166 1.1 skrll if (irq->intr_ipl != ipl) { 167 1.1 skrll device_printf(dev, 168 1.1 skrll "cannot share irq with different ipl\n"); 169 1.1 skrll return NULL; 170 1.1 skrll } 171 1.1 skrll if (irq->intr_istflags != istflags) { 172 1.1 skrll device_printf(dev, 173 1.1 skrll "cannot share irq between mpsafe/non-mpsafe\n"); 174 1.1 skrll return NULL; 175 1.1 skrll } 176 1.1 skrll } 177 1.1 skrll 178 1.1 skrll struct intc_irqhandler *irqh = kmem_alloc(sizeof(*irqh), KM_SLEEP); 179 1.1 skrll irqh->ih_irq = irq; 180 1.1 skrll irqh->ih_fn = func; 181 1.1 skrll irqh->ih_arg = arg; 182 1.1 skrll 183 1.1 skrll irq->intr_refcnt++; 184 1.1 skrll TAILQ_INSERT_TAIL(&irq->intr_handlers, irqh, ih_next); 185 1.1 skrll 186 1.1 skrll /* 187 1.1 skrll * XXX interrupt_distribute(9) assumes that any interrupt 188 1.1 skrll * handle can be used as an input to the MD interrupt_distribute 189 1.7 skrll * implementation, so we are forced to return the handle 190 1.1 skrll * we got back from intr_establish(). Upshot is that the 191 1.1 skrll * input to bcm2835_icu_fdt_disestablish() is ambiguous for 192 1.1 skrll * shared IRQs, rendering them un-disestablishable. 193 1.1 skrll */ 194 1.1 skrll 195 1.1 skrll return irqh; 196 1.1 skrll } 197 1.1 skrll 198 1.1 skrll 199 1.1 skrll static void * 200 1.1 skrll intc_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags, 201 1.1 skrll int (*func)(void *), void *arg, const char *xname) 202 1.1 skrll { 203 1.1 skrll struct intc_fdt_softc * const sc = device_private(dev); 204 1.1 skrll 205 1.1 skrll /* 206 1.1 skrll * 1st (and only) cell is the interrupt source, e.g. 207 1.1 skrll * 1 IRQ_SUPERVISOR_SOFTWARE 208 1.1 skrll * 5 IRQ_SUPERVISOR_TIMER 209 1.1 skrll * 9 IRQ_SUPERVISOR_EXTERNAL 210 1.1 skrll */ 211 1.1 skrll 212 1.1 skrll const u_int source = be32toh(specifier[0]); 213 1.1 skrll const u_int mpsafe = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0; 214 1.1 skrll 215 1.1 skrll return intc_intr_establish(sc, source, ipl, mpsafe, func, arg, xname); 216 1.1 skrll } 217 1.1 skrll 218 1.1 skrll static void 219 1.1 skrll intc_fdt_disestablish(device_t dev, void *ih) 220 1.1 skrll { 221 1.1 skrll #if 0 222 1.1 skrll struct intc_fdt_softc * const sc = device_private(dev); 223 1.1 skrll #endif 224 1.1 skrll } 225 1.1 skrll 226 1.1 skrll static bool 227 1.1 skrll intc_fdt_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen) 228 1.1 skrll { 229 1.1 skrll if (!specifier) 230 1.1 skrll return false; 231 1.1 skrll 232 1.1 skrll struct intc_fdt_softc * const sc = device_private(dev); 233 1.1 skrll if (sc->sc_ci == NULL) 234 1.1 skrll return false; 235 1.1 skrll 236 1.1 skrll const u_int source = be32toh(specifier[0]); 237 1.8 skrll snprintf(buf, buflen, "cpu%u/%s #%u", cpu_index(sc->sc_ci), 238 1.1 skrll intc_sources[source], source); 239 1.1 skrll 240 1.1 skrll return true; 241 1.1 skrll } 242 1.1 skrll 243 1.1 skrll 244 1.1 skrll struct fdtbus_interrupt_controller_func intc_fdt_funcs = { 245 1.1 skrll .establish = intc_fdt_establish, 246 1.1 skrll .disestablish = intc_fdt_disestablish, 247 1.1 skrll .intrstr = intc_fdt_intrstr 248 1.1 skrll }; 249 1.1 skrll 250 1.1 skrll 251 1.1 skrll static void 252 1.1 skrll intc_intr_handler(struct trapframe *tf, register_t epc, register_t status, 253 1.1 skrll register_t cause) 254 1.1 skrll { 255 1.2 skrll const int ppl = splhigh(); 256 1.1 skrll struct cpu_info * const ci = curcpu(); 257 1.2 skrll unsigned long pending; 258 1.2 skrll int ipl; 259 1.1 skrll 260 1.1 skrll KASSERT(CAUSE_INTERRUPT_P(cause)); 261 1.1 skrll 262 1.6 skrll struct intc_fdt_softc * const sc = intc_getsc(ci); 263 1.1 skrll 264 1.1 skrll ci->ci_intr_depth++; 265 1.2 skrll ci->ci_data.cpu_nintr++; 266 1.1 skrll 267 1.2 skrll while (ppl < (ipl = splintr(&pending))) { 268 1.2 skrll if (pending == 0) 269 1.2 skrll continue; 270 1.2 skrll 271 1.2 skrll splx(ipl); 272 1.2 skrll 273 1.2 skrll int source = ffs(pending) - 1; 274 1.2 skrll struct intc_irq *irq = sc->sc_irq[source]; 275 1.3 skrll sc->sc_evs[source].ev_count++; 276 1.3 skrll 277 1.2 skrll KASSERTMSG(irq != NULL, "source %d\n", source); 278 1.2 skrll 279 1.2 skrll if (irq) { 280 1.2 skrll struct intc_irqhandler *iih; 281 1.2 skrll 282 1.2 skrll bool mpsafe = 283 1.2 skrll source != IRQ_SUPERVISOR_EXTERNAL || 284 1.2 skrll (irq->intr_istflags & IST_MPSAFE) != 0; 285 1.2 skrll struct clockframe cf = { 286 1.2 skrll .cf_epc = epc, 287 1.2 skrll .cf_status = status, 288 1.2 skrll .cf_intr_depth = ci->ci_intr_depth 289 1.2 skrll }; 290 1.2 skrll 291 1.2 skrll if (!mpsafe) { 292 1.2 skrll KERNEL_LOCK(1, NULL); 293 1.2 skrll } 294 1.2 skrll 295 1.2 skrll TAILQ_FOREACH(iih, &irq->intr_handlers, ih_next) { 296 1.2 skrll int handled = 297 1.2 skrll iih->ih_fn(iih->ih_arg ? iih->ih_arg : &cf); 298 1.2 skrll if (handled) 299 1.2 skrll break; 300 1.2 skrll } 301 1.2 skrll 302 1.2 skrll if (!mpsafe) { 303 1.2 skrll KERNEL_UNLOCK_ONE(NULL); 304 1.2 skrll } 305 1.1 skrll } 306 1.2 skrll splhigh(); 307 1.1 skrll } 308 1.1 skrll ci->ci_intr_depth--; 309 1.2 skrll splx(ppl); 310 1.1 skrll } 311 1.1 skrll 312 1.1 skrll 313 1.1 skrll 314 1.1 skrll static int 315 1.1 skrll intc_match(device_t parent, cfdata_t cf, void *aux) 316 1.1 skrll { 317 1.1 skrll struct fdt_attach_args * const faa = aux; 318 1.1 skrll return of_compatible_match(faa->faa_phandle, compat_data); 319 1.1 skrll } 320 1.1 skrll 321 1.1 skrll static void 322 1.1 skrll intc_attach(device_t parent, device_t self, void *aux) 323 1.1 skrll { 324 1.1 skrll const struct fdt_attach_args * const faa = aux; 325 1.1 skrll const int phandle = faa->faa_phandle; 326 1.1 skrll 327 1.1 skrll int error = fdtbus_register_interrupt_controller(self, phandle, 328 1.4 skrll &intc_fdt_funcs); 329 1.1 skrll if (error) { 330 1.1 skrll aprint_error(": couldn't register with fdtbus: %d\n", error); 331 1.1 skrll return; 332 1.1 skrll } 333 1.1 skrll 334 1.1 skrll struct cpu_info * const ci = device_private(parent); 335 1.1 skrll if (ci == NULL) { 336 1.1 skrll aprint_naive(": disabled\n"); 337 1.1 skrll aprint_normal(": disabled\n"); 338 1.1 skrll return; 339 1.1 skrll } 340 1.1 skrll aprint_naive("\n"); 341 1.1 skrll aprint_normal(": local interrupt controller\n"); 342 1.1 skrll 343 1.1 skrll struct intc_fdt_softc * const sc = device_private(self); 344 1.1 skrll 345 1.1 skrll riscv_intr_set_handler(intc_intr_handler); 346 1.1 skrll 347 1.1 skrll sc->sc_dev = self; 348 1.1 skrll sc->sc_ci = ci; 349 1.1 skrll sc->sc_hartid = ci->ci_cpuid; 350 1.1 skrll 351 1.1 skrll intc_intr_establish(sc, IRQ_SUPERVISOR_TIMER, IPL_SCHED, IST_MPSAFE, 352 1.1 skrll riscv_timer_intr, NULL, "clock"); 353 1.1 skrll #ifdef MULTIPROCESSOR 354 1.6 skrll ci->ci_intcsoftc = sc; 355 1.1 skrll intc_intr_establish(sc, IRQ_SUPERVISOR_SOFTWARE, IPL_HIGH, IST_MPSAFE, 356 1.1 skrll riscv_ipi_intr, NULL, "ipi"); 357 1.6 skrll #else 358 1.6 skrll intc_sc = sc; 359 1.1 skrll #endif 360 1.1 skrll } 361 1.1 skrll 362 1.1 skrll CFATTACH_DECL_NEW(intc_fdt, sizeof(struct intc_fdt_softc), 363 1.1 skrll intc_match, intc_attach, NULL, NULL); 364