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).
This commit is contained in:
thorpej 2019-11-28 01:08:06 +00:00
parent fa69ba79e0
commit e0e714b12b
1 changed files with 110 additions and 5 deletions

View File

@ -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_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