Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/arch/arm/fdt Support interrupt sharing.



details:   https://anonhg.NetBSD.org/src/rev/6a494b21fc53
branches:  trunk
changeset: 825066:6a494b21fc53
user:      jmcneill <jmcneill%NetBSD.org@localhost>
date:      Wed Jun 28 23:49:29 2017 +0000

description:
Support interrupt sharing.

diffstat:

 sys/arch/arm/fdt/gic_fdt.c |  98 +++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 93 insertions(+), 5 deletions(-)

diffs (158 lines):

diff -r 183fe9519b00 -r 6a494b21fc53 sys/arch/arm/fdt/gic_fdt.c
--- a/sys/arch/arm/fdt/gic_fdt.c        Wed Jun 28 23:48:22 2017 +0000
+++ b/sys/arch/arm/fdt/gic_fdt.c        Wed Jun 28 23:49:29 2017 +0000
@@ -1,4 +1,4 @@
-/* $NetBSD: gic_fdt.c,v 1.4 2017/05/30 22:00:25 jmcneill Exp $ */
+/* $NetBSD: gic_fdt.c,v 1.5 2017/06/28 23:49:29 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2015-2017 Jared McNeill <jmcneill%invisible.ca@localhost>
@@ -27,7 +27,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: gic_fdt.c,v 1.4 2017/05/30 22:00:25 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: gic_fdt.c,v 1.5 2017/06/28 23:49:29 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -35,6 +35,7 @@
 #include <sys/intr.h>
 #include <sys/systm.h>
 #include <sys/kernel.h>
+#include <sys/lwp.h>
 #include <sys/kmem.h>
 
 #include <arm/cortex/gic_intr.h>
@@ -42,9 +43,13 @@
 
 #include <dev/fdt/fdtvar.h>
 
+#define        GIC_MAXIRQ      1020
+
 static int     gic_fdt_match(device_t, cfdata_t, void *);
 static void    gic_fdt_attach(device_t, device_t, void *);
 
+static int     gic_fdt_intr(void *);
+
 static void *  gic_fdt_establish(device_t, u_int *, int, int,
                    int (*)(void *), void *);
 static void    gic_fdt_disestablish(device_t, void *);
@@ -56,9 +61,30 @@
        .intrstr = gic_fdt_intrstr
 };
 
+struct gic_fdt_softc;
+struct gic_fdt_irq;
+
+struct gic_fdt_irqhandler {
+       struct gic_fdt_irq      *ih_irq;
+       int                     (*ih_fn)(void *);
+       void                    *ih_arg;
+       bool                    ih_mpsafe;
+       TAILQ_ENTRY(gic_fdt_irqhandler) ih_next;
+};
+
+struct gic_fdt_irq {
+       struct gic_fdt_softc    *intr_sc;
+       void                    *intr_ih;
+       int                     intr_refcnt;
+       int                     intr_ipl;
+       TAILQ_HEAD(, gic_fdt_irqhandler) intr_handlers;
+};
+
 struct gic_fdt_softc {
        device_t                sc_dev;
        int                     sc_phandle;
+
+       struct gic_fdt_irq      *sc_irq[GIC_MAXIRQ];
 };
 
 CFATTACH_DECL_NEW(gic_fdt, sizeof(struct gic_fdt_softc),
@@ -137,7 +163,9 @@
 gic_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags,
     int (*func)(void *), void *arg)
 {
-       int iflags = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
+       struct gic_fdt_softc * const sc = device_private(dev);
+       struct gic_fdt_irq *firq;
+       struct gic_fdt_irqhandler *firqh;
 
        /* 1st cell is the interrupt type; 0 is SPI, 1 is PPI */
        /* 2nd cell is the interrupt number */
@@ -149,13 +177,73 @@
        const u_int trig = be32toh(specifier[2]) & 0xf;
        const u_int level = (trig & 0x3) ? IST_EDGE : IST_LEVEL;
 
-       return intr_establish(irq, ipl, level | iflags, func, arg);
+       firq = sc->sc_irq[irq];
+       if (firq == NULL) {
+               firq = kmem_alloc(sizeof(*firq), KM_SLEEP);
+               firq->intr_sc = sc;
+               firq->intr_refcnt = 0;
+               firq->intr_ipl = ipl;
+               TAILQ_INIT(&firq->intr_handlers);
+               firq->intr_ih = intr_establish(irq, ipl, level | IST_MPSAFE,
+                   gic_fdt_intr, firq);
+               if (firq->intr_ih == NULL) {
+                       kmem_free(firq, sizeof(*firq));
+                       return NULL;
+               }
+               sc->sc_irq[irq] = firq;
+       }
+
+       if (firq->intr_ipl != ipl) {
+               device_printf(dev, "cannot share irq with different ipl\n");
+               return NULL;
+       }
+
+       firq->intr_refcnt++;
+
+       firqh = kmem_alloc(sizeof(*firqh), KM_SLEEP);
+       firqh->ih_mpsafe = (flags & FDT_INTR_MPSAFE) != 0;
+       firqh->ih_irq = firq;
+       firqh->ih_fn = func;
+       firqh->ih_arg = arg;
+       TAILQ_INSERT_TAIL(&firq->intr_handlers, firqh, ih_next);
+
+       return firqh;
 }
 
 static void
 gic_fdt_disestablish(device_t dev, void *ih)
 {
-       intr_disestablish(ih);
+       struct gic_fdt_irqhandler *firqh = ih;
+       struct gic_fdt_irq *firq = firqh->ih_irq;
+
+       KASSERT(firq->intr_refcnt > 0);
+
+       TAILQ_REMOVE(&firq->intr_handlers, firqh, ih_next);
+       kmem_free(firqh, sizeof(*firqh));
+
+       firq->intr_refcnt--;
+       if (firq->intr_refcnt == 0) {
+               intr_disestablish(firq->intr_ih);
+               kmem_free(firq, sizeof(*firq));
+       }
+}
+
+static int
+gic_fdt_intr(void *priv)
+{
+       struct gic_fdt_irq *firq = priv;
+       struct gic_fdt_irqhandler *firqh;
+       int handled = 0;
+
+       TAILQ_FOREACH(firqh, &firq->intr_handlers, ih_next) {
+               if (!firqh->ih_mpsafe)
+                       KERNEL_LOCK(1, curlwp);
+               handled += firqh->ih_fn(firqh->ih_arg);
+               if (!firqh->ih_mpsafe)
+                       KERNEL_UNLOCK_ONE(curlwp);
+       }
+
+       return handled;
 }
 
 static bool



Home | Main Index | Thread Index | Old Index