Module Name:    src
Committed By:   jmcneill
Date:           Wed Jun 28 23:49:30 UTC 2017

Modified Files:
        src/sys/arch/arm/fdt: gic_fdt.c

Log Message:
Support interrupt sharing.


To generate a diff of this commit:
cvs rdiff -u -r1.4 -r1.5 src/sys/arch/arm/fdt/gic_fdt.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/fdt/gic_fdt.c
diff -u src/sys/arch/arm/fdt/gic_fdt.c:1.4 src/sys/arch/arm/fdt/gic_fdt.c:1.5
--- src/sys/arch/arm/fdt/gic_fdt.c:1.4	Tue May 30 22:00:25 2017
+++ src/sys/arch/arm/fdt/gic_fdt.c	Wed Jun 28 23:49:29 2017
@@ -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 <jmcne...@invisible.ca>
@@ -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 @@ __KERNEL_RCSID(0, "$NetBSD: gic_fdt.c,v 
 #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 @@ __KERNEL_RCSID(0, "$NetBSD: gic_fdt.c,v 
 
 #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 @@ struct fdtbus_interrupt_controller_func 
 	.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 @@ static void *
 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 @@ gic_fdt_establish(device_t dev, u_int *s
 	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

Reply via email to