[BACK]Return to sunxi_nmi.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / arch / arm / sunxi

Annotation of src/sys/arch/arm/sunxi/sunxi_nmi.c, Revision 1.9

1.9     ! thorpej     1: /* $NetBSD: sunxi_nmi.c,v 1.8 2021/01/18 02:35:49 thorpej Exp $ */
1.1       jmcneill    2:
                      3: /*-
                      4:  * Copyright (c) 2018 Jared McNeill <jmcneill@invisible.ca>
                      5:  * All rights reserved.
                      6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
                     15:  *
                     16:  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
                     17:  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
                     18:  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
                     19:  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
                     20:  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
                     21:  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
                     22:  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
                     23:  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
                     24:  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     25:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     26:  * SUCH DAMAGE.
                     27:  */
                     28:
                     29: #define        _INTR_PRIVATE
                     30:
                     31: #include <sys/cdefs.h>
1.9     ! thorpej    32: __KERNEL_RCSID(0, "$NetBSD: sunxi_nmi.c,v 1.8 2021/01/18 02:35:49 thorpej Exp $");
1.1       jmcneill   33:
                     34: #include <sys/param.h>
                     35: #include <sys/bus.h>
                     36: #include <sys/device.h>
                     37: #include <sys/intr.h>
1.3       skrll      38: #include <sys/kernel.h>
1.1       jmcneill   39: #include <sys/systm.h>
1.5       thorpej    40: #include <sys/atomic.h>
                     41: #include <sys/mutex.h>
1.4       skrll      42: #include <sys/lwp.h>
1.1       jmcneill   43:
                     44: #include <dev/fdt/fdtvar.h>
                     45:
                     46: #include <arm/cpu.h>
                     47: #include <arm/pic/picvar.h>
                     48: #include <arm/fdt/arm_fdtvar.h>
                     49:
                     50: /* ctrl_reg */
                     51: #define        NMI_CTRL_IRQ_LOW_LEVEL  0
                     52: #define        NMI_CTRL_IRQ_LOW_EDGE   1
                     53: #define        NMI_CTRL_IRQ_HIGH_LEVEL 2
                     54: #define        NMI_CTRL_IRQ_HIGH_EDGE  3
                     55: #define        NMI_CTRL_IRQ_TYPE       __BITS(1,0)
                     56:
                     57: /* pend_reg */
                     58: #define        NMI_PEND_IRQ_ACK        __BIT(0)
                     59:
                     60: /* enable_reg */
                     61: #define        NMI_ENABLE_IRQEN        __BIT(0)
                     62:
                     63: struct sunxi_nmi_config {
                     64:        const char *    name;
                     65:        bus_size_t      ctrl_reg;
                     66:        bus_size_t      pend_reg;
                     67:        bus_size_t      enable_reg;
                     68: };
                     69:
                     70: static const struct sunxi_nmi_config sun7i_a20_sc_nmi_config = {
                     71:        .name = "NMI",
                     72:        .ctrl_reg = 0x00,
                     73:        .pend_reg = 0x04,
                     74:        .enable_reg = 0x08,
                     75: };
                     76:
                     77: static const struct sunxi_nmi_config sun6i_a31_r_intc_config = {
                     78:        .name = "R_INTC",
                     79:        .ctrl_reg = 0x0c,
                     80:        .pend_reg = 0x10,
                     81:        .enable_reg = 0x40,
                     82: };
                     83:
1.2       jmcneill   84: static const struct sunxi_nmi_config sun9i_a80_nmi_config = {
                     85:        .name = "NMI",
                     86:        .ctrl_reg = 0x00,
                     87:        .pend_reg = 0x04,
                     88:        .enable_reg = 0x08,
                     89: };
                     90:
1.8       thorpej    91: static const struct device_compatible_entry compat_data[] = {
                     92:        { .compat = "allwinner,sun7i-a20-sc-nmi",
                     93:          .data = &sun7i_a20_sc_nmi_config },
                     94:        { .compat = "allwinner,sun6i-a31-r-intc",
                     95:          .data = &sun6i_a31_r_intc_config },
                     96:        { .compat = "allwinner,sun9i-a80-nmi",
                     97:          .data = &sun9i_a80_nmi_config },
                     98:
1.9     ! thorpej    99:        { }
1.1       jmcneill  100: };
                    101:
                    102: struct sunxi_nmi_softc {
                    103:        device_t sc_dev;
                    104:        bus_space_tag_t sc_bst;
                    105:        bus_space_handle_t sc_bsh;
                    106:        int sc_phandle;
                    107:
1.5       thorpej   108:        kmutex_t sc_intr_lock;
                    109:
1.1       jmcneill  110:        const struct sunxi_nmi_config *sc_config;
                    111:
1.5       thorpej   112:        struct intrsource sc_is;
                    113:        void    *sc_ih;
1.1       jmcneill  114: };
                    115:
                    116: #define NMI_READ(sc, reg) \
                    117:        bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh, (reg))
                    118: #define NMI_WRITE(sc, reg, val) \
                    119:        bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
                    120:
                    121: static void
                    122: sunxi_nmi_irq_ack(struct sunxi_nmi_softc *sc)
                    123: {
                    124:        uint32_t val;
                    125:
                    126:        val = NMI_READ(sc, sc->sc_config->pend_reg);
                    127:        val |= NMI_PEND_IRQ_ACK;
                    128:        NMI_WRITE(sc, sc->sc_config->pend_reg, val);
                    129: }
                    130:
                    131: static void
                    132: sunxi_nmi_irq_enable(struct sunxi_nmi_softc *sc, bool on)
                    133: {
                    134:        uint32_t val;
                    135:
                    136:        val = NMI_READ(sc, sc->sc_config->enable_reg);
                    137:        if (on)
                    138:                val |= NMI_ENABLE_IRQEN;
                    139:        else
                    140:                val &= ~NMI_ENABLE_IRQEN;
                    141:        NMI_WRITE(sc, sc->sc_config->enable_reg, val);
                    142: }
                    143:
                    144: static void
                    145: sunxi_nmi_irq_set_type(struct sunxi_nmi_softc *sc, u_int irq_type)
                    146: {
                    147:        uint32_t val;
                    148:
                    149:        val = NMI_READ(sc, sc->sc_config->ctrl_reg);
                    150:        val &= ~NMI_CTRL_IRQ_TYPE;
                    151:        val |= __SHIFTIN(irq_type, NMI_CTRL_IRQ_TYPE);
                    152:        NMI_WRITE(sc, sc->sc_config->ctrl_reg, val);
                    153: }
                    154:
                    155: static int
                    156: sunxi_nmi_intr(void *priv)
                    157: {
                    158:        struct sunxi_nmi_softc * const sc = priv;
1.5       thorpej   159:        int (*func)(void *);
1.1       jmcneill  160:        int rv = 0;
                    161:
1.5       thorpej   162:        func = atomic_load_acquire(&sc->sc_is.is_func);
                    163:        if (func)
                    164:                rv = func(sc->sc_is.is_arg);
                    165:
                    166:        /*
                    167:         * We don't serialize access to this register because we're the
                    168:         * only thing fiddling wth it.
                    169:         */
1.1       jmcneill  170:        sunxi_nmi_irq_ack(sc);
                    171:
                    172:        return rv;
                    173: }
                    174:
                    175: static void *
                    176: sunxi_nmi_fdt_establish(device_t dev, u_int *specifier, int ipl, int flags,
1.6       jmcneill  177:     int (*func)(void *), void *arg, const char *xname)
1.1       jmcneill  178: {
                    179:        struct sunxi_nmi_softc * const sc = device_private(dev);
                    180:        u_int irq_type;
1.5       thorpej   181:        int ist;
1.1       jmcneill  182:
                    183:        /* 1st cell is the interrupt number */
                    184:        const u_int irq = be32toh(specifier[0]);
                    185:        /* 2nd cell is polarity */
                    186:        const u_int pol = be32toh(specifier[1]);
                    187:
                    188:        if (irq != 0) {
                    189: #ifdef DIAGNOSTIC
                    190:                device_printf(dev, "IRQ %u is invalid\n", irq);
                    191: #endif
                    192:                return NULL;
                    193:        }
                    194:
                    195:        switch (pol & 0x7) {
                    196:        case 1: /* IRQ_TYPE_EDGE_RISING */
                    197:                irq_type = NMI_CTRL_IRQ_HIGH_EDGE;
1.5       thorpej   198:                ist = IST_EDGE;
1.1       jmcneill  199:                break;
                    200:        case 2: /* IRQ_TYPE_EDGE_FALLING */
                    201:                irq_type = NMI_CTRL_IRQ_LOW_EDGE;
1.5       thorpej   202:                ist = IST_EDGE;
1.1       jmcneill  203:                break;
                    204:        case 3: /* IRQ_TYPE_LEVEL_HIGH */
                    205:                irq_type = NMI_CTRL_IRQ_HIGH_LEVEL;
1.5       thorpej   206:                ist = IST_LEVEL;
1.1       jmcneill  207:                break;
                    208:        case 4: /* IRQ_TYPE_LEVEL_LOW */
                    209:                irq_type = NMI_CTRL_IRQ_LOW_LEVEL;
1.5       thorpej   210:                ist = IST_LEVEL;
1.1       jmcneill  211:                break;
                    212:        default:
                    213:                irq_type = NMI_CTRL_IRQ_LOW_LEVEL;
1.5       thorpej   214:                ist = IST_LEVEL;
1.1       jmcneill  215:                break;
                    216:        }
                    217:
1.5       thorpej   218:        mutex_enter(&sc->sc_intr_lock);
                    219:
                    220:        if (atomic_load_relaxed(&sc->sc_is.is_func) != NULL) {
                    221:                mutex_exit(&sc->sc_intr_lock);
                    222: #ifdef DIAGNOSTIC
                    223:                device_printf(dev, "%s in use\n", sc->sc_config->name);
                    224: #endif
                    225:                return NULL;
                    226:        }
                    227:
                    228:        sc->sc_is.is_arg = arg;
                    229:        atomic_store_release(&sc->sc_is.is_func, func);
                    230:
                    231:        sc->sc_is.is_type = ist;
                    232:        sc->sc_is.is_ipl = ipl;
                    233:        sc->sc_is.is_mpsafe = (flags & FDT_INTR_MPSAFE) ? true : false;
                    234:
                    235:        mutex_exit(&sc->sc_intr_lock);
                    236:
1.7       jmcneill  237:        sc->sc_ih = fdtbus_intr_establish_xname(sc->sc_phandle, 0, ipl, flags,
                    238:            sunxi_nmi_intr, sc, device_xname(dev));
1.1       jmcneill  239:
1.5       thorpej   240:        mutex_enter(&sc->sc_intr_lock);
1.1       jmcneill  241:        sunxi_nmi_irq_set_type(sc, irq_type);
                    242:        sunxi_nmi_irq_enable(sc, true);
1.5       thorpej   243:        mutex_exit(&sc->sc_intr_lock);
1.1       jmcneill  244:
1.5       thorpej   245:        return &sc->sc_is;
                    246: }
                    247:
                    248: static void
                    249: sunxi_nmi_fdt_mask(device_t dev, void *ih __unused)
                    250: {
                    251:        struct sunxi_nmi_softc * const sc = device_private(dev);
                    252:
                    253:        mutex_enter(&sc->sc_intr_lock);
                    254:        if (sc->sc_is.is_mask_count++ == 0) {
                    255:                sunxi_nmi_irq_enable(sc, false);
                    256:        }
                    257:        mutex_exit(&sc->sc_intr_lock);
                    258: }
                    259:
                    260: static void
                    261: sunxi_nmi_fdt_unmask(device_t dev, void *ih __unused)
                    262: {
                    263:        struct sunxi_nmi_softc * const sc = device_private(dev);
                    264:
                    265:        mutex_enter(&sc->sc_intr_lock);
                    266:        if (sc->sc_is.is_mask_count-- == 1) {
                    267:                sunxi_nmi_irq_enable(sc, true);
                    268:        }
                    269:        mutex_exit(&sc->sc_intr_lock);
1.1       jmcneill  270: }
                    271:
                    272: static void
                    273: sunxi_nmi_fdt_disestablish(device_t dev, void *ih)
                    274: {
                    275:        struct sunxi_nmi_softc * const sc = device_private(dev);
1.5       thorpej   276:        struct intrsource * const is = ih;
                    277:
                    278:        KASSERT(is == &sc->sc_is);
1.1       jmcneill  279:
1.5       thorpej   280:        mutex_enter(&sc->sc_intr_lock);
1.1       jmcneill  281:        sunxi_nmi_irq_enable(sc, false);
1.5       thorpej   282:        is->is_mask_count = 0;
                    283:        mutex_exit(&sc->sc_intr_lock);
1.1       jmcneill  284:
1.5       thorpej   285:        fdtbus_intr_disestablish(sc->sc_phandle, sc->sc_ih);
                    286:        sc->sc_ih = NULL;
1.1       jmcneill  287:
1.5       thorpej   288:        mutex_enter(&sc->sc_intr_lock);
                    289:        is->is_arg = NULL;
                    290:        is->is_func = NULL;
                    291:        mutex_exit(&sc->sc_intr_lock);
1.1       jmcneill  292: }
                    293:
                    294: static bool
                    295: sunxi_nmi_fdt_intrstr(device_t dev, u_int *specifier, char *buf, size_t buflen)
                    296: {
                    297:        struct sunxi_nmi_softc * const sc = device_private(dev);
                    298:
                    299:        snprintf(buf, buflen, "%s", sc->sc_config->name);
                    300:
                    301:        return true;
                    302: }
                    303:
                    304: static const struct fdtbus_interrupt_controller_func sunxi_nmi_fdt_funcs = {
                    305:        .establish = sunxi_nmi_fdt_establish,
                    306:        .disestablish = sunxi_nmi_fdt_disestablish,
                    307:        .intrstr = sunxi_nmi_fdt_intrstr,
1.5       thorpej   308:        .mask = sunxi_nmi_fdt_mask,
                    309:        .unmask = sunxi_nmi_fdt_unmask,
1.1       jmcneill  310: };
                    311:
                    312: static int
                    313: sunxi_nmi_match(device_t parent, cfdata_t cf, void *aux)
                    314: {
                    315:        struct fdt_attach_args * const faa = aux;
                    316:
                    317:        return of_match_compat_data(faa->faa_phandle, compat_data);
                    318: }
                    319:
                    320: static void
                    321: sunxi_nmi_attach(device_t parent, device_t self, void *aux)
                    322: {
                    323:        struct sunxi_nmi_softc * const sc = device_private(self);
                    324:        struct fdt_attach_args * const faa = aux;
                    325:        const int phandle = faa->faa_phandle;
                    326:        bus_addr_t addr;
                    327:        bus_size_t size;
                    328:        int error;
                    329:
                    330:        if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
                    331:                aprint_error(": couldn't get registers\n");
                    332:                return;
                    333:        }
                    334:
                    335:        sc->sc_dev = self;
                    336:        sc->sc_phandle = phandle;
