[BACK]Return to nct.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / sys / dev / isa

Annotation of src/sys/dev/isa/nct.c, Revision 1.4.8.1

1.4.8.1 ! thorpej     1: /*     $NetBSD: nct.c,v 1.4 2021/04/24 23:36:55 thorpej Exp $  */
1.1       martin      2:
                      3: /*-
1.3       ad          4:  * Copyright (c) 2019, 2020 The NetBSD Foundation, Inc.
1.1       martin      5:  * All rights reserved.
                      6:  *
                      7:  * This code is derived from software contributed to The NetBSD Foundation
                      8:  * by Andrew Doran.
                      9:  *
                     10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
                     20:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
                     21:  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
                     22:  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
                     23:  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
                     24:  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
                     25:  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
                     26:  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
                     27:  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
                     28:  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
                     29:  * POSSIBILITY OF SUCH DAMAGE.
                     30:  */
                     31:
                     32: /*
                     33:  * Nuvoton NCT5104D
                     34:  *
                     35:  * - GPIO: full support.
                     36:  * - Watchdog: no support.  Watchdog uses GPIO pins.
                     37:  * - UARTS: handled by com driver.  3rd & 4th UARTs use GPIO pins.
                     38:  *
                     39:  * If asked to probe with a wildcard address, we'll only do so if known to
1.3       ad         40:  * be running on a PC Engines system.  Probe is invasive.
1.1       martin     41:  *
                     42:  * Register access on Super I/O chips typically involves one or two levels
                     43:  * of indirection, so we try hard to avoid needless register access.
                     44:  */
                     45:
                     46: #include <sys/cdefs.h>
1.4.8.1 ! thorpej    47: __KERNEL_RCSID(0, "$NetBSD: nct.c,v 1.4 2021/04/24 23:36:55 thorpej Exp $");
1.1       martin     48:
                     49: #include <sys/param.h>
                     50: #include <sys/systm.h>
                     51: #include <sys/types.h>
                     52: #include <sys/device.h>
                     53: #include <sys/module.h>
                     54: #include <sys/bus.h>
                     55: #include <sys/gpio.h>
                     56:
                     57: #include <machine/autoconf.h>
                     58:
                     59: #include <dev/isa/isavar.h>
                     60:
                     61: #include <dev/gpio/gpiovar.h>
                     62:
                     63: /*
                     64:  * Hardware interface definition (enough for GPIO only).
                     65:  */
                     66:
                     67: /* I/O basics */
                     68: #define        NCT_IOBASE_A            0x2e
                     69: #define        NCT_IOBASE_B            0x4e
                     70: #define        NCT_IOSIZE              2
                     71: #define        NCT_CHIP_ID_1           0x1061
