Home | History | Annotate | Line # | Download | only in fdt
      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