xref: /freebsd/sys/riscv/riscv/sbi_ipi.c (revision 6ec8bf9f)
16ec8bf9fSJessica Clarke /*-
26ec8bf9fSJessica Clarke  * SPDX-License-Identifier: BSD-2-Clause
36ec8bf9fSJessica Clarke  *
46ec8bf9fSJessica Clarke  * Copyright (c) 2021 Jessica Clarke <jrtc27@FreeBSD.org>
56ec8bf9fSJessica Clarke  *
66ec8bf9fSJessica Clarke  * Redistribution and use in source and binary forms, with or without
76ec8bf9fSJessica Clarke  * modification, are permitted provided that the following conditions
86ec8bf9fSJessica Clarke  * are met:
96ec8bf9fSJessica Clarke  * 1. Redistributions of source code must retain the above copyright
106ec8bf9fSJessica Clarke  *    notice, this list of conditions and the following disclaimer.
116ec8bf9fSJessica Clarke  * 2. Redistributions in binary form must reproduce the above copyright
126ec8bf9fSJessica Clarke  *    notice, this list of conditions and the following disclaimer in the
136ec8bf9fSJessica Clarke  *    documentation and/or other materials provided with the distribution.
146ec8bf9fSJessica Clarke  *
156ec8bf9fSJessica Clarke  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
166ec8bf9fSJessica Clarke  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
176ec8bf9fSJessica Clarke  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
186ec8bf9fSJessica Clarke  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
196ec8bf9fSJessica Clarke  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
206ec8bf9fSJessica Clarke  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
216ec8bf9fSJessica Clarke  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
226ec8bf9fSJessica Clarke  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
236ec8bf9fSJessica Clarke  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
246ec8bf9fSJessica Clarke  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
256ec8bf9fSJessica Clarke  * SUCH DAMAGE.
266ec8bf9fSJessica Clarke  */
276ec8bf9fSJessica Clarke 
286ec8bf9fSJessica Clarke #include <sys/param.h>
296ec8bf9fSJessica Clarke #include <sys/systm.h>
306ec8bf9fSJessica Clarke #include <sys/bus.h>
316ec8bf9fSJessica Clarke #include <sys/cpuset.h>
326ec8bf9fSJessica Clarke #include <sys/kernel.h>
336ec8bf9fSJessica Clarke #include <sys/module.h>
346ec8bf9fSJessica Clarke #include <sys/rman.h>
356ec8bf9fSJessica Clarke 
366ec8bf9fSJessica Clarke #include <machine/intr.h>
376ec8bf9fSJessica Clarke #include <machine/sbi.h>
386ec8bf9fSJessica Clarke #include <machine/smp.h>
396ec8bf9fSJessica Clarke 
406ec8bf9fSJessica Clarke #include <dev/ofw/openfirm.h>
416ec8bf9fSJessica Clarke #include <dev/ofw/ofw_bus.h>
426ec8bf9fSJessica Clarke #include <dev/ofw/ofw_bus_subr.h>
436ec8bf9fSJessica Clarke 
446ec8bf9fSJessica Clarke #include "pic_if.h"
456ec8bf9fSJessica Clarke 
466ec8bf9fSJessica Clarke struct sbi_ipi_softc {
476ec8bf9fSJessica Clarke 	device_t		dev;
486ec8bf9fSJessica Clarke 	struct resource		*irq_res;
496ec8bf9fSJessica Clarke 	void			*ih;
506ec8bf9fSJessica Clarke 	struct intr_irqsrc	isrc;
516ec8bf9fSJessica Clarke 	uint32_t		pending_ipis[MAXCPU];
526ec8bf9fSJessica Clarke };
536ec8bf9fSJessica Clarke 
546ec8bf9fSJessica Clarke static void
sbi_ipi_pic_ipi_send(device_t dev,struct intr_irqsrc * isrc,cpuset_t cpus,u_int ipi)556ec8bf9fSJessica Clarke sbi_ipi_pic_ipi_send(device_t dev, struct intr_irqsrc *isrc, cpuset_t cpus,
566ec8bf9fSJessica Clarke     u_int ipi)
576ec8bf9fSJessica Clarke {
586ec8bf9fSJessica Clarke 	struct sbi_ipi_softc *sc;
596ec8bf9fSJessica Clarke 	struct pcpu *pc;
606ec8bf9fSJessica Clarke 	u_long mask;
616ec8bf9fSJessica Clarke 	u_int cpu;
626ec8bf9fSJessica Clarke 
636ec8bf9fSJessica Clarke 	sc = device_get_softc(dev);
646ec8bf9fSJessica Clarke 
656ec8bf9fSJessica Clarke 	KASSERT(isrc == &sc->isrc, ("%s: not the IPI isrc", __func__));
666ec8bf9fSJessica Clarke 	KASSERT(ipi < INTR_IPI_COUNT,
676ec8bf9fSJessica Clarke 	    ("%s: not a valid IPI: %u", __func__, ipi));
686ec8bf9fSJessica Clarke 
696ec8bf9fSJessica Clarke 	mask = 0;
706ec8bf9fSJessica Clarke 	STAILQ_FOREACH(pc, &cpuhead, pc_allcpu) {
716ec8bf9fSJessica Clarke 		cpu = pc->pc_cpuid;
726ec8bf9fSJessica Clarke 		if (CPU_ISSET(cpu, &cpus)) {
736ec8bf9fSJessica Clarke 			atomic_set_32(&sc->pending_ipis[cpu], 1u << ipi);
746ec8bf9fSJessica Clarke 			mask |= (1ul << pc->pc_hart);
756ec8bf9fSJessica Clarke 		}
766ec8bf9fSJessica Clarke 	}
776ec8bf9fSJessica Clarke 	sbi_send_ipi(&mask);
786ec8bf9fSJessica Clarke }
796ec8bf9fSJessica Clarke 
806ec8bf9fSJessica Clarke static int
sbi_ipi_pic_ipi_setup(device_t dev,u_int ipi,struct intr_irqsrc ** isrcp)816ec8bf9fSJessica Clarke sbi_ipi_pic_ipi_setup(device_t dev, u_int ipi, struct intr_irqsrc **isrcp)
826ec8bf9fSJessica Clarke {
836ec8bf9fSJessica Clarke 	struct sbi_ipi_softc *sc;
846ec8bf9fSJessica Clarke 
856ec8bf9fSJessica Clarke 	sc = device_get_softc(dev);
866ec8bf9fSJessica Clarke 
876ec8bf9fSJessica Clarke 	KASSERT(ipi < INTR_IPI_COUNT,
886ec8bf9fSJessica Clarke 	    ("%s: not a valid IPI: %u", __func__, ipi));
896ec8bf9fSJessica Clarke 
906ec8bf9fSJessica Clarke 	*isrcp = &sc->isrc;
916ec8bf9fSJessica Clarke 
926ec8bf9fSJessica Clarke 	return (0);
936ec8bf9fSJessica Clarke 
946ec8bf9fSJessica Clarke }
956ec8bf9fSJessica Clarke 
966ec8bf9fSJessica Clarke static int
sbi_ipi_intr(void * arg)976ec8bf9fSJessica Clarke sbi_ipi_intr(void *arg)
986ec8bf9fSJessica Clarke {
996ec8bf9fSJessica Clarke 	struct sbi_ipi_softc *sc;
1006ec8bf9fSJessica Clarke 	uint32_t ipi_bitmap;
1016ec8bf9fSJessica Clarke 	u_int cpu, ipi;
1026ec8bf9fSJessica Clarke 	int bit;
1036ec8bf9fSJessica Clarke 
1046ec8bf9fSJessica Clarke 	sc = arg;
1056ec8bf9fSJessica Clarke 
1066ec8bf9fSJessica Clarke 	csr_clear(sip, SIP_SSIP);
1076ec8bf9fSJessica Clarke 
1086ec8bf9fSJessica Clarke 	cpu = PCPU_GET(cpuid);
1096ec8bf9fSJessica Clarke 
1106ec8bf9fSJessica Clarke 	mb();
1116ec8bf9fSJessica Clarke 
1126ec8bf9fSJessica Clarke 	ipi_bitmap = atomic_readandclear_32(&sc->pending_ipis[cpu]);
1136ec8bf9fSJessica Clarke 	if (ipi_bitmap == 0)
1146ec8bf9fSJessica Clarke 		return (FILTER_HANDLED);
1156ec8bf9fSJessica Clarke 
1166ec8bf9fSJessica Clarke 	mb();
1176ec8bf9fSJessica Clarke 
1186ec8bf9fSJessica Clarke 	while ((bit = ffs(ipi_bitmap))) {
1196ec8bf9fSJessica Clarke 		ipi = (bit - 1);
1206ec8bf9fSJessica Clarke 		ipi_bitmap &= ~(1u << ipi);
1216ec8bf9fSJessica Clarke 
1226ec8bf9fSJessica Clarke 		intr_ipi_dispatch(ipi);
1236ec8bf9fSJessica Clarke 	}
1246ec8bf9fSJessica Clarke 
1256ec8bf9fSJessica Clarke 	return (FILTER_HANDLED);
1266ec8bf9fSJessica Clarke }
1276ec8bf9fSJessica Clarke 
1286ec8bf9fSJessica Clarke static int
sbi_ipi_probe(device_t dev)1296ec8bf9fSJessica Clarke sbi_ipi_probe(device_t dev)
1306ec8bf9fSJessica Clarke {
1316ec8bf9fSJessica Clarke 	device_set_desc(dev, "RISC-V SBI Inter-Processor Interrupts");
1326ec8bf9fSJessica Clarke 
1336ec8bf9fSJessica Clarke 	return (BUS_PROBE_NOWILDCARD);
1346ec8bf9fSJessica Clarke }
1356ec8bf9fSJessica Clarke 
1366ec8bf9fSJessica Clarke static int
sbi_ipi_attach(device_t dev)1376ec8bf9fSJessica Clarke sbi_ipi_attach(device_t dev)
1386ec8bf9fSJessica Clarke {
1396ec8bf9fSJessica Clarke 	struct sbi_ipi_softc *sc;
1406ec8bf9fSJessica Clarke 	const char *name;
1416ec8bf9fSJessica Clarke 	int irq, rid, error;
1426ec8bf9fSJessica Clarke 	phandle_t iparent;
1436ec8bf9fSJessica Clarke 	pcell_t cell;
1446ec8bf9fSJessica Clarke 
1456ec8bf9fSJessica Clarke 	sc = device_get_softc(dev);
1466ec8bf9fSJessica Clarke 	sc->dev = dev;
1476ec8bf9fSJessica Clarke 
1486ec8bf9fSJessica Clarke 	memset(sc->pending_ipis, 0, sizeof(sc->pending_ipis));
1496ec8bf9fSJessica Clarke 
1506ec8bf9fSJessica Clarke 	name = device_get_nameunit(dev);
1516ec8bf9fSJessica Clarke 	error = intr_isrc_register(&sc->isrc, sc->dev, INTR_ISRCF_IPI,
1526ec8bf9fSJessica Clarke 	    "%s,ipi", name);
1536ec8bf9fSJessica Clarke 	if (error != 0) {
1546ec8bf9fSJessica Clarke 		device_printf(dev, "Can't register interrupt: %d\n", error);
1556ec8bf9fSJessica Clarke 		return (ENXIO);
1566ec8bf9fSJessica Clarke 	}
1576ec8bf9fSJessica Clarke 
1586ec8bf9fSJessica Clarke 	iparent = OF_xref_from_node(ofw_bus_get_node(intr_irq_root_dev));
1596ec8bf9fSJessica Clarke 	cell = IRQ_SOFTWARE_SUPERVISOR;
1606ec8bf9fSJessica Clarke 	irq = ofw_bus_map_intr(dev, iparent, 1, &cell);
1616ec8bf9fSJessica Clarke 	error = bus_set_resource(dev, SYS_RES_IRQ, 0, irq, 1);
1626ec8bf9fSJessica Clarke 	if (error != 0) {
1636ec8bf9fSJessica Clarke 		device_printf(dev, "Unable to register IRQ resource\n");
1646ec8bf9fSJessica Clarke 		return (ENXIO);
1656ec8bf9fSJessica Clarke 	}
1666ec8bf9fSJessica Clarke 
1676ec8bf9fSJessica Clarke 	rid = 0;
1686ec8bf9fSJessica Clarke 	sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
1696ec8bf9fSJessica Clarke 	    RF_ACTIVE | RF_SHAREABLE);
1706ec8bf9fSJessica Clarke 	if (sc->irq_res == NULL) {
1716ec8bf9fSJessica Clarke 		device_printf(dev, "Unable to alloc IRQ resource\n");
1726ec8bf9fSJessica Clarke 		return (ENXIO);
1736ec8bf9fSJessica Clarke 	}
1746ec8bf9fSJessica Clarke 
1756ec8bf9fSJessica Clarke 	error = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_CLK,
1766ec8bf9fSJessica Clarke 	    sbi_ipi_intr, NULL, sc, &sc->ih);
1776ec8bf9fSJessica Clarke 	if (error != 0) {
1786ec8bf9fSJessica Clarke 		device_printf(dev, "Unable to setup IRQ resource\n");
1796ec8bf9fSJessica Clarke 		return (ENXIO);
1806ec8bf9fSJessica Clarke 	}
1816ec8bf9fSJessica Clarke 
1826ec8bf9fSJessica Clarke 	/* TODO: Define a set of priorities once other IPI sources exist */
1836ec8bf9fSJessica Clarke 	error = intr_ipi_pic_register(dev, 0);
1846ec8bf9fSJessica Clarke 	if (error != 0) {
1856ec8bf9fSJessica Clarke 		device_printf(dev, "Can't register as IPI source: %d\n", error);
1866ec8bf9fSJessica Clarke 		return (ENXIO);
1876ec8bf9fSJessica Clarke 	}
1886ec8bf9fSJessica Clarke 
1896ec8bf9fSJessica Clarke 	return (0);
1906ec8bf9fSJessica Clarke }
1916ec8bf9fSJessica Clarke 
1926ec8bf9fSJessica Clarke static device_method_t sbi_ipi_methods[] = {
1936ec8bf9fSJessica Clarke 	/* Device interface */
1946ec8bf9fSJessica Clarke 	DEVMETHOD(device_probe,		sbi_ipi_probe),
1956ec8bf9fSJessica Clarke 	DEVMETHOD(device_attach,	sbi_ipi_attach),
1966ec8bf9fSJessica Clarke 
1976ec8bf9fSJessica Clarke 	/* Interrupt controller interface */
1986ec8bf9fSJessica Clarke 	DEVMETHOD(pic_ipi_send,		sbi_ipi_pic_ipi_send),
1996ec8bf9fSJessica Clarke 	DEVMETHOD(pic_ipi_setup,	sbi_ipi_pic_ipi_setup),
2006ec8bf9fSJessica Clarke 
2016ec8bf9fSJessica Clarke 	DEVMETHOD_END
2026ec8bf9fSJessica Clarke };
2036ec8bf9fSJessica Clarke 
2046ec8bf9fSJessica Clarke DEFINE_CLASS_0(sbi_ipi, sbi_ipi_driver, sbi_ipi_methods,
2056ec8bf9fSJessica Clarke     sizeof(struct sbi_ipi_softc));
2066ec8bf9fSJessica Clarke /* Local interrupt controller attaches during BUS_PASS_ORDER_FIRST */
2076ec8bf9fSJessica Clarke EARLY_DRIVER_MODULE(sbi_ipi, sbi, sbi_ipi_driver, 0, 0,
2086ec8bf9fSJessica Clarke     BUS_PASS_INTERRUPT + BUS_PASS_ORDER_EARLY);
209