1.3       ad         72: #define        NCT_CHIP_ID_2           0xc452  /* PC Engines APU */
1.1       martin     73: #define        NCT_NUM_PINS            17
                     74:
                     75: /* Enable/disable keys */
                     76: #define NCT_KEY_UNLOCK         0x87
                     77: #define NCT_KEY_LOCK           0xaa
                     78:
                     79: /* I/O ports */
                     80: #define        NCT_PORT_SELECT         0
                     81: #define        NCT_PORT_DATA           1
                     82:
                     83: /* Global registers */
                     84: #define        GD_DEVSEL               0x0007  /* logical device select */
                     85: #define        GD_MULTIFUN             0x001c  /* multi function selection */
                     86: #define                GD_MULTIFUN_GPIO1       0x04    /* clr: gpio1 available */
                     87: #define                GD_MULTIFUN_GPIO0       0x08    /* clr: gpio0 available */
                     88: #define                GD_MULTIFUN_GPIO67      0x10    /* set: gpio67 available */
                     89: #define        GD_GLOBOPT              0x0027  /* global option */
                     90: #define        GD_GLOBOPT_GPIO67       0x04    /* clr: gpio67 available */
                     91: #define        GD_ID_HIGH              0x0020  /* ID high byte */
                     92: #define        GD_ID_LOW               0x0021  /* ID low byte */
                     93:
                     94: /* Logical device 7 */
                     95: #define        LD7_ENABLE              0x0730  /* GPIO function enable */
                     96: #define                LD7_ENABLE_GPIO0        0x01
                     97: #define                LD7_ENABLE_GPIO1        0x02
                     98: #define                LD7_ENABLE_GPIO67       0x40
                     99: #define        LD7_GPIO0_DIRECTION     0x07e0  /* clr for output, set for input */
                    100: #define        LD7_GPIO0_DATA          0x07e1  /* current status */
                    101: #define        LD7_GPIO0_INVERSION     0x07e2  /* set to invert i/o */
                    102: #define        LD7_GPIO0_STATUS        0x07e3  /* edge detect, reading clears */
                    103: #define        LD7_GPIO1_DIRECTION     0x07e4  /* clr for output, set for input */
                    104: #define        LD7_GPIO1_DATA          0x07e5  /* current status */
                    105: #define        LD7_GPIO1_INVERSION     0x07e6  /* set to invert i/o */
                    106: #define        LD7_GPIO1_STATUS        0x07e7  /* edge detect, reading clears */
                    107: #define        LD7_GPIO67_DIRECTION    0x07f8  /* clr for output, set for input */
                    108: #define        LD7_GPIO67_DATA         0x07f9  /* current status */
                    109: #define        LD7_GPIO67_INVERSION    0x07fa  /* set to invert i/o */
                    110: #define        LD7_GPIO67_STATUS       0x07fb  /* edge detect, reading clears */
                    111:
                    112: /* Logical device 8 */
                    113: #define        LD8_DEVCFG              0x0830  /* WDT/GPIO device config */
                    114: #define        LD8_GPIO0_MULTIFUNC     0x08e0  /* clr: gpio, set: pin unusable */
                    115: #define        LD8_GPIO1_MULTIFUNC     0x08e1  /* clr: gpio, set: pin unusable */
                    116: #define        LD8_GPIO67_MULTIFUNC    0x08e7  /* clr: gpio, set: pin unusable */
                    117:
                    118: /* Logical device 10 */
                    119: #define        LDA_UARTC_ENABLE        0x0a30  /* bit 0: UARTC active */
                    120:
                    121: /* Logical device 11 */
                    122: #define        LDB_UARTD_ENABLE        0x0b30  /* bit 0: UARTD active */
                    123:
                    124: /* Logical device 15 */
                    125: #define        LDF_GPIO0_OUTMODE       0x0fe0  /* clr: push/pull, set: open drain */
                    126: #define        LDF_GPIO1_OUTMODE       0x0fe1  /* clr: push/pull, set: open drain */
                    127: #define        LDF_GPIO67_OUTMODE      0x0fe6  /* clr: push/pull, set: open drain */
                    128:
                    129: /*
                    130:  * Internal GPIO bank description, including register addresses and cached
                    131:  * register content.
                    132:  */
                    133: struct nct_bank {
                    134:        /* Pin descriptions */
                    135:        u_int8_t        nb_firstpin;
                    136:        u_int8_t        nb_numpins;
                    137:        u_int8_t        nb_enabled;
                    138:
                    139:        /* Cached values */
                    140:        u_int8_t        nb_val_dir;
                    141:        u_int8_t        nb_val_inv;
                    142:        u_int8_t        nb_val_mode;
                    143:
                    144:        /* Register addresses */
                    145:        u_int16_t       nb_reg_dir;
                    146:        u_int16_t       nb_reg_data;
                    147:        u_int16_t       nb_reg_inv;
                    148:        u_int16_t       nb_reg_stat;
                    149:        u_int16_t       nb_reg_mode;
                    150: };
                    151:
                    152: /*
                    153:  * Driver instance.
                    154:  */
                    155: struct nct_softc {
                    156:        device_t                sc_dev;                 /* MI device */
                    157:        bus_space_tag_t         sc_iot;                 /* I/O tag */
                    158:        bus_space_handle_t      sc_ioh;                 /* I/O handle */
                    159:        struct gpio_chipset_tag  sc_gc;                 /* GPIO tag */
                    160:        gpio_pin_t              sc_pins[NCT_NUM_PINS];  /* GPIO pin descr. */
                    161:
                    162:        /* Access to the remaining members is covered by sc_lock. */
                    163:        kmutex_t                sc_lock;                /* Serialization */
                    164:        int                     sc_curdev;              /* Cur. logical dev */
                    165:        int                     sc_curreg;              /* Cur. register */
                    166:        struct nct_bank         sc_bank[3];             /* Bank descriptions */
                    167: };
                    168:
                    169: static void    nct_attach(device_t, device_t, void *);
                    170: static int     nct_detach(device_t, int);
                    171: static void    nct_gpio_ctl(void *, int, int);
                    172: static int     nct_gpio_read(void *, int);
                    173: static void    nct_gpio_write(void *, int, int);
                    174: static int     nct_match(device_t, cfdata_t , void *);
                    175: static u_int8_t        nct_rd(struct nct_softc *, int);
                    176: static struct  nct_bank *nct_sel(struct nct_softc *, int, u_int8_t *);
                    177: static void    nct_wr(struct nct_softc *, int, u_int8_t);
                    178:
                    179: static inline void
                    180: nct_outb(struct nct_softc *sc, int reg, u_int8_t data)
                    181: {
                    182:
                    183:        bus_space_write_1(sc->sc_iot, sc->sc_ioh, reg, data);
                    184: }
                    185:
                    186: static inline u_int8_t
                    187: nct_inb(struct nct_softc *sc, int reg)
                    188: {
                    189:
                    190:        return bus_space_read_1(sc->sc_iot, sc->sc_ioh, reg);
                    191: }
                    192:
                    193: CFATTACH_DECL_NEW(nct,
                    194:                  sizeof(struct nct_softc),
                    195:                  nct_match,
                    196:                  nct_attach,
                    197:                  nct_detach,
                    198:                  NULL);
                    199:
                    200: MODULE(MODULE_CLASS_DRIVER, nct, "gpio");
                    201:
                    202: /*
                    203:  * Module linkage.
                    204:  */
                    205: #ifdef _MODULE
                    206: #include "ioconf.c"
                    207: #endif
                    208:
                    209: static int
                    210: nct_modcmd(modcmd_t cmd, void *priv)
                    211: {
                    212:        int error = 0;
                    213:
                    214:        switch (cmd) {
                    215:        case MODULE_CMD_INIT:
                    216: #ifdef _MODULE
                    217:                error = config_init_component(cfdriver_ioconf_nct,
                    218:                    cfattach_ioconf_nct, cfdata_ioconf_nct);
                    219: #endif
                    220:                return error;
                    221:        case MODULE_CMD_FINI:
                    222: #ifdef _MODULE
                    223:                error = config_fini_component(cfdriver_ioconf_nct,
                    224:                    cfattach_ioconf_nct, cfdata_ioconf_nct);
                    225: #endif
                    226:                return error;
                    227:        default:
                    228:                return ENOTTY;
                    229:        }
                    230: }
                    231:
                    232: /*
                    233:  * Probe for device.
                    234:  */
                    235: static int
                    236: nct_match(device_t parent, cfdata_t match, void *aux)
                    237: {
                    238:        int ioaddrs[2] = { 0x2e, 0x4e };
                    239:        struct isa_attach_args *ia = aux;
                    240:        bus_space_handle_t ioh;
                    241:        int nioaddr, i;
                    242:        u_int8_t low, high;
                    243:        u_int16_t id;
1.3       ad        244:        const char *vendor;
1.1       martin    245:
                    246:        /*
                    247:         * Allow override of I/O base address.  If no I/O base address is
1.3       ad        248:         * provided, proceed to probe if running on a PC Engines system.
1.1       martin    249:         */
                    250:        if (ia->ia_nio > 0 && ia->ia_io[0].ir_addr != ISA_UNKNOWN_PORT) {
                    251:                ioaddrs[0] = ia->ia_io[0].ir_addr;
                    252:                nioaddr = 1;
                    253:        } else {
1.2       martin    254:                vendor = pmf_get_platform("system-vendor");
1.3       ad        255:                if (vendor != NULL && strstr(vendor, "PC Engines") != NULL) {
1.2       martin    256:                        nioaddr = __arraycount(ioaddrs);
                    257:                } else {
                    258:                        nioaddr = 0;
                    259:                }
1.1       martin    260:        }
                    261:
                    262:        /*
                    263:         * Probe at the selected addresses, if any.
                    264:         */
                    265:        for (i = 0; i < nioaddr; i++) {
                    266:                if (bus_space_map(ia->ia_iot, ioaddrs[i], NCT_IOSIZE, 0,
                    267:                    &ioh) != 0) {
                    268:                    continue;
                    269:                }
                    270:                 /* Unlock chip */
                    271:                bus_space_write_1(ia->ia_iot, ioh, NCT_PORT_SELECT,
                    272:                    NCT_KEY_UNLOCK);
                    273:                bus_space_write_1(ia->ia_iot, ioh, NCT_PORT_SELECT,
                    274:                    NCT_KEY_UNLOCK);
                    275:                /* Read ID */
                    276:                bus_space_write_1(ia->ia_iot, ioh, NCT_PORT_SELECT, GD_ID_LOW);
                    277:                low = bus_space_read_1(ia->ia_iot, ioh, NCT_PORT_DATA);
                    278:                bus_space_write_1(ia->ia_iot, ioh, NCT_PORT_SELECT, GD_ID_HIGH);
                    279:                high = bus_space_read_1(ia->ia_iot, ioh, NCT_PORT_DATA);
                    280:                id = (u_int16_t)low | ((u_int16_t)high << 8);
                    281:                bus_space_unmap(ia->ia_iot, ioh, NCT_IOSIZE);
                    282:                if (id == NCT_CHIP_ID_1 || id == NCT_CHIP_ID_2) {
                    283:                        ia->ia_nirq = 0;
                    284:                        ia->ia_ndrq = 0;
                    285:                        ia->ia_niomem = 0;
                    286:                        ia->ia_nio = 1;
                    287:                        ia->ia_io[0].ir_size = NCT_IOSIZE;
                    288:                        ia->ia_io[0].ir_addr = ioaddrs[i];
                    289:                        return 1;
                    290:                }
                    291:        }
                    292:        return 0;
                    293: }
                    294:
                    295: /*
                    296:  * Attach device instance.
                    297:  */
                    298: static void
                    299: nct_attach(device_t parent, device_t self, void *aux)
                    300: {
                    301:        struct nct_softc *sc = device_private(self);
                    302:        struct isa_attach_args *ia = aux;
                    303:        struct gpiobus_attach_args gba;
                    304:        struct nct_bank *nb;
                    305:        u_int8_t multifun, enable;
                    306:        int i, j;
                    307:
                    308:        /*
                    309:         * Set up register space and basics of our state.
                    310:         */
                    311:        if (bus_space_map(ia->ia_iot, ia->ia_io[0].ir_addr,
                    312:            ia->ia_io[0].ir_size, 0, &sc->sc_ioh) != 0) {
                    313:                aprint_normal(": can't map i/o space\n");
                    314:                return;
                    315:        }
                    316:        aprint_normal(": Nuvoton NCT5104D GPIO\n");
                    317:        sc->sc_dev = self;
                    318:        sc->sc_iot = ia->ia_iot;
                    319:        sc->sc_curdev = -1;
                    320:        sc->sc_curreg = -1;
                    321:
                    322:         /*
                    323:          * All pin access is funneled through a common, indirect register
                    324:          * interface.  The gpio framework doesn't serialize calls to our
                    325:          * access methods, so do it internally.  This is likely such a
                    326:          * common requirement that it should be factored out as is done for
                    327:          * audio devices, allowing the driver to specify the appropriate
                    328:          * locks.  Anyhow, acquire the lock immediately to pacify locking
                    329:          * assertions.
                    330:          */
                    331:         mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM);
                    332:        mutex_spin_enter(&sc->sc_lock);
                    333:
                    334:         /*
                    335:          * Disable watchdog timer and GPIO alternate I/O mapping.
                    336:          */
                    337:         nct_wr(sc, LD8_DEVCFG, 0);
                    338:
                    339:        /*
1.3       ad        340:         * The BIOS doesn't set things up the way we want.  Pfft.
                    341:         * Enable all GPIO0/GPIO1 pins.
1.1       martin    342:         */
                    343:        multifun = nct_rd(sc, GD_MULTIFUN);
