xref: /netbsd-src/sys/arch/riscv/fdt/intc_fdt.c (revision 1ebb89df375d8343993a5f12083a05637e9a9234)
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