Source-Changes-HG archive

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

[src/trunk]: src/sys/arch/arm/broadcom There are some devices on the bcm283x ...



details:   https://anonhg.NetBSD.org/src/rev/546ee5605edc
branches:  trunk
changeset: 465553:546ee5605edc
user:      thorpej <thorpej%NetBSD.org@localhost>
date:      Thu Nov 28 01:08:06 2019 +0000

description:
There are some devices on the bcm283x that share IRQs (notably, the i2c
controllers), but the generic ARM PIC code doesn't support this.  Add a
workaround inspired by the GICv3 driver (thanks Jared for pointint it out).

diffstat:

 sys/arch/arm/broadcom/bcm2835_intr.c |  115 +++++++++++++++++++++++++++++++++-
 1 files changed, 110 insertions(+), 5 deletions(-)

diffs (169 lines):

diff -r 5aa29c554872 -r 546ee5605edc sys/arch/arm/broadcom/bcm2835_intr.c
--- a/sys/arch/arm/broadcom/bcm2835_intr.c      Thu Nov 28 00:18:36 2019 +0000
+++ b/sys/arch/arm/broadcom/bcm2835_intr.c      Thu Nov 28 01:08:06 2019 +0000
@@ -1,7 +1,7 @@
-/*     $NetBSD: bcm2835_intr.c,v 1.24 2019/09/25 16:57:10 skrll Exp $  */
+/*     $NetBSD: bcm2835_intr.c,v 1.25 2019/11/28 01:08:06 thorpej Exp $        */
 
 /*-
- * Copyright (c) 2012, 2015 The NetBSD Foundation, Inc.
+ * Copyright (c) 2012, 2015, 2019 The NetBSD Foundation, Inc.
  * All rights reserved.
  *
  * This code is derived from software contributed to The NetBSD Foundation
@@ -30,7 +30,7 @@
  */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: bcm2835_intr.c,v 1.24 2019/09/25 16:57:10 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: bcm2835_intr.c,v 1.25 2019/11/28 01:08:06 thorpej Exp $");
 
 #define _INTR_PRIVATE
 
@@ -87,6 +87,8 @@
 static void bcm2835_icu_fdt_disestablish(device_t, void *);
 static bool bcm2835_icu_fdt_intrstr(device_t, u_int *, char *, size_t);
 
+static int bcm2835_icu_intr(void *);
+
 static int bcm2836mp_icu_fdt_decode_irq(u_int *);
 static void *bcm2836mp_icu_fdt_establish(device_t, u_int *, int, int,
     int (*)(void *), void *);
@@ -162,11 +164,35 @@
 static TAILQ_HEAD(, bcm2836mp_interrupt) bcm2836mp_interrupts =
     TAILQ_HEAD_INITIALIZER(bcm2836mp_interrupts);
 
+struct bcm2835icu_irqhandler;
+struct bcm2835icu_irq;
+struct bcm2835icu_softc;
+
+struct bcm2835icu_irqhandler {
+       struct bcm2835icu_irq   *ih_irq;
+       int                     (*ih_fn)(void *);
+       void                    *ih_arg;
+       TAILQ_ENTRY(bcm2835icu_irqhandler) ih_next;
+};
+
+struct bcm2835icu_irq {
+       struct bcm2835icu_softc *intr_sc;
+       void                    *intr_ih;
+       void                    *intr_arg;
+       int                     intr_refcnt;
+       int                     intr_ipl;
+       int                     intr_irq;
+       int                     intr_mpsafe;
+       TAILQ_HEAD(, bcm2835icu_irqhandler) intr_handlers;
+};
+
 struct bcm2835icu_softc {
        device_t                sc_dev;
        bus_space_tag_t         sc_iot;
        bus_space_handle_t      sc_ioh;
 
+       struct bcm2835icu_irq   *sc_irq[BCM2835_NIRQ];
+
        int sc_phandle;
 };
 
@@ -437,6 +463,9 @@
 bcm2835_icu_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags,
     int (*func)(void *), void *arg)
 {
+       struct bcm2835icu_softc * const sc = device_private(dev);
+       struct bcm2835icu_irq *firq;
+       struct bcm2835icu_irqhandler *firqh;
        int iflags = (flags & FDT_INTR_MPSAFE) ? IST_MPSAFE : 0;
        int irq;
 
@@ -444,13 +473,89 @@
        if (irq == -1)
                return NULL;
 
-       return intr_establish(irq, ipl, IST_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_arg = arg;
+               firq->intr_ipl = ipl;
+               firq->intr_mpsafe = iflags;
+               firq->intr_irq = irq;
+               TAILQ_INIT(&firq->intr_handlers);
+               if (arg == NULL) {
+                       firq->intr_ih = intr_establish(irq, ipl,
+                           IST_LEVEL | iflags, func, NULL);
+               } else {
+                       firq->intr_ih = intr_establish(irq, ipl,
+                           IST_LEVEL | iflags, bcm2835_icu_intr, firq);
+               }
+               if (firq->intr_ih == NULL) {
+                       kmem_free(firq, sizeof(*firq));
+                       return NULL;
+               }
+               sc->sc_irq[irq] = firq;
+       } else {
+               if (firq->intr_arg == NULL || arg == NULL) {
+                       device_printf(dev,
+                           "cannot share irq with NULL-arg handler\n");
+                       return NULL;
+               }
+               if (firq->intr_ipl != ipl) {
+                       device_printf(dev,
+                           "cannot share irq with different ipl\n");
+                       return NULL;
+               }
+               if (firq->intr_mpsafe != iflags) {
+                       device_printf(dev,
+                           "cannot share irq between mpsafe/non-mpsafe\n");
+                       return NULL;
+               }
+       }
+
+       firqh = kmem_alloc(sizeof(*firqh), KM_SLEEP);
+       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
 bcm2835_icu_fdt_disestablish(device_t dev, void *ih)
 {
-       intr_disestablish(ih);
+       struct bcm2835icu_softc * const sc = device_private(dev);
+       struct bcm2835icu_irqhandler *firqh = ih;
+       struct bcm2835icu_irq *firq = firqh->ih_irq;
+
+       KASSERT(firq->intr_refcnt > 0);
+
+       /* XXX */
+       if (firq->intr_refcnt > 1)
+               panic("%s: cannot disestablish shared irq", __func__);
+
+       intr_disestablish(firq->intr_ih);
+
+       TAILQ_REMOVE(&firq->intr_handlers, firqh, ih_next);
+       kmem_free(firqh, sizeof(*firqh));
+
+       sc->sc_irq[firq->intr_irq] = NULL;
+       kmem_free(firq, sizeof(*firq));
+}
+
+static int
+bcm2835_icu_intr(void *priv)
+{
+       struct bcm2835icu_irq *firq = priv;
+       struct bcm2835icu_irqhandler *firqh;
+       int handled = 0;
+
+       TAILQ_FOREACH(firqh, &firq->intr_handlers, ih_next) {
+               handled |= firqh->ih_fn(firqh->ih_arg);
+       }
+
+       return handled;
 }
 
 static bool



Home | Main Index | Thread Index | Old Index