1.3       ad        344:        nct_wr(sc, GD_MULTIFUN,
                    345:            multifun & ~(GD_MULTIFUN_GPIO0 | GD_MULTIFUN_GPIO1));
                    346:        nct_wr(sc, LDA_UARTC_ENABLE, 0);
                    347:        nct_wr(sc, LD8_GPIO0_MULTIFUNC, 0);
                    348:        nct_wr(sc, LDB_UARTD_ENABLE, 0);
                    349:        nct_wr(sc, LD8_GPIO1_MULTIFUNC, 0);
                    350:        multifun = nct_rd(sc, GD_MULTIFUN);
                    351:        enable = nct_rd(sc, LD7_ENABLE) | LD7_ENABLE_GPIO0 | LD7_ENABLE_GPIO1;
1.1       martin    352:
                    353:        nb = &sc->sc_bank[0];
                    354:        nb->nb_firstpin = 0;
                    355:        nb->nb_numpins = 8;
1.3       ad        356:        nb->nb_enabled = 0xff;
1.1       martin    357:        nb->nb_reg_dir = LD7_GPIO0_DIRECTION;
                    358:        nb->nb_reg_data = LD7_GPIO0_DATA;
                    359:        nb->nb_reg_inv = LD7_GPIO0_INVERSION;
                    360:        nb->nb_reg_stat = LD7_GPIO0_STATUS;
                    361:        nb->nb_reg_mode = LDF_GPIO0_OUTMODE;
