1*1ebb89dfSskrll /* $NetBSD: intc_fdt.c,v 1.7 2024/12/30 15:18:11 skrll Exp $ */ 275b842b8Sskrll 375b842b8Sskrll /*- 475b842b8Sskrll * Copyright (c) 2023 The NetBSD Foundation, Inc. 575b842b8Sskrll * All rights reserved. 675b842b8Sskrll * 775b842b8Sskrll * This code is derived from software contributed to The NetBSD Foundation 875b842b8Sskrll * by Nick Hudson 975b842b8Sskrll * 1075b842b8Sskrll * Redistribution and use in source and binary forms, with or without 1175b842b8Sskrll * modification, are permitted provided that the following conditions 1275b842b8Sskrll * are met: 1375b842b8Sskrll * 1. Redistributions of source code must retain the above copyright 1475b842b8Sskrll * notice, this list of conditions and the following disclaimer. 1575b842b8Sskrll * 2. Redistributions in binary form must reproduce the above copyright 1675b842b8Sskrll * notice, this list of conditions and the following disclaimer in the 1775b842b8Sskrll * documentation and/or other materials provided with the distribution. 1875b842b8Sskrll * 1975b842b8Sskrll * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 2075b842b8Sskrll * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 2175b842b8Sskrll * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 2275b842b8Sskrll * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 2375b842b8Sskrll * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 2475b842b8Sskrll * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 2575b842b8Sskrll * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 2675b842b8Sskrll * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 2775b842b8Sskrll * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 2875b842b8Sskrll * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 2975b842b8Sskrll * POSSIBILITY OF SUCH DAMAGE. 3075b842b8Sskrll */ 3175b842b8Sskrll 3208c3a075Sskrll #include "opt_multiprocessor.h" 3308c3a075Sskrll 3475b842b8Sskrll #include <sys/cdefs.h> 35*1ebb89dfSskrll __KERNEL_RCSID(0, "$NetBSD: intc_fdt.c,v 1.7 2024/12/30 15:18:11 skrll Exp $"); 3675b842b8Sskrll 3775b842b8Sskrll #include <sys/param.h> 3875b842b8Sskrll 3975b842b8Sskrll #include <sys/bus.h> 4075b842b8Sskrll #include <sys/cpu.h> 4175b842b8Sskrll #include <sys/device.h> 4275b842b8Sskrll #include <sys/evcnt.h> 4375b842b8Sskrll #include <sys/kmem.h> 4475b842b8Sskrll #include <sys/intr.h> 4575b842b8Sskrll 4675b842b8Sskrll #include <dev/fdt/fdtvar.h> 4775b842b8Sskrll 4875b842b8Sskrll #include <machine/frame.h> 4975b842b8Sskrll #include <machine/machdep.h> 5075b842b8Sskrll #include <machine/sysreg.h> 5175b842b8Sskrll 5275b842b8Sskrll static const struct device_compatible_entry compat_data[] = { 5375b842b8Sskrll { .compat = "riscv,cpu-intc" }, 5475b842b8Sskrll DEVICE_COMPAT_EOL 5575b842b8Sskrll }; 5675b842b8Sskrll 5775b842b8Sskrll 5875b842b8Sskrll struct intc_irqhandler; 5975b842b8Sskrll struct intc_irq; 6075b842b8Sskrll 6175b842b8Sskrll typedef int (*intcih_t)(void *); 6275b842b8Sskrll 6375b842b8Sskrll struct intc_irqhandler { 6475b842b8Sskrll struct intc_irq *ih_irq; 6575b842b8Sskrll intcih_t ih_fn; 6675b842b8Sskrll void *ih_arg; 6775b842b8Sskrll TAILQ_ENTRY(intc_irqhandler) 6875b842b8Sskrll ih_next; 6975b842b8Sskrll }; 7075b842b8Sskrll 7175b842b8Sskrll struct intc_irq { 7275b842b8Sskrll struct intc_fdt_softc *intr_sc; 7375b842b8Sskrll void *intr_ih; 7475b842b8Sskrll void *intr_arg; 7575b842b8Sskrll int intr_refcnt; 7675b842b8Sskrll int intr_ipl; 7775b842b8Sskrll int intr_source; 7875b842b8Sskrll int intr_istflags; 7975b842b8Sskrll struct evcnt *pcpu_evs; 8075b842b8Sskrll TAILQ_HEAD(, intc_irqhandler) 8175b842b8Sskrll intr_handlers; 8275b842b8Sskrll }; 8375b842b8Sskrll 8475b842b8Sskrll 8575b842b8Sskrll struct intc_fdt_softc { 8675b842b8Sskrll device_t sc_dev; 8775b842b8Sskrll bus_space_tag_t sc_bst; 8875b842b8Sskrll bus_space_handle_t sc_bsh; 8975b842b8Sskrll 9075b842b8Sskrll struct intc_irq *sc_irq[IRQ_NSOURCES]; 9175b842b8Sskrll 9256ebfa49Sskrll struct evcnt sc_evs[IRQ_NSOURCES]; 9375b842b8Sskrll 9475b842b8Sskrll struct cpu_info *sc_ci; 9575b842b8Sskrll cpuid_t sc_hartid; 9675b842b8Sskrll }; 9775b842b8Sskrll 9856ebfa49Sskrll static const char * const intc_sources[IRQ_NSOURCES] = { 9956ebfa49Sskrll /* Software interrupts */ 10056ebfa49Sskrll "(reserved)", 10156ebfa49Sskrll "Supervisor software", 10256ebfa49Sskrll "Virtual Supervisor software", 10356ebfa49Sskrll "Machine software", 10456ebfa49Sskrll 10556ebfa49Sskrll /* Timer interrupts */ 10656ebfa49Sskrll "(reserved)", 10756ebfa49Sskrll "Supervisor timer", 10856ebfa49Sskrll "Virtual Supervisor timer", 10956ebfa49Sskrll "Machine timer", 11056ebfa49Sskrll 11156ebfa49Sskrll /* External interrupts */ 11256ebfa49Sskrll "(reserved)", 11356ebfa49Sskrll "Supervisor external", 11456ebfa49Sskrll "Virtual Supervisor external", 11556ebfa49Sskrll "Machine external", 11656ebfa49Sskrll 11756ebfa49Sskrll "(reserved)", 11856ebfa49Sskrll "Supervisor guest external.", 11956ebfa49Sskrll "(reserved)", 12056ebfa49Sskrll "(reserved)" 12156ebfa49Sskrll }; 12275b842b8Sskrll 123a08bd3ccSskrll #ifndef MULTIPROCESSOR 124a08bd3ccSskrll struct intc_fdt_softc *intc_sc; 125a08bd3ccSskrll #endif 126a08bd3ccSskrll 127a08bd3ccSskrll 128a08bd3ccSskrll static inline struct intc_fdt_softc * 129a08bd3ccSskrll intc_getsc(struct cpu_info *ci) 130a08bd3ccSskrll { 131a08bd3ccSskrll #ifdef MULTIPROCESSOR 132a08bd3ccSskrll return ci->ci_intcsoftc; 133a08bd3ccSskrll #else 134a08bd3ccSskrll return intc_sc; 135a08bd3ccSskrll #endif 136a08bd3ccSskrll } 13775b842b8Sskrll 13875b842b8Sskrll static void * 13975b842b8Sskrll intc_intr_establish(struct intc_fdt_softc *sc, u_int source, u_int ipl, 14075b842b8Sskrll u_int istflags, int (*func)(void *), void *arg, const char *xname) 14175b842b8Sskrll { 14275b842b8Sskrll if (source > IRQ_NSOURCES) 14375b842b8Sskrll return NULL; 14475b842b8Sskrll 14575b842b8Sskrll const device_t dev = sc->sc_dev; 14675b842b8Sskrll struct intc_irq *irq = sc->sc_irq[source]; 14775b842b8Sskrll if (irq == NULL) { 14875b842b8Sskrll irq = kmem_alloc(sizeof(*irq), KM_SLEEP); 14975b842b8Sskrll irq->intr_sc = sc; 15075b842b8Sskrll irq->intr_refcnt = 0; 15175b842b8Sskrll irq->intr_arg = arg; 15275b842b8Sskrll irq->intr_ipl = ipl; 15375b842b8Sskrll irq->intr_istflags = istflags; 15475b842b8Sskrll irq->intr_source = source; 15575b842b8Sskrll TAILQ_INIT(&irq->intr_handlers); 15675b842b8Sskrll sc->sc_irq[source] = irq; 15756ebfa49Sskrll 15856ebfa49Sskrll evcnt_attach_dynamic(&sc->sc_evs[source], EVCNT_TYPE_INTR, NULL, 15956ebfa49Sskrll device_xname(sc->sc_dev), intc_sources[source]); 16075b842b8Sskrll } else { 16175b842b8Sskrll if (irq->intr_arg == NULL || arg == NULL) { 16275b842b8Sskrll device_printf(dev, 16375b842b8Sskrll "cannot share irq with NULL-arg handler\n"); 16475b842b8Sskrll return NULL; 16575b842b8Sskrll } 16675b842b8Sskrll if (irq->intr_ipl != ipl) { 16775b842b8Sskrll device_printf(dev, 16875b842b8Sskrll "cannot share irq with different ipl\n"); 16975b842b8Sskrll return NULL; 17075b842b8Sskrll } 17175b842b8Sskrll if (irq->intr_istflags != istflags) { 17275b842b8Sskrll device_printf(dev, 17375b842b8Sskrll "cannot share irq between mpsafe/non-mpsafe\n"); 17475b842b8Sskrll return NULL; 17575b842b8Sskrll } 17675b842b8Sskrll } 17775b842b8Sskrll 17875b842b8Sskrll struct intc_irqhandler *irqh = kmem_alloc(sizeof(*irqh), KM_SLEEP); 17975b842b8Sskrll irqh->ih_irq = irq; 18075b842b8Sskrll irqh->ih_fn = func; 18175b842b8Sskrll irqh->ih_arg = arg; 18275b842b8Sskrll 18375b842b8Sskrll irq->intr_refcnt++; 18475b842b8Sskrll TAILQ_INSERT_TAIL(&irq->intr_handlers, irqh, ih_next); 18575b842b8Sskrll 18675b842b8Sskrll /* 18775b842b8Sskrll * XXX interrupt_distribute(9) assumes that any interrupt 18875b842b8Sskrll * handle can be used as an input to the MD interrupt_distribute 189*1ebb89dfSskrll * implementation, so we are forced to return the handle 19075b842b8Sskrll * we got back from intr_establish(). Upshot is that the 19175b842b8Sskrll * input to bcm2835_icu_fdt_disestablish() is ambiguous for 19275b842b8Sskrll * shared IRQs, rendering them un-disestablishable. 19375b842b8Sskrll */ 19475b842b8Sskrll 19575b842b8Sskrll return irqh; 19675b842b8Sskrll } 19775b842b8Sskrll 19875b842b8Sskrll 19975b842b8Sskrll static void * 20075b842b8Sskrll intc_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags, 20175b842b8Sskrll int (*func)(void *), void *arg, const char *xname) 20275b842b8Sskrll { 20375b842b8Sskrll struct intc_fdt_softc * const sc = device_private(dev); 20475b842b8Sskrll 20575b842b8Sskrll /* 20675b842b8Sskrll * 1st (and only) cell is the interrupt source, e.g. 20775b842b8Sskrll * 1 IRQ_SUPERVISOR_SOFTWARE 20875b842b8Sskrll * 5 IRQ_SUPERVISOR_TIMER 20975b842b8Sskrll * 9 IRQ_SUPERVISOR_EXTERNAL 21075b842b8Sskrll */ 21175b842b8Sskrll 21275b842b8Sskrll const u_int source = be32toh(specifier[0]); 21375b842b8Sskrll const u_int mpsafe = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0; 21475b842b8Sskrll 21575b842b8Sskrll return intc_intr_establish(sc, source, ipl, mpsafe, func, arg, xname); 21675b842b8Sskrll } 21775b842b8Sskrll 21875b842b8Sskrll static void 21975b842b8Sskrll intc_fdt_disestablish(device_t dev, void *ih) 22075b842b8Sskrll { 22175b842b8Sskrll #if 0 22275b842b8Sskrll struct intc_fdt_softc * const sc = device_private(dev); 22375b842b8Sskrll #endif 22475b842b8Sskrll } 22575b842b8Sskrll 22675b842b8Sskrll static bool 22775b842b8Sskrll intc_fdt_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen) 22875b842b8Sskrll { 22975b842b8Sskrll if (!specifier) 23075b842b8Sskrll return false; 23175b842b8Sskrll 23275b842b8Sskrll struct intc_fdt_softc * const sc = device_private(dev); 23375b842b8Sskrll if (sc->sc_ci == NULL) 23475b842b8Sskrll return false; 23575b842b8Sskrll 23675b842b8Sskrll const u_int source = be32toh(specifier[0]); 23775b842b8Sskrll snprintf(buf, buflen, "cpu%lu/%s #%u", sc->sc_hartid, 23875b842b8Sskrll intc_sources[source], source); 23975b842b8Sskrll 24075b842b8Sskrll return true; 24175b842b8Sskrll } 24275b842b8Sskrll 24375b842b8Sskrll 24475b842b8Sskrll struct fdtbus_interrupt_controller_func intc_fdt_funcs = { 24575b842b8Sskrll .establish = intc_fdt_establish, 24675b842b8Sskrll .disestablish = intc_fdt_disestablish, 24775b842b8Sskrll .intrstr = intc_fdt_intrstr 24875b842b8Sskrll }; 24975b842b8Sskrll 25075b842b8Sskrll 25175b842b8Sskrll static void 25275b842b8Sskrll intc_intr_handler(struct trapframe *tf, register_t epc, register_t status, 25375b842b8Sskrll register_t cause) 25475b842b8Sskrll { 25508c3a075Sskrll const int ppl = splhigh(); 25675b842b8Sskrll struct cpu_info * const ci = curcpu(); 25708c3a075Sskrll unsigned long pending; 25808c3a075Sskrll int ipl; 25975b842b8Sskrll 26075b842b8Sskrll KASSERT(CAUSE_INTERRUPT_P(cause)); 26175b842b8Sskrll 262a08bd3ccSskrll struct intc_fdt_softc * const sc = intc_getsc(ci); 26375b842b8Sskrll 26475b842b8Sskrll ci->ci_intr_depth++; 26508c3a075Sskrll ci->ci_data.cpu_nintr++; 26608c3a075Sskrll 26708c3a075Sskrll while (ppl < (ipl = splintr(&pending))) { 26808c3a075Sskrll if (pending == 0) 26908c3a075Sskrll continue; 27008c3a075Sskrll 27108c3a075Sskrll splx(ipl); 27208c3a075Sskrll 27308c3a075Sskrll int source = ffs(pending) - 1; 27475b842b8Sskrll struct intc_irq *irq = sc->sc_irq[source]; 27556ebfa49Sskrll sc->sc_evs[source].ev_count++; 27656ebfa49Sskrll 27775b842b8Sskrll KASSERTMSG(irq != NULL, "source %d\n", source); 27808c3a075Sskrll 27975b842b8Sskrll if (irq) { 28075b842b8Sskrll struct intc_irqhandler *iih; 28175b842b8Sskrll 28208c3a075Sskrll bool mpsafe = 28308c3a075Sskrll source != IRQ_SUPERVISOR_EXTERNAL || 28408c3a075Sskrll (irq->intr_istflags & IST_MPSAFE) != 0; 28575b842b8Sskrll struct clockframe cf = { 28675b842b8Sskrll .cf_epc = epc, 28775b842b8Sskrll .cf_status = status, 28875b842b8Sskrll .cf_intr_depth = ci->ci_intr_depth 28975b842b8Sskrll }; 29075b842b8Sskrll 29175b842b8Sskrll if (!mpsafe) { 29275b842b8Sskrll KERNEL_LOCK(1, NULL); 29375b842b8Sskrll } 29475b842b8Sskrll 29575b842b8Sskrll TAILQ_FOREACH(iih, &irq->intr_handlers, ih_next) { 29675b842b8Sskrll int handled = 29775b842b8Sskrll iih->ih_fn(iih->ih_arg ? iih->ih_arg : &cf); 29875b842b8Sskrll if (handled) 29975b842b8Sskrll break; 30075b842b8Sskrll } 30175b842b8Sskrll 30275b842b8Sskrll if (!mpsafe) { 30375b842b8Sskrll KERNEL_UNLOCK_ONE(NULL); 30475b842b8Sskrll } 30575b842b8Sskrll } 30608c3a075Sskrll splhigh(); 30708c3a075Sskrll } 30875b842b8Sskrll ci->ci_intr_depth--; 30908c3a075Sskrll splx(ppl); 31075b842b8Sskrll } 31175b842b8Sskrll 31275b842b8Sskrll 31375b842b8Sskrll 31475b842b8Sskrll static int 31575b842b8Sskrll intc_match(device_t parent, cfdata_t cf, void *aux) 31675b842b8Sskrll { 31775b842b8Sskrll struct fdt_attach_args * const faa = aux; 31875b842b8Sskrll return of_compatible_match(faa->faa_phandle, compat_data); 31975b842b8Sskrll } 32075b842b8Sskrll 32175b842b8Sskrll static void 32275b842b8Sskrll intc_attach(device_t parent, device_t self, void *aux) 32375b842b8Sskrll { 32475b842b8Sskrll const struct fdt_attach_args * const faa = aux; 32575b842b8Sskrll const int phandle = faa->faa_phandle; 32675b842b8Sskrll 32775b842b8Sskrll int error = fdtbus_register_interrupt_controller(self, phandle, 32875b842b8Sskrll &intc_fdt_funcs); 32975b842b8Sskrll if (error) { 33075b842b8Sskrll aprint_error(": couldn't register with fdtbus: %d\n", error); 33175b842b8Sskrll return; 33275b842b8Sskrll } 33375b842b8Sskrll 33475b842b8Sskrll struct cpu_info * const ci = device_private(parent); 33575b842b8Sskrll if (ci == NULL) { 33675b842b8Sskrll aprint_naive(": disabled\n"); 33775b842b8Sskrll aprint_normal(": disabled\n"); 33875b842b8Sskrll return; 33975b842b8Sskrll } 34075b842b8Sskrll aprint_naive("\n"); 34175b842b8Sskrll aprint_normal(": local interrupt controller\n"); 34275b842b8Sskrll 34375b842b8Sskrll struct intc_fdt_softc * const sc = device_private(self); 34475b842b8Sskrll 34575b842b8Sskrll riscv_intr_set_handler(intc_intr_handler); 34675b842b8Sskrll 34775b842b8Sskrll sc->sc_dev = self; 34875b842b8Sskrll sc->sc_ci = ci; 34975b842b8Sskrll sc->sc_hartid = ci->ci_cpuid; 35075b842b8Sskrll 35175b842b8Sskrll intc_intr_establish(sc, IRQ_SUPERVISOR_TIMER, IPL_SCHED, IST_MPSAFE, 35275b842b8Sskrll riscv_timer_intr, NULL, "clock"); 35375b842b8Sskrll #ifdef MULTIPROCESSOR 354a08bd3ccSskrll ci->ci_intcsoftc = sc; 35575b842b8Sskrll intc_intr_establish(sc, IRQ_SUPERVISOR_SOFTWARE, IPL_HIGH, IST_MPSAFE, 35675b842b8Sskrll riscv_ipi_intr, NULL, "ipi"); 357a08bd3ccSskrll #else 358a08bd3ccSskrll intc_sc = sc; 35975b842b8Sskrll #endif 36075b842b8Sskrll } 36175b842b8Sskrll 36275b842b8Sskrll CFATTACH_DECL_NEW(intc_fdt, sizeof(struct intc_fdt_softc), 36375b842b8Sskrll intc_match, intc_attach, NULL, NULL); 364