Module Name: src Committed By: thorpej Date: Sun Feb 16 20:29:36 UTC 2020
Modified Files: src/sys/arch/arm/sunxi: sunxi_nmi.c Log Message: Provide a back-end for fdt_intr_mask() / fdt_intr_unmask(). To generate a diff of this commit: cvs rdiff -u -r1.4 -r1.5 src/sys/arch/arm/sunxi/sunxi_nmi.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/sunxi/sunxi_nmi.c diff -u src/sys/arch/arm/sunxi/sunxi_nmi.c:1.4 src/sys/arch/arm/sunxi/sunxi_nmi.c:1.5 --- src/sys/arch/arm/sunxi/sunxi_nmi.c:1.4 Tue Jan 7 10:20:07 2020 +++ src/sys/arch/arm/sunxi/sunxi_nmi.c Sun Feb 16 20:29:36 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: sunxi_nmi.c,v 1.4 2020/01/07 10:20:07 skrll Exp $ */ +/* $NetBSD: sunxi_nmi.c,v 1.5 2020/02/16 20:29:36 thorpej Exp $ */ /*- * Copyright (c) 2018 Jared McNeill <jmcne...@invisible.ca> @@ -29,7 +29,7 @@ #define _INTR_PRIVATE #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sunxi_nmi.c,v 1.4 2020/01/07 10:20:07 skrll Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sunxi_nmi.c,v 1.5 2020/02/16 20:29:36 thorpej Exp $"); #include <sys/param.h> #include <sys/bus.h> @@ -37,6 +37,8 @@ __KERNEL_RCSID(0, "$NetBSD: sunxi_nmi.c, #include <sys/intr.h> #include <sys/kernel.h> #include <sys/systm.h> +#include <sys/atomic.h> +#include <sys/mutex.h> #include <sys/lwp.h> #include <dev/fdt/fdtvar.h> @@ -99,10 +101,12 @@ struct sunxi_nmi_softc { bus_space_handle_t sc_bsh; int sc_phandle; + kmutex_t sc_intr_lock; + const struct sunxi_nmi_config *sc_config; - int (*sc_func)(void *); - void *sc_arg; + struct intrsource sc_is; + void *sc_ih; }; #define NMI_READ(sc, reg) \ @@ -148,11 +152,17 @@ static int sunxi_nmi_intr(void *priv) { struct sunxi_nmi_softc * const sc = priv; + int (*func)(void *); int rv = 0; - if (sc->sc_func) - rv = sc->sc_func(sc->sc_arg); - + func = atomic_load_acquire(&sc->sc_is.is_func); + if (func) + rv = func(sc->sc_is.is_arg); + + /* + * We don't serialize access to this register because we're the + * only thing fiddling wth it. + */ sunxi_nmi_irq_ack(sc); return rv; @@ -164,19 +174,13 @@ sunxi_nmi_fdt_establish(device_t dev, u_ { struct sunxi_nmi_softc * const sc = device_private(dev); u_int irq_type; + int ist; /* 1st cell is the interrupt number */ const u_int irq = be32toh(specifier[0]); /* 2nd cell is polarity */ const u_int pol = be32toh(specifier[1]); - if (sc->sc_func != NULL) { -#ifdef DIAGNOSTIC - device_printf(dev, "%s in use\n", sc->sc_config->name); -#endif - return NULL; - } - if (irq != 0) { #ifdef DIAGNOSTIC device_printf(dev, "IRQ %u is invalid\n", irq); @@ -187,42 +191,100 @@ sunxi_nmi_fdt_establish(device_t dev, u_ switch (pol & 0x7) { case 1: /* IRQ_TYPE_EDGE_RISING */ irq_type = NMI_CTRL_IRQ_HIGH_EDGE; + ist = IST_EDGE; break; case 2: /* IRQ_TYPE_EDGE_FALLING */ irq_type = NMI_CTRL_IRQ_LOW_EDGE; + ist = IST_EDGE; break; case 3: /* IRQ_TYPE_LEVEL_HIGH */ irq_type = NMI_CTRL_IRQ_HIGH_LEVEL; + ist = IST_LEVEL; break; case 4: /* IRQ_TYPE_LEVEL_LOW */ irq_type = NMI_CTRL_IRQ_LOW_LEVEL; + ist = IST_LEVEL; break; default: irq_type = NMI_CTRL_IRQ_LOW_LEVEL; + ist = IST_LEVEL; break; } - sc->sc_func = func; - sc->sc_arg = arg; + mutex_enter(&sc->sc_intr_lock); + + if (atomic_load_relaxed(&sc->sc_is.is_func) != NULL) { + mutex_exit(&sc->sc_intr_lock); +#ifdef DIAGNOSTIC + device_printf(dev, "%s in use\n", sc->sc_config->name); +#endif + return NULL; + } + + sc->sc_is.is_arg = arg; + atomic_store_release(&sc->sc_is.is_func, func); + + sc->sc_is.is_type = ist; + sc->sc_is.is_ipl = ipl; + sc->sc_is.is_mpsafe = (flags & FDT_INTR_MPSAFE) ? true : false; + + mutex_exit(&sc->sc_intr_lock); + + sc->sc_ih = fdtbus_intr_establish(sc->sc_phandle, 0, ipl, flags, + sunxi_nmi_intr, sc); + mutex_enter(&sc->sc_intr_lock); sunxi_nmi_irq_set_type(sc, irq_type); sunxi_nmi_irq_enable(sc, true); + mutex_exit(&sc->sc_intr_lock); - return fdtbus_intr_establish(sc->sc_phandle, 0, ipl, flags, - sunxi_nmi_intr, sc); + return &sc->sc_is; +} + +static void +sunxi_nmi_fdt_mask(device_t dev, void *ih __unused) +{ + struct sunxi_nmi_softc * const sc = device_private(dev); + + mutex_enter(&sc->sc_intr_lock); + if (sc->sc_is.is_mask_count++ == 0) { + sunxi_nmi_irq_enable(sc, false); + } + mutex_exit(&sc->sc_intr_lock); +} + +static void +sunxi_nmi_fdt_unmask(device_t dev, void *ih __unused) +{ + struct sunxi_nmi_softc * const sc = device_private(dev); + + mutex_enter(&sc->sc_intr_lock); + if (sc->sc_is.is_mask_count-- == 1) { + sunxi_nmi_irq_enable(sc, true); + } + mutex_exit(&sc->sc_intr_lock); } static void sunxi_nmi_fdt_disestablish(device_t dev, void *ih) { struct sunxi_nmi_softc * const sc = device_private(dev); + struct intrsource * const is = ih; + + KASSERT(is == &sc->sc_is); + mutex_enter(&sc->sc_intr_lock); sunxi_nmi_irq_enable(sc, false); + is->is_mask_count = 0; + mutex_exit(&sc->sc_intr_lock); - fdtbus_intr_disestablish(sc->sc_phandle, ih); + fdtbus_intr_disestablish(sc->sc_phandle, sc->sc_ih); + sc->sc_ih = NULL; - sc->sc_func = NULL; - sc->sc_arg = NULL; + mutex_enter(&sc->sc_intr_lock); + is->is_arg = NULL; + is->is_func = NULL; + mutex_exit(&sc->sc_intr_lock); } static bool @@ -239,6 +301,8 @@ static const struct fdtbus_interrupt_con .establish = sunxi_nmi_fdt_establish, .disestablish = sunxi_nmi_fdt_disestablish, .intrstr = sunxi_nmi_fdt_intrstr, + .mask = sunxi_nmi_fdt_mask, + .unmask = sunxi_nmi_fdt_unmask, }; static int @@ -276,6 +340,18 @@ sunxi_nmi_attach(device_t parent, device aprint_naive("\n"); aprint_normal(": %s\n", sc->sc_config->name); + mutex_init(&sc->sc_intr_lock, MUTEX_SPIN, IPL_HIGH); + + /* + * Normally it's assumed that an intrsource can be passed to + * interrupt_distribute(). We're providing our own that's + * independent of our parent PIC, but because we will leave + * the intrsource::is_pic field NULL, the right thing + * (i.e. nothing) will happen in interrupt_distribute(). + */ + snprintf(sc->sc_is.is_source, sizeof(sc->sc_is.is_source), + "%s", sc->sc_config->name); + sunxi_nmi_irq_enable(sc, false); sunxi_nmi_irq_ack(sc);