From nobody Thu Jun 18 14:57:42 2026 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4gh3jz1tJ1z6hM53 for ; Thu, 18 Jun 2026 14:57:43 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4gh3jz12kwz43vt for ; Thu, 18 Jun 2026 14:57:43 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781794663; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Zr6+PSTW73FkLfYW/UvWx22o2RA2RiOlBkiad9+aM9Q=; b=bzSUxqvfGmZNmFLF23hOxnPr90dr5d3ip2ikDgcx9su4VLDc82UzAoTAE7uoVfTN5uQMz5 xOHx/lfuS1J0ZMJ9FX6jUvRQsQ4MFXjqdt+QQiK9AdPSOkNp5A0eU0c9OhueoRVmzs24MV EuAEOb5ihqZ1yhZXLs+zqB5P4iXJMOn6XLbIG6w2e3l0lDBchmoqWm9pEA6jpgG0D4YfPW +8xihIyo59Qk0aPVTlD1FsZRo7RjeCLTZcKPUQnK3TzTaN8on3oHSDlxWL/O4S4DSs/ZWN 2NMOrxD8pi1rEF7pq5JGTf57+kv+BRg6is/E1HzTxzqsV++pXlDsoe/VRyF4JQ== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1781794663; a=rsa-sha256; cv=none; b=rmxkU1k/ufUAn25B45NizsLdONdb2uXmochKjNIzMUtwzHSJEYogb+fXd454tTLCV+12ny DPnX2d3lRswUwF7HD77IsQe2CqcJ4jdVNXR4ZuND/QyBIHiSTSi7orhNHT5hhu4YbTh/jH JHjpDbmzljqrEGNbJcDlVhymdJ2uNqYT7gicgUTDAEufoQr7/C1KiMfKSnOIO05dFKezec YB3F5Fpzljn6LK7WaIhU5QxDac0HMPTPx0kPYedYtZ4UAPGEOAMlRpON3N2s2ux6X2B/6k aoXultlrCneUfQKzevnWHj7H/ezuuHAU5n3leK7EppQ07PpVPrIqWTtBe07l1g== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781794663; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=Zr6+PSTW73FkLfYW/UvWx22o2RA2RiOlBkiad9+aM9Q=; b=kmjQ/JtQ7sVImwqPaE5keJgGPrnjFXtowNaTcz/oTCvsq+yEa5g0pRWEbzfnRRPMmj7cOX xnxNwQipZFxaNBnm8kTf0rF5IwfuHoVb2VAnm+YrKjURqkhySs7HvyduHFYQIe9puorIbC tsgh15nK2NiOxA/8mGBFiVFQ9W8Oi6ewny0FmHMN6tk3xYIqqvISlFExbZqwZWToROR+D5 QNUbsslTcrD383htszyKjDHWfaTt0Y0kORLZPBXdtjHGNFYtdIYqEnbgBwGmjNqWGHh/f4 x0WCmIBaCvJ6LbORrNvqOBehVq5/+SEHTNj3W028Qe2OlhQ2rJCMzHaTPORgFQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4gh3jy70hZz138S for ; Thu, 18 Jun 2026 14:57:42 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3f2ed by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 18 Jun 2026 14:57:42 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Andrew Turner Subject: git: cbef29bde7ce - main - arm64: Add an initial GICv5 IWB driver List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: andrew X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: cbef29bde7ce8f2bac4bff831a21e9e54ae51d20 Auto-Submitted: auto-generated Date: Thu, 18 Jun 2026 14:57:42 +0000 Message-Id: <6a340766.3f2ed.3bf209d0@gitrepo.freebsd.org> The branch main has been updated by andrew: URL: https://cgit.FreeBSD.org/src/commit/?id=cbef29bde7ce8f2bac4bff831a21e9e54ae51d20 commit cbef29bde7ce8f2bac4bff831a21e9e54ae51d20 Author: Andrew Turner AuthorDate: 2026-06-18 13:52:18 +0000 Commit: Andrew Turner CommitDate: 2026-06-18 14:56:53 +0000 arm64: Add an initial GICv5 IWB driver Add a driver to support the GICv5 interrupt wire bridge (IWB). The IWB translates the change in state of an input wire and sends a MSI to the interrupt translation service (ITS) to be translated to an LPI. Unlike other MSI sources each wire has a fixed Event ID value it will write in the MSI data. Sponsored by: Arm Ltd Differential Revision: https://reviews.freebsd.org/D54252 --- sys/arm64/arm64/gicv5_iwb.c | 561 ++++++++++++++++++++++++++++++++++++++++++++ sys/conf/files.arm64 | 1 + 2 files changed, 562 insertions(+) diff --git a/sys/arm64/arm64/gicv5_iwb.c b/sys/arm64/arm64/gicv5_iwb.c new file mode 100644 index 000000000000..fa2ad370e626 --- /dev/null +++ b/sys/arm64/arm64/gicv5_iwb.c @@ -0,0 +1,561 @@ +/*- + * Copyright (c) 2025 Arm Ltd + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "opt_acpi.h" +#include "opt_platform.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef FDT +#include +#include +#include +#include +#endif + +#include "pci_if.h" +#include "pic_if.h" + +#define IWB_IDR0 0x0000 +#define IDR0_IW_RANGE_SHIFT 0 +#define IDR0_IW_RANGE_MASK (0x7ffu << IDR0_IW_RANGE_SHIFT) +#define IDR0_IW_RANGE_IRQs(x) \ + ((((x) & IDR0_IW_RANGE_MASK) + 1) * 32) +#define IWB_IIDR 0x0040 +#define IWB_AIDR 0x0044 +#define IWB_CR0 0x0080 +#define CR0_IWBEN (0x1u << 0) +#define IWB_WENABLE_STATUSR 0x00C0 +#define WENABLE_STATUSR_IDLE (0x1u << 0) +#define IWB_WRESAMPLER 0x00C8 +#define IWB_WENABLER(irq) (0x2000 + (4 * ((irq) / 32))) +#define WENABLER_MASK(irq) (0x1u << ((irq) % 32)) +#define WENABLER_ENABLED(irq) (0x1u << ((irq) % 32)) +#define IWB_WTMR(irq) (0x4000 + (4 * ((irq) / 32))) +#define WTMR_MASK(irq) (0x1u << ((irq) % 32)) +#define WTMR_LEVEL(irq) (0x1u << ((irq) % 32)) + +struct gicv5_iwb_softc; + +struct gicv5_iwb_irqsrc { + struct intr_irqsrc gi_isrc; + uint32_t gi_irq; + enum intr_polarity gi_pol; + enum intr_trigger gi_trig; + struct resource *gi_res; /* Parent MSI interrupt resource */ + void *gi_cookie; + struct gicv5_iwb_softc *gi_sc; + int gi_rid; +}; + +struct gicv5_iwb_softc { + struct mtx sc_mtx; + device_t sc_dev; + struct resource *sc_mem; + struct intr_pic *sc_pic; + struct gicv5_iwb_irqsrc *sc_irqs; + int sc_mem_rid; + u_int sc_nirq; +}; + +static device_attach_t gicv5_iwb_attach; + +static pic_disable_intr_t gicv5_iwb_disable_intr; +static pic_enable_intr_t gicv5_iwb_enable_intr; +static pic_map_intr_t gicv5_iwb_map_intr; +static pic_setup_intr_t gicv5_iwb_setup_intr; +static pic_teardown_intr_t gicv5_iwb_teardown_intr; +static pic_post_filter_t gicv5_iwb_post_filter; +static pic_post_ithread_t gicv5_iwb_post_ithread; +static pic_pre_ithread_t gicv5_iwb_pre_ithread; + +static device_method_t gicv5_iwb_methods[] = { + /* Bus interface */ + DEVMETHOD(device_attach, gicv5_iwb_attach), + + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, gicv5_iwb_disable_intr), + DEVMETHOD(pic_enable_intr, gicv5_iwb_enable_intr), + DEVMETHOD(pic_map_intr, gicv5_iwb_map_intr), + DEVMETHOD(pic_setup_intr, gicv5_iwb_setup_intr), + DEVMETHOD(pic_teardown_intr, gicv5_iwb_teardown_intr), + DEVMETHOD(pic_post_filter, gicv5_iwb_post_filter), + DEVMETHOD(pic_post_ithread, gicv5_iwb_post_ithread), + DEVMETHOD(pic_pre_ithread, gicv5_iwb_pre_ithread), + + /* End */ + DEVMETHOD_END +}; + +static DEFINE_CLASS_0(iwb, gicv5_iwb_driver, gicv5_iwb_methods, + sizeof(struct gicv5_iwb_softc)); + +static void +gicv5_iwb_wait_for_wenabler(struct gicv5_iwb_softc *sc) +{ + uint32_t reg; + int timeout; + + mtx_assert(&sc->sc_mtx, MA_OWNED); + + /* Timeout of ~10ms */ + timeout = 10000; + do { + reg = bus_read_4(sc->sc_mem, IWB_WENABLE_STATUSR); + if ((reg & WENABLE_STATUSR_IDLE) != 0) + return; + DELAY(1); + } while (--timeout > 0); + + device_printf(sc->sc_dev, "IWB_WENABLE_STATUSR timeout\n"); +} + +static int +gicv5_iwb_intr(void *arg) +{ + struct gicv5_iwb_irqsrc *gi = arg; + struct trapframe *tf; + + tf = curthread->td_intr_frame; + if (intr_isrc_dispatch(&gi->gi_isrc, tf) != 0) { + device_t dev; + + dev = gi->gi_sc->sc_dev; + gicv5_iwb_disable_intr(dev, &gi->gi_isrc); + device_printf(dev, "Stray irq %u disabled\n", + gi->gi_irq); + } + + return (FILTER_HANDLED); +} + +static int +gicv5_iwb_attach(device_t dev) +{ + struct gicv5_iwb_softc *sc; + const char *name; + uint32_t cr0; + int count, error; + u_int i; + + sc = device_get_softc(dev); + sc->sc_dev = dev; + + mtx_init(&sc->sc_mtx, "GICv5 IWB lock", NULL, MTX_SPIN); + + sc->sc_mem_rid = 0; + sc->sc_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->sc_mem_rid, RF_ACTIVE); + if (sc->sc_mem == NULL) + return (ENXIO); + + cr0 = bus_read_4(sc->sc_mem, IWB_CR0); + if ((cr0 & CR0_IWBEN) == 0) { + device_printf(dev, "IWB not enabled in firmware: %x\n", cr0); + bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_mem_rid, + sc->sc_mem); + return (ENXIO); + } + + sc->sc_nirq = IDR0_IW_RANGE_IRQs(bus_read_4(sc->sc_mem, IWB_IDR0)); + if (bootverbose) + device_printf(dev, "Found %u irqs\n", sc->sc_nirq); + + /* Disable all interrupts */ + for (int i = 0; i < sc->sc_nirq; i += 32) + bus_write_4(sc->sc_mem, IWB_WENABLER(i), 0); + mtx_lock_spin(&sc->sc_mtx); + gicv5_iwb_wait_for_wenabler(sc); + mtx_unlock_spin(&sc->sc_mtx); + + /* Allocate an MSI for each interrupt */ + count = sc->sc_nirq; + error = PCI_ALLOC_MSI(device_get_parent(dev), dev, &count); + if (error != 0) { + device_printf(dev, "Unable to allocate MSI interrupts\n"); + return (error); + } + + name = device_get_nameunit(dev); + sc->sc_irqs = mallocarray(sc->sc_nirq, sizeof(struct gicv5_iwb_irqsrc), + M_DEVBUF, M_ZERO | M_WAITOK); + for (i = 0; i < sc->sc_nirq; i++) { + sc->sc_irqs[i].gi_irq = i; + sc->sc_irqs[i].gi_sc = sc; + + sc->sc_irqs[i].gi_rid = i + 1; + sc->sc_irqs[i].gi_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, + &sc->sc_irqs[i].gi_rid, RF_ACTIVE); + if (sc->sc_irqs[i].gi_res == NULL) { + device_printf(dev, + "Unable to allocate MSI for wire %d\n", i); + error = ENXIO; + goto exit; + } + + error = bus_setup_intr(dev, sc->sc_irqs[i].gi_res, + INTR_TYPE_MISC | INTR_MPSAFE, gicv5_iwb_intr, NULL, + &sc->sc_irqs[i], &sc->sc_irqs[i].gi_cookie); + if (error != 0) { + device_printf(dev, "Unable to setup MSI for wire %d\n", + i); + goto exit_setup_intr; + } + + error = intr_isrc_register(&sc->sc_irqs[i].gi_isrc, dev, + 0, "%s,%u", name, i); + if (error != 0) { + device_printf(dev, "Unable to register wire %d\n", i); + goto exit_isrc; + } + } + + return (0); + +exit_isrc: + bus_teardown_intr(dev, sc->sc_irqs[i].gi_res, sc->sc_irqs[i].gi_cookie); +exit_setup_intr: + bus_release_resource(dev, SYS_RES_IRQ, + sc->sc_irqs[i].gi_rid, sc->sc_irqs[i].gi_res); +exit: + sc->sc_irqs[i].gi_res = NULL; + + for (u_int j = 0; j < i; j++) { + MPASS(sc->sc_irqs[j].gi_res != NULL); + + intr_isrc_deregister(&sc->sc_irqs[j].gi_isrc); + if (sc->sc_irqs[j].gi_cookie != NULL) + bus_teardown_intr(dev, sc->sc_irqs[j].gi_res, + sc->sc_irqs[j].gi_cookie); + bus_release_resource(dev, SYS_RES_IRQ, + sc->sc_irqs[j].gi_rid, sc->sc_irqs[j].gi_res); + } + + /* TODO: Implement in buses this could attach to */ + /*PCI_RELEASE_MSI(device_get_parent(dev), dev, count);*/ + + return (error); +} + +static void +gicv5_iwb_disable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct gicv5_iwb_irqsrc *gi = (struct gicv5_iwb_irqsrc *)isrc; + struct gicv5_iwb_softc *sc; + uint32_t reg; + u_int irq; + + sc = device_get_softc(dev); + irq = gi->gi_irq; + + mtx_lock_spin(&sc->sc_mtx); + reg = bus_read_4(sc->sc_mem, IWB_WENABLER(irq)); + reg &= ~WENABLER_ENABLED(irq); + bus_write_4(sc->sc_mem, IWB_WENABLER(irq), reg); + + gicv5_iwb_wait_for_wenabler(sc); + mtx_unlock_spin(&sc->sc_mtx); +} + +static void +gicv5_iwb_enable_intr(device_t dev, struct intr_irqsrc *isrc) +{ + struct gicv5_iwb_irqsrc *gi = (struct gicv5_iwb_irqsrc *)isrc; + struct gicv5_iwb_softc *sc; + uint32_t reg; + u_int irq; + + sc = device_get_softc(dev); + irq = gi->gi_irq; + + mtx_lock_spin(&sc->sc_mtx); + reg = bus_read_4(sc->sc_mem, IWB_WENABLER(irq)); + reg |= WENABLER_ENABLED(irq); + bus_write_4(sc->sc_mem, IWB_WENABLER(irq), reg); + + gicv5_iwb_wait_for_wenabler(sc); + mtx_unlock_spin(&sc->sc_mtx); +} + +#ifdef FDT +static int +gicv5_iwb_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp, + enum intr_polarity *polp, enum intr_trigger *trigp) +{ + if (ncells < 2) + return (EINVAL); + + /* + * The 1st cell contains the interrupt number + * The 2nd cell is the flags, encoded as follows: + * bits[3:0] trigger type and level flags + */ + *irqp = cells[0]; + + switch (cells[1] & FDT_INTR_MASK) { + case FDT_INTR_EDGE_RISING: + *trigp = INTR_TRIGGER_EDGE; + *polp = INTR_POLARITY_HIGH; + break; + case FDT_INTR_EDGE_FALLING: + *trigp = INTR_TRIGGER_EDGE; + *polp = INTR_POLARITY_LOW; + break; + case FDT_INTR_LEVEL_HIGH: + *trigp = INTR_TRIGGER_LEVEL; + *polp = INTR_POLARITY_HIGH; + break; + case FDT_INTR_LEVEL_LOW: + *trigp = INTR_TRIGGER_LEVEL; + *polp = INTR_POLARITY_LOW; + break; + default: + device_printf(dev, "unsupported trigger/polarity " + "configuration 0x%02x\n", cells[2]); + return (EINVAL); + } + + return (0); +} +#endif + +static int +do_gicv5_iwb_map_intr(device_t dev, struct intr_map_data *data, u_int *irqp, + enum intr_polarity *polp, enum intr_trigger *trigp) +{ + struct gicv5_iwb_softc *sc; + enum intr_polarity pol; + enum intr_trigger trig; +#ifdef FDT + struct intr_map_data_fdt *daf; +#endif + u_int irq; + + sc = device_get_softc(dev); + + switch (data->type) { +#ifdef FDT + case INTR_MAP_DATA_FDT: + daf = (struct intr_map_data_fdt *)data; + if (gicv5_iwb_map_fdt(dev, daf->ncells, daf->cells, &irq, &pol, + &trig) != 0) + return (EINVAL); + break; +#endif + default: + return (EINVAL); + } + + if (irq > sc->sc_nirq) + return (EINVAL); + + switch (pol) { + case INTR_POLARITY_CONFORM: + case INTR_POLARITY_LOW: + case INTR_POLARITY_HIGH: + break; + default: + return (EINVAL); + } + switch (trig) { + case INTR_TRIGGER_CONFORM: + case INTR_TRIGGER_EDGE: + case INTR_TRIGGER_LEVEL: + break; + default: + return (EINVAL); + } + + *irqp = irq; + if (polp != NULL) + *polp = pol; + if (trigp != NULL) + *trigp = trig; + return (0); +} + +static int +gicv5_iwb_map_intr(device_t dev, struct intr_map_data *data, + struct intr_irqsrc **isrcp) +{ + struct gicv5_iwb_softc *sc; + u_int irq; + int error; + + error = do_gicv5_iwb_map_intr(dev, data, &irq, NULL, NULL); + if (error == 0) { + sc = device_get_softc(dev); + *isrcp = &sc->sc_irqs[irq].gi_isrc; + } + return (error); +} + +static int +gicv5_iwb_setup_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + struct gicv5_iwb_irqsrc *gi = (struct gicv5_iwb_irqsrc *)isrc; + struct gicv5_iwb_softc *sc; + enum intr_trigger trig; + enum intr_polarity pol; + uint32_t reg; + u_int irq; + int error; + + if (data == NULL) + return (ENOTSUP); + + error = do_gicv5_iwb_map_intr(dev, data, &irq, &pol, &trig); + if (error != 0) + return (error); + + if (gi->gi_irq != irq || pol == INTR_POLARITY_CONFORM || + trig == INTR_TRIGGER_CONFORM) + return (EINVAL); + + /* Compare config if this is not first setup. */ + if (isrc->isrc_handlers != 0) { + if (pol != gi->gi_pol || trig != gi->gi_trig) + return (EINVAL); + else + return (0); + } + + sc = device_get_softc(dev); + + gi->gi_pol = pol; + gi->gi_trig = trig; + + mtx_lock_spin(&sc->sc_mtx); + reg = bus_read_4(sc->sc_mem, IWB_WTMR(irq)); + reg &= ~WTMR_MASK(irq); + if (trig == INTR_TRIGGER_LEVEL) + reg |= WTMR_LEVEL(irq); + bus_write_4(sc->sc_mem, IWB_WTMR(irq), reg); + mtx_unlock_spin(&sc->sc_mtx); + + return (0); +} + +static int +gicv5_iwb_teardown_intr(device_t dev, struct intr_irqsrc *isrc, + struct resource *res, struct intr_map_data *data) +{ + return (0); +} + +static void +gicv5_iwb_post_filter(device_t dev, struct intr_irqsrc *isrc) +{ + /* Handled by the parent as we use a filter to dispatch */ +} + +static void +gicv5_iwb_post_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + gicv5_iwb_enable_intr(dev, isrc); +} + +static void +gicv5_iwb_pre_ithread(device_t dev, struct intr_irqsrc *isrc) +{ + gicv5_iwb_disable_intr(dev, isrc); +} + +#ifdef FDT +struct gicv5_iwb_fdt_softc { + struct gicv5_iwb_softc sc_base; +}; + +static device_probe_t gicv5_iwb_fdt_probe; +static device_attach_t gicv5_iwb_fdt_attach; + +static device_method_t gicv5_iwb_fdt_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, gicv5_iwb_fdt_probe), + DEVMETHOD(device_attach, gicv5_iwb_fdt_attach), + + /* End */ + DEVMETHOD_END +}; + +#define iwb_baseclasses iwbv5_fdt_baseclasses +DEFINE_CLASS_1(iwb, gicv5_iwb_fdt_driver, gicv5_iwb_fdt_methods, + sizeof(struct gicv5_iwb_fdt_softc), gicv5_iwb_driver); +#undef iwb_baseclasses + +/* This needs to be after the ITS as it sends MSI messages there */ +EARLY_DRIVER_MODULE(gicv5_iwb, simplebus, gicv5_iwb_fdt_driver, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); + +static int +gicv5_iwb_fdt_probe(device_t dev) +{ + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "arm,gic-v5-iwb")) + return (ENXIO); + + device_set_desc(dev, "ARM GICv5 Interrupt Wire Bridge"); + return (BUS_PROBE_DEFAULT); +} + +static int +gicv5_iwb_fdt_attach(device_t dev) +{ + struct gicv5_iwb_fdt_softc *sc; + intptr_t xref; + phandle_t node; + int error; + + error = gicv5_iwb_attach(dev); + if (error != 0) + return (error); + + sc = device_get_softc(dev); + node = ofw_bus_get_node(dev); + xref = OF_xref_from_node(node); + sc->sc_base.sc_pic = intr_pic_register(dev, xref); + if (sc->sc_base.sc_pic == NULL) + return (ENXIO); + + OF_device_register_xref(xref, dev); + + return (0); +} +#endif /* FDT */ diff --git a/sys/conf/files.arm64 b/sys/conf/files.arm64 index a2244a9fe9b8..6a495d193890 100644 --- a/sys/conf/files.arm64 +++ b/sys/conf/files.arm64 @@ -62,6 +62,7 @@ arm64/arm64/gic_v3_fdt.c optional fdt arm64/arm64/gicv5.c standard arm64/arm64/gicv5_fdt.c optional fdt arm64/arm64/gicv5_its.c optional fdt +arm64/arm64/gicv5_iwb.c optional fdt arm64/arm64/hyp_stub.S standard arm64/arm64/identcpu.c standard arm64/arm64/kexec_support.c standard