Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F109832380
D43293.id132541.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
13 KB
Referenced Files
None
Subscribers
None
D43293.id132541.diff
View Options
diff --git a/sys/conf/files.riscv b/sys/conf/files.riscv
--- a/sys/conf/files.riscv
+++ b/sys/conf/files.riscv
@@ -53,6 +53,7 @@
riscv/riscv/nexus.c standard
riscv/riscv/ofw_machdep.c optional fdt
riscv/riscv/plic.c standard
+riscv/riscv/aplic.c standard
riscv/riscv/pmap.c standard
riscv/riscv/ptrace_machdep.c standard
riscv/riscv/riscv_console.c optional rcons
diff --git a/sys/riscv/riscv/aplic.c b/sys/riscv/riscv/aplic.c
new file mode 100644
--- /dev/null
+++ b/sys/riscv/riscv/aplic.c
@@ -0,0 +1,470 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2024 Himanshu Chauhan <hchauhan@thechauhan.dev>
+ *
+ * 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 <sys/param.h>
+#include <sys/systm.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/ktr.h>
+#include <sys/module.h>
+#include <sys/proc.h>
+#include <sys/rman.h>
+#include <sys/smp.h>
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+#include <machine/riscvreg.h>
+
+#include <dev/ofw/openfirm.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+#include "pic_if.h"
+
+#define APLIC_MAX_IRQS 1023
+
+/* Smaller priority number means higher priority */
+#define APLIC_INTR_DEF_PRIO (1)
+
+static pic_disable_intr_t aplic_disable_intr;
+static pic_enable_intr_t aplic_enable_intr;
+static pic_map_intr_t aplic_map_intr;
+static pic_setup_intr_t aplic_setup_intr;
+static pic_post_ithread_t aplic_post_ithread;
+static pic_pre_ithread_t aplic_pre_ithread;
+static pic_bind_intr_t aplic_bind_intr;
+
+struct aplic_irqsrc {
+ struct intr_irqsrc isrc;
+ u_int irq;
+};
+
+struct aplic_softc {
+ device_t dev;
+ struct resource *intc_res;
+ struct aplic_irqsrc isrcs[APLIC_MAX_IRQS];
+ int ndev;
+};
+
+#define APLIC_DOMAIN_CFG_IE (1UL << 8) /* Enable domain IRQs */
+#define APLIC_DOMAIN_CFG_DM (1UL << 2) /* IRQ delivery mode */
+#define APLIC_DOMAIN_CFG_BE (1UL << 0) /* Endianess */
+
+#define APLIC_MODE_DIRECT 0 /* Direct delivery mode */
+#define APLIC_MODE_MSI 1 /* MSI delivery mode */
+
+#define APLIC_SRC_CFG_DLGT (1UL << 10) /* Source delegation */
+#define APLIC_SRC_CFG_SM_SHIFT 0
+#define APLIC_SRC_CFG_SM_MASK ((0x7UL) << APLIC_SRC_CFG_SM_SHIFT)
+
+#define APLIC_SRC_CFG_SM_INACTIVE 0 /* APLIC inactive in domain */
+#define APLIC_SRC_CFG_SM_DETACHED 1 /* Detached from source wire */
+#define APLIC_SRC_CFG_SM_EDGE_RSE 4 /* Asserted on rising edge */
+#define APLIC_SRC_CFG_SM_EDGE_FLL 5 /* Asserted on falling edge */
+#define APLIC_SRC_CFG_SM_LVL_HI 6 /* Asserted when high */
+#define APLIC_SRC_CFG_SM_LVL_LO 7 /* Asserted when low */
+
+/* Register offsets in APLIC configuration space */
+#define APLIC_DOMAIN_CFG (0x0000)
+#define APLIC_SRC_CFG(_idx) (0x0004 + ((_idx-1) * 4))
+#define APLIC_TARGET(_idx) (0x3004 + ((_idx-1) * 4))
+#define APLIC_MMSIADDRCFG (0x1BC0)
+#define APLIC_MMSIADDRCFGH (0x1BC4)
+#define APLIC_SMSIADDRCFG (0x1BC8)
+#define APLIC_SMSIADDRCFGH (0x1BCC)
+#define APLIC_SETIPNUM (0x1CDC)
+#define APLIC_CLRIPNUM (0x1DDC)
+#define APLIC_SETIENUM (0x1EDC)
+#define APLIC_CLRIENUM (0x1FDC)
+#define APLIC_SETIPNUM_LE (0x2000)
+#define APLIC_SETIPNUM_BE (0x2004)
+#define APLIC_GENMSI (0x3000)
+#define APLIC_IDC_BASE (0x4000)
+
+#define APLIC_SETIE_BASE (0x1E00)
+#define APLIC_CLRIE_BASE (0x1F00)
+
+/* Interrupt delivery control structure */
+#define APLIC_IDC_IDELIVERY_OFFS (0x0000)
+#define APLIC_IDC_IFORCE_OFFS (0x0004)
+#define APLIC_IDC_ITHRESHOLD_OFFS (0x0008)
+#define APLIC_IDC_TOPI_OFFS (0x0018)
+#define APLIC_IDC_CLAIMI_OFFS (0x001C)
+
+#define APLIC_IDC_SZ (0x20)
+
+#define APLIC_IDC_IDELIVERY_DISABLE (0)
+#define APLIC_IDC_IDELIVERY_ENABLE (1)
+#define APLIC_IDC_ITHRESHOLD_DISABLE (0)
+
+#define APLIC_IDC_CLAIMI_PRIO_MASK (0xff)
+#define APLIC_IDC_CLAIMI_IRQ_SHIFT (16)
+#define APLIC_IDC_CLAIMI_IRQ_MASK (0x3ff)
+
+#define APLIC_IDC_CLAIMI_IRQ(_claimi) \
+ (((_claimi) >> APLIC_IDC_CLAIMI_IRQ_SHIFT) \
+ & APLIC_IDC_CLAIMI_IRQ_MASK)
+
+#define APLIC_IDC_CLAIMI_PRIO(_claimi) ((_claimi) & APLIC_IDC_CLAIMI_PRIO_MASK)
+
+#define APLIC_IDC_IDELIVERY(_hart_id) \
+ (APLIC_IDC_BASE + ((_hart_id * APLIC_IDC_SZ) + APLIC_IDC_IDELIVERY_OFFS))
+
+#define APLIC_IDC_IFORCE(_hart_id) \
+ (APLIC_IDC_BASE + ((_hart_id * APLIC_IDC_SZ) + APLIC_IDC_IFORCE_OFFS))
+
+#define APLIC_IDC_ITHRESHOLD(_hart_id) \
+ (APLIC_IDC_BASE + ((_hart_id * APLIC_IDC_SZ) \
+ + APLIC_IDC_ITHRESHOLD_OFFS))
+
+#define APLIC_IDC_TOPI(_hart_id) \
+ (APLIC_IDC_BASE + ((_hart_id * APLIC_IDC_SZ) + APLIC_IDC_TOPI_OFFS))
+
+#define APLIC_IDC_CLAIMI(_hart_id) \
+ (APLIC_IDC_BASE + ((_hart_id * APLIC_IDC_SZ) + APLIC_IDC_CLAIMI_OFFS))
+
+#define APLIC_MK_IRQ_TARGET(_hartid, _prio) (_hartid << 18 | (_prio & 0xff))
+
+#define aplic_read(sc, reg) bus_read_4(sc->intc_res, (reg))
+#define aplic_write(sc, reg, val) bus_write_4(sc->intc_res, (reg), (val))
+
+static u_int aplic_irq_cpu;
+
+static uint32_t
+riscv_cpu_to_hartid(uint32_t cpu)
+{
+ return pcpu_find(cpu)->pc_hart;
+}
+
+static inline void
+aplic_irq_dispatch(struct aplic_softc *sc, u_int irq, u_int prio,
+ struct trapframe *tf)
+{
+ struct aplic_irqsrc *src;
+
+ src = &sc->isrcs[irq];
+
+ if (intr_isrc_dispatch(&src->isrc, tf) != 0)
+ if (bootverbose)
+ device_printf(sc->dev, "Stray irq %u detected\n", irq);
+}
+
+static int
+aplic_intr(void *arg)
+{
+ struct aplic_softc *sc;
+ struct trapframe *tf;
+ uint32_t claimi, prio, irq;
+ uint32_t hartid;
+
+ sc = arg;
+ hartid = PCPU_GET(hart);
+
+ /* Claim any pending interrupt. */
+ claimi = aplic_read(sc, APLIC_IDC_CLAIMI(hartid));
+ prio = APLIC_IDC_CLAIMI_PRIO(claimi);
+ irq = APLIC_IDC_CLAIMI_IRQ(claimi);
+
+ if (irq == 0)
+ return (FILTER_STRAY);
+
+ tf = curthread->td_intr_frame;
+ aplic_irq_dispatch(sc, irq, prio, tf);
+
+ return (FILTER_HANDLED);
+}
+
+static void
+aplic_disable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct aplic_softc *sc;
+ struct aplic_irqsrc *src;
+
+ sc = device_get_softc(dev);
+ src = (struct aplic_irqsrc *)isrc;
+
+ /* Disable the interrupt source */
+ aplic_write(sc, APLIC_CLRIENUM, src->irq);
+}
+
+static void
+aplic_enable_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct aplic_softc *sc;
+ struct aplic_irqsrc *src;
+
+ sc = device_get_softc(dev);
+ src = (struct aplic_irqsrc *)isrc;
+
+ /* Enable the interrupt source */
+ aplic_write(sc, APLIC_SETIENUM, src->irq);
+}
+
+static int
+aplic_map_intr(device_t dev, struct intr_map_data *data,
+ struct intr_irqsrc **isrcp)
+{
+ struct intr_map_data_fdt *daf;
+ struct aplic_softc *sc;
+
+ sc = device_get_softc(dev);
+
+ if (data->type != INTR_MAP_DATA_FDT)
+ return (ENOTSUP);
+
+ daf = (struct intr_map_data_fdt *)data;
+ if (daf->ncells != 2 || daf->cells[0] > sc->ndev) {
+ device_printf(dev, "Invalid cell data\n");
+ return (EINVAL);
+ }
+
+ *isrcp = &sc->isrcs[daf->cells[0]].isrc;
+
+ return (0);
+}
+
+static int
+aplic_probe(device_t dev)
+{
+ if (!ofw_bus_status_okay(dev))
+ return (ENXIO);
+
+ if (!ofw_bus_is_compatible(dev, "riscv,aplic"))
+ return (ENXIO);
+
+ /* This aplic domain doesn't belong to S-mode */
+ if (ofw_bus_has_prop(dev, "riscv,delegate"))
+ return (ENXIO);
+
+ device_set_desc(dev, "Advanced Platform-Level Interrupt Controller");
+
+ return (BUS_PROBE_DEFAULT);
+}
+
+/*
+ * Setup APLIC in direct mode.
+ */
+static int
+aplic_setup_direct_mode(device_t dev)
+{
+ struct aplic_irqsrc *isrcs;
+ struct aplic_softc *sc;
+ struct intr_pic *pic;
+ uint32_t irq;
+ uint32_t cpu;
+ uint32_t hartid;
+ const char *name;
+ phandle_t node;
+ phandle_t xref;
+ int error;
+ int rid;
+
+ sc = device_get_softc(dev);
+ node = ofw_bus_get_node(dev);
+
+ sc->dev = dev;
+
+ if ((OF_getencprop(node, "riscv,num-sources", &sc->ndev,
+ sizeof(sc->ndev))) < 0) {
+ device_printf(dev, "Error: could not get number of devices\n");
+ return (ENXIO);
+ }
+
+ if (sc->ndev > APLIC_MAX_IRQS) {
+ device_printf(dev, "Error: invalid ndev (%d)\n", sc->ndev);
+ return (ENXIO);
+ }
+
+ /* Request memory resources */
+ rid = 0;
+ sc->intc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
+ RF_ACTIVE);
+ if (sc->intc_res == NULL) {
+ device_printf(dev,
+ "Error: could not allocate memory resources\n");
+ return (ENXIO);
+ }
+
+ /* Set APLIC in direct mode and enable all interrupts */
+ aplic_write(sc, APLIC_DOMAIN_CFG,
+ APLIC_MODE_DIRECT | APLIC_DOMAIN_CFG_IE);
+
+ /* Register the interrupt sources */
+ isrcs = sc->isrcs;
+ name = device_get_nameunit(sc->dev);
+ for (irq = 1; irq <= sc->ndev; irq++) {
+ isrcs[irq].irq = irq;
+ error = intr_isrc_register(&isrcs[irq].isrc, sc->dev,
+ 0, "%s,%u", name, irq);
+ if (error != 0)
+ goto fail;
+
+ aplic_write(sc, APLIC_SRC_CFG(irq),
+ APLIC_SRC_CFG_SM_DETACHED);
+ }
+
+ /* Turn off the interrupt delivery for all CPUs */
+ CPU_FOREACH(cpu) {
+ hartid = riscv_cpu_to_hartid(cpu);
+ aplic_write(sc, APLIC_IDC_IDELIVERY(hartid),
+ APLIC_IDC_IDELIVERY_DISABLE);
+ aplic_write(sc, APLIC_IDC_ITHRESHOLD(hartid),
+ APLIC_IDC_ITHRESHOLD_DISABLE);
+ }
+
+ xref = OF_xref_from_node(node);
+ pic = intr_pic_register(sc->dev, xref);
+ if (pic == NULL) {
+ error = ENXIO;
+ goto fail;
+ }
+
+ intr_pic_claim_root(sc->dev, xref, aplic_intr, sc, 0);
+
+ csr_set(sie, SIE_SEIE);
+
+ if (bootverbose)
+ device_printf(dev, "APLIC configured in direct mode.\n");
+
+ return (0);
+
+ fail:
+ bus_release_resource(dev, SYS_RES_MEMORY, rid, sc->intc_res);
+ sc->intc_res = NULL;
+
+ return (error);
+}
+
+static int
+aplic_attach(device_t dev)
+{
+ int rc;
+
+ /* APLIC with IMSIC on hart is not supported */
+ if (ofw_bus_has_prop(dev, "msi-parent")) {
+ device_printf(dev, "APLIC with IMSIC is unsupported\n");
+ return (ENXIO);
+ }
+
+ rc = aplic_setup_direct_mode(dev);
+
+ return (rc);
+}
+
+static void
+aplic_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ aplic_disable_intr(dev, isrc);
+}
+
+static void
+aplic_post_ithread(device_t dev, struct intr_irqsrc *isrc)
+{
+ aplic_enable_intr(dev, isrc);
+}
+
+static int
+aplic_setup_intr(device_t dev, struct intr_irqsrc *isrc, struct resource *res,
+ struct intr_map_data *data)
+{
+ struct aplic_irqsrc *src;
+ struct aplic_softc *sc;
+
+ CPU_ZERO(&isrc->isrc_cpu);
+
+ sc = device_get_softc(dev);
+ src = (struct aplic_irqsrc *)isrc;
+
+ aplic_write(sc, APLIC_SRC_CFG(src->irq),
+ APLIC_SRC_CFG_SM_EDGE_RSE);
+
+ /*
+ * In uniprocessor system, bind_intr will not be called.
+ * So bind the interrupt on this CPU. If secondary CPUs
+ * are present, then bind_intr will be called again and
+ * interrupts will rebind to those CPUs.
+ */
+ aplic_bind_intr(dev, isrc);
+
+ return (0);
+}
+
+static int
+aplic_bind_intr(device_t dev, struct intr_irqsrc *isrc)
+{
+ struct aplic_softc *sc;
+ struct aplic_irqsrc *src;
+ uint32_t cpu, hartid;
+
+ sc = device_get_softc(dev);
+ src = (struct aplic_irqsrc *)isrc;
+
+ /* Disable the interrupt source */
+ aplic_write(sc, APLIC_CLRIENUM, src->irq);
+
+ if (CPU_EMPTY(&isrc->isrc_cpu)) {
+ cpu = aplic_irq_cpu = intr_irq_next_cpu(aplic_irq_cpu,
+ &all_cpus);
+ CPU_SETOF(cpu, &isrc->isrc_cpu);
+ } else {
+ cpu = CPU_FFS(&isrc->isrc_cpu) - 1;
+ }
+
+ hartid = riscv_cpu_to_hartid(cpu);
+
+ aplic_write(sc, APLIC_TARGET(src->irq),
+ APLIC_MK_IRQ_TARGET(hartid, APLIC_INTR_DEF_PRIO));
+ aplic_write(sc, APLIC_IDC_IDELIVERY(hartid), APLIC_IDC_IDELIVERY_ENABLE);
+ aplic_enable_intr(dev, isrc);
+
+ return (0);
+}
+
+static device_method_t aplic_methods[] = {
+ DEVMETHOD(device_probe, aplic_probe),
+ DEVMETHOD(device_attach, aplic_attach),
+
+ DEVMETHOD(pic_disable_intr, aplic_disable_intr),
+ DEVMETHOD(pic_enable_intr, aplic_enable_intr),
+ DEVMETHOD(pic_map_intr, aplic_map_intr),
+ DEVMETHOD(pic_pre_ithread, aplic_pre_ithread),
+ DEVMETHOD(pic_post_ithread, aplic_post_ithread),
+ DEVMETHOD(pic_post_filter, aplic_post_ithread),
+ DEVMETHOD(pic_setup_intr, aplic_setup_intr),
+ DEVMETHOD(pic_bind_intr, aplic_bind_intr),
+
+ DEVMETHOD_END
+};
+
+static driver_t aplic_driver = {
+ "aplic",
+ aplic_methods,
+ sizeof(struct aplic_softc),
+};
+
+EARLY_DRIVER_MODULE(aplic, simplebus, aplic_driver, 0, 0,
+ BUS_PASS_INTERRUPT + BUS_PASS_ORDER_MIDDLE);
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Feb 11, 2:37 AM (1 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
16587851
Default Alt Text
D43293.id132541.diff (13 KB)
Attached To
Mode
D43293: RISCV: Introduce support for APLIC interrupt controller
Attached
Detach File
Event Timeline
Log In to Comment