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:
parent
fa69ba79e0
commit
e0e714b12b
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue