diff --git a/sys/arch/arm/broadcom/bcm2835_intr.c b/sys/arch/arm/broadcom/bcm2835_intr.c index bd74d01e807a..d7150c3454fc 100644 --- a/sys/arch/arm/broadcom/bcm2835_intr.c +++ b/sys/arch/arm/broadcom/bcm2835_intr.c @@ -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 -__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_establish(device_t, u_int *, int, int, 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 @@ struct bcm2836mp_interrupt { 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 @@ static void * 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 @@ bcm2835_icu_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags, 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