1.8       thorpej   337:        sc->sc_config = of_search_compatible(phandle, compat_data)->data;
1.1       jmcneill  338:        sc->sc_bst = faa->faa_bst;
                    339:        if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh) != 0) {
                    340:                aprint_error(": couldn't map registers\n");
                    341:                return;
                    342:        }
                    343:
                    344:        aprint_naive("\n");
                    345:        aprint_normal(": %s\n", sc->sc_config->name);
                    346:
1.5       thorpej   347:        mutex_init(&sc->sc_intr_lock, MUTEX_SPIN, IPL_HIGH);
                    348:
                    349:        /*
                    350:         * Normally it's assumed that an intrsource can be passed to
                    351:         * interrupt_distribute().  We're providing our own that's
                    352:         * independent of our parent PIC, but because we will leave
                    353:         * the intrsource::is_pic field NULL, the right thing
                    354:         * (i.e. nothing) will happen in interrupt_distribute().
                    355:         */
                    356:        snprintf(sc->sc_is.is_source, sizeof(sc->sc_is.is_source),
                    357:                 "%s", sc->sc_config->name);
                    358:
1.1       jmcneill  359:        sunxi_nmi_irq_enable(sc, false);
                    360:        sunxi_nmi_irq_ack(sc);
                    361:
                    362:        error = fdtbus_register_interrupt_controller(self, phandle,
                    363:            &sunxi_nmi_fdt_funcs);
                    364:        if (error) {
                    365:                aprint_error_dev(self, "couldn't register with fdtbus: %d\n",
                    366:                    error);
                    367:                return;
                    368:        }
                    369: }
                    370:
                    371: CFATTACH_DECL_NEW(sunxi_nmi, sizeof(struct sunxi_nmi_softc),
                    372:        sunxi_nmi_match, sunxi_nmi_attach, NULL, NULL);

CVSweb <webmaster@jp.NetBSD.org>