Skip to content

Commit

Permalink
plic: handling for interrupt-cells == 2
Browse files Browse the repository at this point in the history
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
  • Loading branch information
mhorne committed Oct 30, 2024
1 parent d584228 commit 47ca5d1
Showing 1 changed file with 43 additions and 2 deletions.
45 changes: 43 additions & 2 deletions sys/riscv/riscv/plic.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -214,17 +217,55 @@ 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);

if (data->type != INTR_MAP_DATA_FDT)
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);
}
Expand Down

0 comments on commit 47ca5d1

Please sign in to comment.