git: 47ca5d103f22 - main - plic: handling for interrupt-cells == 2

From: Mitchell Horne <mhorne_at_FreeBSD.org>
Date: Wed, 30 Oct 2024 18:21:02 UTC
The branch main has been updated by mhorne:

URL: https://cgit.FreeBSD.org/src/commit/?id=47ca5d103f229b090899379ce449af5e89faf627

commit 47ca5d103f229b090899379ce449af5e89faf627
Author:     Mitchell Horne <mhorne@FreeBSD.org>
AuthorDate: 2024-10-30 18:17:09 +0000
Commit:     Mitchell Horne <mhorne@FreeBSD.org>
CommitDate: 2024-10-30 18:18:16 +0000

    plic: handling for interrupt-cells == 2
    
    Some device trees report interrupt info in two cells: the first value is
    the IRQ number, the second value indicates the trigger type, such as
    IRQ_TYPE_LEVEL_HIGH. The device tree for the Allwinner D1(s) is one such
    case. Thus, extend the PLIC driver to accept this extra information when
    available.
    
    Apparently, some PLIC implementations using edge-triggered interrupts
    will require some special handling. This is not required for the D1, and
    therefore not implemented in this change. However, to prevent
    misbehaviour a check is added to reject this case and a message will be
    printed.
    
    Similarly, emit messages for the error paths, e.g. ncells == 3. Drivers
    will fail to attach all the same, but the message will aid in diagnosing
    the issue more quickly.
    
    Reviewed by:    br
    MFC after:      1 week
    Sponsored by:   The FreeBSD Foundation
    Differential Revision:  https://reviews.freebsd.org/D47135
---
 sys/riscv/riscv/plic.c | 45 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 43 insertions(+), 2 deletions(-)

diff --git a/sys/riscv/riscv/plic.c b/sys/riscv/riscv/plic.c
index e28019e6f458..b7dda1d19daf 100644
--- a/sys/riscv/riscv/plic.c
+++ b/sys/riscv/riscv/plic.c
@@ -49,6 +49,8 @@
 #include <dev/ofw/ofw_bus.h>
 #include <dev/ofw/ofw_bus_subr.h>
 
+#include <dt-bindings/interrupt-controller/irq.h>
+
 #include "pic_if.h"
 
 #define	PLIC_MAX_IRQS		1024
@@ -82,6 +84,7 @@ static pic_bind_intr_t		plic_bind_intr;
 struct plic_irqsrc {
 	struct intr_irqsrc	isrc;
 	u_int			irq;
+	u_int			trigtype;
 };
 
 struct plic_context {
@@ -214,6 +217,7 @@ plic_map_intr(device_t dev, struct intr_map_data *data,
 {
 	struct intr_map_data_fdt *daf;
 	struct plic_softc *sc;
+	u_int irq, type;
 
 	sc = device_get_softc(dev);
 
@@ -221,10 +225,47 @@ plic_map_intr(device_t dev, struct intr_map_data *data,
 		return (ENOTSUP);
 
 	daf = (struct intr_map_data_fdt *)data;
-	if (daf->ncells != 1 || daf->cells[0] > sc->ndev)
+	if (daf->ncells != 1 && daf->ncells != 2) {
+		device_printf(dev, "invalid ncells value: %u\n", daf->ncells);
+		return (EINVAL);
+	}
+
+	irq = daf->cells[0];
+	type = daf->ncells == 2 ? daf->cells[1] : IRQ_TYPE_LEVEL_HIGH;
+
+	if (irq > sc->ndev) {
+		device_printf(dev, "irq (%u) > sc->ndev (%u)",
+		    daf->cells[0], sc->ndev);
+		return (EINVAL);
+	}
+
+	/*
+	 * TODO: handling of edge-triggered interrupts.
+	 *
+	 * From sifive,plic-1.0.0.yaml:
+	 *
+	 * "The PLIC supports both edge-triggered and level-triggered
+	 * interrupts. For edge-triggered interrupts, the RISC-V PLIC spec
+	 * allows two responses to edges seen while an interrupt handler is
+	 * active; the PLIC may either queue them or ignore them. In the first
+	 * case, handlers are oblivious to the trigger type, so it is not
+	 * included in the interrupt specifier. In the second case, software
+	 * needs to know the trigger type, so it can reorder the interrupt flow
+	 * to avoid missing interrupts. This special handling is needed by at
+	 * least the Renesas RZ/Five SoC (AX45MP AndesCore with a NCEPLIC100)
+	 * and the T-HEAD C900 PLIC."
+	 *
+	 * For now, prevent interrupts with type IRQ_TYPE_EDGE_RISING from
+	 * allocation. Emit a message so that when the relevant driver fails to
+	 * attach, it will at least be clear why.
+	 */
+	if (type != IRQ_TYPE_LEVEL_HIGH) {
+		device_printf(dev, "edge-triggered interrupts not supported\n");
 		return (EINVAL);
+	}
 
-	*isrcp = &sc->isrcs[daf->cells[0]].isrc;
+	sc->isrcs[irq].trigtype = type;
+	*isrcp = &sc->isrcs[irq].isrc;
 
 	return (0);
 }