1.3       ad        362:
1.1       martin    363:        nb = &sc->sc_bank[1];
                    364:        nb->nb_firstpin = 8;
                    365:        nb->nb_numpins = 8;
1.3       ad        366:        nb->nb_enabled = 0xff;
1.1       martin    367:        nb->nb_reg_dir = LD7_GPIO1_DIRECTION;
                    368:        nb->nb_reg_data = LD7_GPIO1_DATA;
                    369:        nb->nb_reg_inv = LD7_GPIO1_INVERSION;
                    370:        nb->nb_reg_stat = LD7_GPIO1_STATUS;
                    371:        nb->nb_reg_mode = LDF_GPIO1_OUTMODE;
                    372:
                    373:        nb = &sc->sc_bank[2];
                    374:        nb->nb_firstpin = 16;
                    375:        nb->nb_numpins = 1;
                    376:        nb->nb_reg_dir = LD7_GPIO67_DIRECTION;
                    377:        nb->nb_reg_data = LD7_GPIO67_DATA;
                    378:        nb->nb_reg_stat = LD7_GPIO67_STATUS;
                    379:        nb->nb_reg_mode = LDF_GPIO67_OUTMODE;
                    380:        if ((multifun & GD_MULTIFUN_GPIO67) != 0 &&
                    381:            (nct_rd(sc, GD_GLOBOPT) & GD_GLOBOPT_GPIO67) == 0) {
                    382:                nct_wr(sc, LD8_GPIO67_MULTIFUNC, 0);
                    383:                nb->nb_enabled = 0x01;
                    384:                enable |= LD7_ENABLE_GPIO67;
                    385:        } else {
                    386:                sc->sc_bank[2].nb_enabled = 0;
                    387:        }
                    388:
                    389:        /*
                    390:         * Display enabled pins and enable GPIO devices accordingly.
                    391:         */
                    392:        nct_wr(sc, LD7_ENABLE, enable);
                    393:        mutex_spin_exit(&sc->sc_lock);
                    394:
                    395:        aprint_normal_dev(self,
                    396:            "enabled pins: GPIO0(%02x) GPIO1(%02x) GPIO67(%01x)\n",
                    397:            (unsigned)sc->sc_bank[0].nb_enabled,
                    398:            (unsigned)sc->sc_bank[1].nb_enabled,
                    399:            (unsigned)sc->sc_bank[2].nb_enabled);
                    400:
                    401:        /*
                    402:         * Fill pin descriptions and initialize registers.
                    403:         */
                    404:        memset(sc->sc_pins, 0, sizeof(sc->sc_pins));
                    405:        for (i = 0; i < __arraycount(sc->sc_bank); i++) {
                    406:                nb = &sc->sc_bank[i];
                    407:                mutex_spin_enter(&sc->sc_lock);
                    408:                nb->nb_val_dir = nct_rd(sc, nb->nb_reg_dir);
                    409:                nb->nb_val_inv = nct_rd(sc, nb->nb_reg_inv);
                    410:                nb->nb_val_mode = nct_rd(sc, nb->nb_reg_mode);
                    411:                mutex_spin_exit(&sc->sc_lock);
                    412:                for (j = 0; j < nb->nb_numpins; j++) {
                    413:                        gpio_pin_t *pin = &sc->sc_pins[nb->nb_firstpin + j];
                    414:                        pin->pin_num = nb->nb_firstpin + j;
                    415:                        /* Skip pin if not configured as GPIO. */
                    416:                        if ((nb->nb_enabled & (1 << j)) == 0) {
                    417:                                continue;
                    418:                        }
                    419:                        pin->pin_caps =
                    420:                            GPIO_PIN_INPUT | GPIO_PIN_OUTPUT |
                    421:                            GPIO_PIN_OPENDRAIN |
                    422:                            GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE |
                    423:                            GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
                    424:                        pin->pin_flags =
                    425:                            GPIO_PIN_INPUT | GPIO_PIN_OPENDRAIN;
                    426:                        nct_gpio_ctl(sc, pin->pin_num, pin->pin_flags);
                    427:                        pin->pin_state = nct_gpio_read(sc, pin->pin_num);
                    428:                }
                    429:        }
                    430:
                    431:        /*
                    432:         * Attach to gpio framework, and attach all pins unconditionally.
                    433:         * If the pins are disabled, we'll ignore any access later.
                    434:         */
                    435:        sc->sc_gc.gp_cookie = sc;
                    436:        sc->sc_gc.gp_pin_read = nct_gpio_read;
                    437:        sc->sc_gc.gp_pin_write = nct_gpio_write;
                    438:        sc->sc_gc.gp_pin_ctl = nct_gpio_ctl;
                    439:
                    440:        gba.gba_gc = &sc->sc_gc;
                    441:        gba.gba_pins = sc->sc_pins;
                    442:        gba.gba_npins = NCT_NUM_PINS;
                    443:
1.4.8.1 ! thorpej   444:        (void)config_found(sc->sc_dev, &gba, gpiobus_print, CFARGS_NONE);
1.1       martin    445: }
                    446:
                    447: /*
                    448:  * Detach device instance.
                    449:  */
                    450: static int
                    451: nct_detach(device_t self, int flags)
                    452: {
                    453:        struct nct_softc *sc = device_private(self);
                    454:
                    455:        bus_space_unmap(sc->sc_iot, sc->sc_ioh, NCT_IOSIZE);
                    456:        mutex_destroy(&sc->sc_lock);
                    457:        return 0;
                    458: }
                    459:
                    460: /*
                    461:  * Read byte from specified register.
                    462:  */
                    463: static u_int8_t
                    464: nct_rd(struct nct_softc *sc, int reg)
                    465: {
                    466:        int dev;
                    467:
                    468:        KASSERT(mutex_owned(&sc->sc_lock));
                    469:
                    470:        dev = reg >> 8;
                    471:        reg &= 0xff;
                    472:
                    473:        if (dev != sc->sc_curdev && dev != 0x00) {
                    474:                sc->sc_curdev = dev;
                    475:                sc->sc_curreg = reg;
                    476:                nct_outb(sc, NCT_PORT_SELECT, GD_DEVSEL);
                    477:                nct_outb(sc, NCT_PORT_DATA, dev);
                    478:                nct_outb(sc, NCT_PORT_SELECT, reg);
                    479:                return nct_inb(sc, NCT_PORT_DATA);
                    480:        } else if (reg != sc->sc_curreg) {
                    481:                sc->sc_curreg = reg;
                    482:                nct_outb(sc, NCT_PORT_SELECT, reg);
                    483:                return nct_inb(sc, NCT_PORT_DATA);
                    484:        } else {
                    485:                return nct_inb(sc, NCT_PORT_DATA);
                    486:        }
                    487: }
                    488:
                    489: /*
                    490:  * Write byte to specified register.
                    491:  */
                    492: static void
                    493: nct_wr(struct nct_softc *sc, int reg, u_int8_t data)
                    494: {
                    495:        int dev;
                    496:
                    497:        KASSERT(mutex_owned(&sc->sc_lock));
                    498:
                    499:        dev = reg >> 8;
                    500:        reg &= 0xff;
                    501:
                    502:        if (dev != sc->sc_curdev && dev != 0x00) {
                    503:                sc->sc_curdev = dev;
                    504:                sc->sc_curreg = reg;
                    505:                nct_outb(sc, NCT_PORT_SELECT, GD_DEVSEL);
                    506:                nct_outb(sc, NCT_PORT_DATA, dev);
                    507:                nct_outb(sc, NCT_PORT_SELECT, reg);
                    508:                nct_outb(sc, NCT_PORT_DATA, data);
                    509:        } else if (reg != sc->sc_curreg) {
                    510:                sc->sc_curreg = reg;
                    511:                nct_outb(sc, NCT_PORT_SELECT, reg);
                    512:                nct_outb(sc, NCT_PORT_DATA, data);
                    513:        } else {
                    514:                nct_outb(sc, NCT_PORT_DATA, data);
                    515:        }
                    516: }
                    517:
                    518: /*
                    519:  * Given pin number, return bank and pin mask.  This alters no state and so
                    520:  * can safely be called without the mutex held.
                    521:  */
                    522: static struct nct_bank *
                    523: nct_sel(struct nct_softc *sc, int pin, u_int8_t *mask)
                    524: {
                    525:        struct nct_bank *nb;
                    526:
                    527:        KASSERT(pin >= 0 && pin < NCT_NUM_PINS);
                    528:        nb = &sc->sc_bank[pin >> 3];
                    529:        KASSERT(pin >= nb->nb_firstpin);
                    530:        KASSERT((pin & 7) < nb->nb_numpins);
                    531:        *mask = (u_int8_t)(1 << (pin & 7)) & nb->nb_enabled;
                    532:        return nb;
                    533: }
                    534:
                    535: /*
                    536:  * GPIO hook: read pin.
                    537:  */
                    538: static int
                    539: nct_gpio_read(void *arg, int pin)
                    540: {
                    541:        struct nct_softc *sc = arg;
                    542:        struct nct_bank *nb;
                    543:        u_int8_t data, mask;
                    544:        int rv = GPIO_PIN_LOW;
                    545:
                    546:        nb = nct_sel(sc, pin, &mask);
                    547:        if (__predict_true(mask != 0)) {
                    548:                mutex_spin_enter(&sc->sc_lock);
                    549:                data = nct_rd(sc, nb->nb_reg_data);
                    550:                if ((data & mask) != 0) {
                    551:                        rv = GPIO_PIN_HIGH;
                    552:                }
                    553:                mutex_spin_exit(&sc->sc_lock);
                    554:        }
                    555:        return rv;
                    556: }
                    557:
                    558: /*
                    559:  * GPIO hook: write pin.
                    560:  */
                    561: static void
                    562: nct_gpio_write(void *arg, int pin, int val)
                    563: {
                    564:        struct nct_softc *sc = arg;
                    565:        struct nct_bank *nb;
                    566:        u_int8_t data, mask;
                    567:
                    568:        nb = nct_sel(sc, pin, &mask);
                    569:        if (__predict_true(mask != 0)) {
                    570:                mutex_spin_enter(&sc->sc_lock);
                    571:                data = nct_rd(sc, nb->nb_reg_data);
                    572:                if (val == GPIO_PIN_LOW) {
                    573:                        data &= ~mask;
                    574:                } else if (val == GPIO_PIN_HIGH) {
                    575:                        data |= mask;
                    576:                }
                    577:                nct_wr(sc, nb->nb_reg_data, data);
                    578:                mutex_spin_exit(&sc->sc_lock);
                    579:        }
                    580: }
                    581:
                    582: /*
                    583:  * GPIO hook: change pin parameters.
                    584:  */
                    585: static void
                    586: nct_gpio_ctl(void *arg, int pin, int flg)
                    587: {
                    588:        struct nct_softc *sc = arg;
                    589:        struct nct_bank *nb;
                    590:        u_int8_t data, mask;
                    591:
                    592:        nb = nct_sel(sc, pin, &mask);
                    593:        if (__predict_false(mask == 0)) {
                    594:                return;
                    595:        }
                    596:
                    597:        /*
                    598:         * Set input direction early to avoid perturbation.
                    599:         */
                    600:        mutex_spin_enter(&sc->sc_lock);
                    601:        data = nb->nb_val_dir;
                    602:        if ((flg & (GPIO_PIN_INPUT | GPIO_PIN_TRISTATE)) != 0) {
                    603:                data |= mask;
                    604:        }
                    605:        if (data != nb->nb_val_dir) {
                    606:                nct_wr(sc, nb->nb_reg_dir, data);
                    607:                nb->nb_val_dir = data;
                    608:        }
                    609:
                    610:        /*
                    611:         * Set inversion.
                    612:         */
                    613:        data = nb->nb_val_inv;
                    614:        if ((flg & (GPIO_PIN_OUTPUT | GPIO_PIN_INVOUT)) ==
                    615:                     (GPIO_PIN_OUTPUT | GPIO_PIN_INVOUT) ||
                    616:             (flg & (GPIO_PIN_INPUT | GPIO_PIN_INVIN)) ==
                    617:                     (GPIO_PIN_INPUT | GPIO_PIN_INVIN)) {
                    618:                data |= mask;
                    619:        } else {
                    620:                data &= ~mask;
                    621:        }
                    622:        if (data != nb->nb_val_inv) {
                    623:                nct_wr(sc, nb->nb_reg_inv, data);
                    624:                nb->nb_val_inv = data;
                    625:        }
                    626:
                    627:        /*
                    628:         * Set drain mode.
                    629:         */
                    630:        data = nb->nb_val_mode;
                    631:        if ((flg & GPIO_PIN_PUSHPULL) != 0) {
                    632:                data |= mask;
                    633:        } else /* GPIO_PIN_OPENDRAIN */ {
                    634:                data &= ~mask;
                    635:        }
                    636:        if (data != nb->nb_val_mode) {
                    637:                nct_wr(sc, nb->nb_reg_mode, data);
                    638:                nb->nb_val_mode = data;
                    639:        }
                    640:
                    641:        /*
                    642:         * Set output direction late to avoid perturbation.
                    643:         */
                    644:        data = nb->nb_val_dir;
                    645:        if ((flg & (GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE)) == GPIO_PIN_OUTPUT) {
                    646:                data &= ~mask;
                    647:        }
                    648:        if (data != nb->nb_val_dir) {
                    649:                nct_wr(sc, nb->nb_reg_dir, data);
                    650:                nb->nb_val_dir = data;
                    651:        }
                    652:        mutex_spin_exit(&sc->sc_lock);
                    653: }

CVSweb <webmaster@jp.NetBSD.org>