16b7b2d80SAdrian Chadd /*- 26b7b2d80SAdrian Chadd * Copyright (c) 2016 Daniel Wyatt <Daniel.Wyatt@gmail.com> 36b7b2d80SAdrian Chadd * All rights reserved. 46b7b2d80SAdrian Chadd * 56b7b2d80SAdrian Chadd * Redistribution and use in source and binary forms, with or without 66b7b2d80SAdrian Chadd * modification, are permitted provided that the following conditions 76b7b2d80SAdrian Chadd * are met: 86b7b2d80SAdrian Chadd * 1. Redistributions of source code must retain the above copyright 96b7b2d80SAdrian Chadd * notice, this list of conditions and the following disclaimer. 106b7b2d80SAdrian Chadd * 2. Redistributions in binary form must reproduce the above copyright 116b7b2d80SAdrian Chadd * notice, this list of conditions and the following disclaimer in the 126b7b2d80SAdrian Chadd * documentation and/or other materials provided with the distribution. 136b7b2d80SAdrian Chadd * 146b7b2d80SAdrian Chadd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 156b7b2d80SAdrian Chadd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 166b7b2d80SAdrian Chadd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 176b7b2d80SAdrian Chadd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 186b7b2d80SAdrian Chadd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 196b7b2d80SAdrian Chadd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 206b7b2d80SAdrian Chadd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 216b7b2d80SAdrian Chadd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 226b7b2d80SAdrian Chadd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 236b7b2d80SAdrian Chadd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 246b7b2d80SAdrian Chadd * SUCH DAMAGE. 256b7b2d80SAdrian Chadd * 266b7b2d80SAdrian Chadd * $FreeBSD$ 276b7b2d80SAdrian Chadd * 286b7b2d80SAdrian Chadd */ 296b7b2d80SAdrian Chadd 306b7b2d80SAdrian Chadd /* 316b7b2d80SAdrian Chadd * Nuvoton GPIO driver. 326b7b2d80SAdrian Chadd */ 336b7b2d80SAdrian Chadd 346b7b2d80SAdrian Chadd #include <sys/cdefs.h> 356b7b2d80SAdrian Chadd 366b7b2d80SAdrian Chadd #include <sys/param.h> 376b7b2d80SAdrian Chadd #include <sys/kernel.h> 386b7b2d80SAdrian Chadd #include <sys/systm.h> 396b7b2d80SAdrian Chadd #include <sys/bus.h> 406b7b2d80SAdrian Chadd #include <sys/eventhandler.h> 416b7b2d80SAdrian Chadd #include <sys/lock.h> 426b7b2d80SAdrian Chadd 436b7b2d80SAdrian Chadd #include <sys/module.h> 446b7b2d80SAdrian Chadd #include <sys/gpio.h> 456b7b2d80SAdrian Chadd 466b7b2d80SAdrian Chadd #include <machine/bus.h> 476b7b2d80SAdrian Chadd 486b7b2d80SAdrian Chadd #include <dev/gpio/gpiobusvar.h> 49e3df342aSAndriy Gapon #include <dev/superio/superio.h> 506b7b2d80SAdrian Chadd 516b7b2d80SAdrian Chadd #include "gpio_if.h" 526b7b2d80SAdrian Chadd 53*7f8d2ed0SStéphane Rochoy #define NCT_PPOD_LDN 0xf /* LDN used to select Push-Pull/Open-Drain */ 546b7b2d80SAdrian Chadd 55*7f8d2ed0SStéphane Rochoy /* Direct access through GPIO register table */ 56*7f8d2ed0SStéphane Rochoy #define NCT_IO_GSR 0 /* Group Select */ 57*7f8d2ed0SStéphane Rochoy #define NCT_IO_IOR 1 /* I/O */ 58*7f8d2ed0SStéphane Rochoy #define NCT_IO_DAT 2 /* Data */ 59*7f8d2ed0SStéphane Rochoy #define NCT_IO_INV 3 /* Inversion */ 60*7f8d2ed0SStéphane Rochoy #define NCT_IO_DST 4 /* Status */ 616b7b2d80SAdrian Chadd 62*7f8d2ed0SStéphane Rochoy #define NCT_MAX_GROUP 9 63*7f8d2ed0SStéphane Rochoy #define NCT_MAX_PIN 75 646b7b2d80SAdrian Chadd 65*7f8d2ed0SStéphane Rochoy #define NCT_PIN_IS_VALID(_sc, _p) ((_p) < (_sc)->npins) 66*7f8d2ed0SStéphane Rochoy #define NCT_PIN_GROUP(_sc, _p) ((_sc)->pinmap[(_p)].group) 67*7f8d2ed0SStéphane Rochoy #define NCT_PIN_GRPNUM(_sc, _p) ((_sc)->pinmap[(_p)].grpnum) 68*7f8d2ed0SStéphane Rochoy #define NCT_PIN_BIT(_sc, _p) ((_sc)->pinmap[(_p)].bit) 69*7f8d2ed0SStéphane Rochoy #define NCT_PIN_BITMASK(_p) (1 << ((_p) & 7)) 706b7b2d80SAdrian Chadd 716b7b2d80SAdrian Chadd #define NCT_GPIO_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ 726b7b2d80SAdrian Chadd GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \ 736b7b2d80SAdrian Chadd GPIO_PIN_INVIN | GPIO_PIN_INVOUT) 746b7b2d80SAdrian Chadd 75*7f8d2ed0SStéphane Rochoy #define NCT_PREFER_INDIRECT_CHANNEL 2 76*7f8d2ed0SStéphane Rochoy 77*7f8d2ed0SStéphane Rochoy #define NCT_VERBOSE_PRINTF(dev, ...) \ 78*7f8d2ed0SStéphane Rochoy do { \ 79*7f8d2ed0SStéphane Rochoy if (__predict_false(bootverbose)) \ 80*7f8d2ed0SStéphane Rochoy device_printf(dev, __VA_ARGS__); \ 81*7f8d2ed0SStéphane Rochoy } while (0) 82*7f8d2ed0SStéphane Rochoy 83155514eaSAndriy Gapon /* 84155514eaSAndriy Gapon * Note that the values are important. 85155514eaSAndriy Gapon * They match actual register offsets. 86155514eaSAndriy Gapon */ 87155514eaSAndriy Gapon typedef enum { 88155514eaSAndriy Gapon REG_IOR = 0, 89155514eaSAndriy Gapon REG_DAT = 1, 90155514eaSAndriy Gapon REG_INV = 2, 91155514eaSAndriy Gapon } reg_t; 92155514eaSAndriy Gapon 93*7f8d2ed0SStéphane Rochoy struct nct_gpio_group { 94*7f8d2ed0SStéphane Rochoy uint32_t caps; 95*7f8d2ed0SStéphane Rochoy uint8_t enable_ldn; 96*7f8d2ed0SStéphane Rochoy uint8_t enable_reg; 97*7f8d2ed0SStéphane Rochoy uint8_t enable_mask; 98*7f8d2ed0SStéphane Rochoy uint8_t data_ldn; 99*7f8d2ed0SStéphane Rochoy uint8_t iobase; 100*7f8d2ed0SStéphane Rochoy uint8_t ppod_reg; /* Push-Pull/Open-Drain */ 101*7f8d2ed0SStéphane Rochoy uint8_t grpnum; 102*7f8d2ed0SStéphane Rochoy uint8_t pinbits[8]; 103*7f8d2ed0SStéphane Rochoy uint8_t npins; 104*7f8d2ed0SStéphane Rochoy }; 105*7f8d2ed0SStéphane Rochoy 1066b7b2d80SAdrian Chadd struct nct_softc { 1076b7b2d80SAdrian Chadd device_t dev; 1086b7b2d80SAdrian Chadd device_t busdev; 1096b7b2d80SAdrian Chadd struct mtx mtx; 110155514eaSAndriy Gapon struct resource *iores; 111155514eaSAndriy Gapon int iorid; 112155514eaSAndriy Gapon int curgrp; 113155514eaSAndriy Gapon struct { 114*7f8d2ed0SStéphane Rochoy uint8_t ior[NCT_MAX_GROUP + 1]; /* direction, 1: input 0: output */ 115*7f8d2ed0SStéphane Rochoy uint8_t out[NCT_MAX_GROUP + 1]; /* output value */ 116*7f8d2ed0SStéphane Rochoy uint8_t out_known[NCT_MAX_GROUP + 1]; /* whether out is valid */ 117*7f8d2ed0SStéphane Rochoy uint8_t inv[NCT_MAX_GROUP + 1]; /* inversion, 1: inverted */ 118155514eaSAndriy Gapon } cache; 119523af57eSConrad Meyer struct gpio_pin pins[NCT_MAX_PIN + 1]; 120*7f8d2ed0SStéphane Rochoy struct nct_device *nctdevp; 121*7f8d2ed0SStéphane Rochoy int npins; /* Total number of pins */ 122*7f8d2ed0SStéphane Rochoy 123*7f8d2ed0SStéphane Rochoy /* Lookup tables */ 124*7f8d2ed0SStéphane Rochoy struct { 125*7f8d2ed0SStéphane Rochoy struct nct_gpio_group *group; 126*7f8d2ed0SStéphane Rochoy uint8_t grpnum; 127*7f8d2ed0SStéphane Rochoy uint8_t bit; 128*7f8d2ed0SStéphane Rochoy } pinmap[NCT_MAX_PIN+1]; 129*7f8d2ed0SStéphane Rochoy struct nct_gpio_group *grpmap[NCT_MAX_GROUP+1]; 1306b7b2d80SAdrian Chadd }; 1316b7b2d80SAdrian Chadd 1326b7b2d80SAdrian Chadd #define GPIO_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \ 1336b7b2d80SAdrian Chadd device_get_nameunit(dev), NULL, MTX_DEF) 1346b7b2d80SAdrian Chadd #define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx) 1356b7b2d80SAdrian Chadd #define GPIO_LOCK(_sc) mtx_lock(&(_sc)->mtx) 1366b7b2d80SAdrian Chadd #define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) 1376b7b2d80SAdrian Chadd #define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED) 1386b7b2d80SAdrian Chadd #define GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED) 1396b7b2d80SAdrian Chadd 140*7f8d2ed0SStéphane Rochoy #define GET_BIT(v, b) (((v) >> (b)) & 1) 141*7f8d2ed0SStéphane Rochoy 142*7f8d2ed0SStéphane Rochoy /* 143*7f8d2ed0SStéphane Rochoy * For most devices there are several GPIO devices, we attach only to one of 144*7f8d2ed0SStéphane Rochoy * them and use the rest without attaching. 145*7f8d2ed0SStéphane Rochoy */ 146*7f8d2ed0SStéphane Rochoy struct nct_device { 147*7f8d2ed0SStéphane Rochoy uint16_t devid; 148*7f8d2ed0SStéphane Rochoy int extid; 1496b7b2d80SAdrian Chadd const char *descr; 150*7f8d2ed0SStéphane Rochoy int ngroups; 151*7f8d2ed0SStéphane Rochoy struct nct_gpio_group groups[NCT_MAX_GROUP + 1]; 152*7f8d2ed0SStéphane Rochoy } nct_devices[] = { 1536b7b2d80SAdrian Chadd { 154*7f8d2ed0SStéphane Rochoy .devid = 0x1061, 155*7f8d2ed0SStéphane Rochoy .descr = "GPIO on Nuvoton NCT5104D", 156*7f8d2ed0SStéphane Rochoy .ngroups = 2, 157*7f8d2ed0SStéphane Rochoy .groups = { 158*7f8d2ed0SStéphane Rochoy { 159*7f8d2ed0SStéphane Rochoy .grpnum = 0, 160*7f8d2ed0SStéphane Rochoy .pinbits = { 0, 1, 2, 3, 4, 5, 6, 7 }, 161*7f8d2ed0SStéphane Rochoy .enable_ldn = 0x07, 162*7f8d2ed0SStéphane Rochoy .enable_reg = 0x30, 163*7f8d2ed0SStéphane Rochoy .enable_mask = 0x01, 164*7f8d2ed0SStéphane Rochoy .data_ldn = 0x07, 165*7f8d2ed0SStéphane Rochoy .ppod_reg = 0xe0, 166*7f8d2ed0SStéphane Rochoy .caps = NCT_GPIO_CAPS, 167*7f8d2ed0SStéphane Rochoy .npins = 8, 168*7f8d2ed0SStéphane Rochoy .iobase = 0xe0, 1696b7b2d80SAdrian Chadd }, 1706b7b2d80SAdrian Chadd { 171*7f8d2ed0SStéphane Rochoy .grpnum = 1, 172*7f8d2ed0SStéphane Rochoy .pinbits = { 0, 1, 2, 3, 4, 5, 6, 7 }, 173*7f8d2ed0SStéphane Rochoy .enable_ldn = 0x07, 174*7f8d2ed0SStéphane Rochoy .enable_reg = 0x30, 175*7f8d2ed0SStéphane Rochoy .enable_mask = 0x02, 176*7f8d2ed0SStéphane Rochoy .data_ldn = 0x07, 177*7f8d2ed0SStéphane Rochoy .ppod_reg = 0xe1, 178*7f8d2ed0SStéphane Rochoy .caps = NCT_GPIO_CAPS, 179*7f8d2ed0SStéphane Rochoy .npins = 8, 180*7f8d2ed0SStéphane Rochoy .iobase = 0xe4, 181*7f8d2ed0SStéphane Rochoy }, 182*7f8d2ed0SStéphane Rochoy }, 1836b7b2d80SAdrian Chadd }, 1849d1208bbSOleksandr Tymoshenko { 185*7f8d2ed0SStéphane Rochoy .devid = 0xc452, 186*7f8d2ed0SStéphane Rochoy .descr = "GPIO on Nuvoton NCT5104D (PC-Engines APU)", 187*7f8d2ed0SStéphane Rochoy .ngroups = 2, 188*7f8d2ed0SStéphane Rochoy .groups = { 189*7f8d2ed0SStéphane Rochoy { 190*7f8d2ed0SStéphane Rochoy .grpnum = 0, 191*7f8d2ed0SStéphane Rochoy .pinbits = { 0, 1, 2, 3, 4, 5, 6, 7 }, 192*7f8d2ed0SStéphane Rochoy .enable_ldn = 0x07, 193*7f8d2ed0SStéphane Rochoy .enable_reg = 0x30, 194*7f8d2ed0SStéphane Rochoy .enable_mask = 0x01, 195*7f8d2ed0SStéphane Rochoy .data_ldn = 0x07, 196*7f8d2ed0SStéphane Rochoy .ppod_reg = 0xe0, 197*7f8d2ed0SStéphane Rochoy .caps = NCT_GPIO_CAPS, 198*7f8d2ed0SStéphane Rochoy .npins = 8, 199*7f8d2ed0SStéphane Rochoy .iobase = 0xe0, 200*7f8d2ed0SStéphane Rochoy }, 201*7f8d2ed0SStéphane Rochoy { 202*7f8d2ed0SStéphane Rochoy .grpnum = 1, 203*7f8d2ed0SStéphane Rochoy .pinbits = { 0, 1, 2, 3, 4, 5, 6, 7 }, 204*7f8d2ed0SStéphane Rochoy .enable_ldn = 0x07, 205*7f8d2ed0SStéphane Rochoy .enable_reg = 0x30, 206*7f8d2ed0SStéphane Rochoy .enable_mask = 0x02, 207*7f8d2ed0SStéphane Rochoy .data_ldn = 0x07, 208*7f8d2ed0SStéphane Rochoy .ppod_reg = 0xe1, 209*7f8d2ed0SStéphane Rochoy .caps = NCT_GPIO_CAPS, 210*7f8d2ed0SStéphane Rochoy .npins = 8, 211*7f8d2ed0SStéphane Rochoy .iobase = 0xe4, 212*7f8d2ed0SStéphane Rochoy }, 213*7f8d2ed0SStéphane Rochoy }, 214*7f8d2ed0SStéphane Rochoy }, 215*7f8d2ed0SStéphane Rochoy { 216*7f8d2ed0SStéphane Rochoy .devid = 0xc453, 217*7f8d2ed0SStéphane Rochoy .descr = "GPIO on Nuvoton NCT5104D (PC-Engines APU3)", 218*7f8d2ed0SStéphane Rochoy .ngroups = 2, 219*7f8d2ed0SStéphane Rochoy .groups = { 220*7f8d2ed0SStéphane Rochoy { 221*7f8d2ed0SStéphane Rochoy .grpnum = 0, 222*7f8d2ed0SStéphane Rochoy .pinbits = { 0, 1, 2, 3, 4, 5, 6, 7 }, 223*7f8d2ed0SStéphane Rochoy .enable_ldn = 0x07, 224*7f8d2ed0SStéphane Rochoy .enable_reg = 0x30, 225*7f8d2ed0SStéphane Rochoy .enable_mask = 0x01, 226*7f8d2ed0SStéphane Rochoy .data_ldn = 0x07, 227*7f8d2ed0SStéphane Rochoy .ppod_reg = 0xe0, 228*7f8d2ed0SStéphane Rochoy .caps = NCT_GPIO_CAPS, 229*7f8d2ed0SStéphane Rochoy .npins = 8, 230*7f8d2ed0SStéphane Rochoy .iobase = 0xe0, 231*7f8d2ed0SStéphane Rochoy }, 232*7f8d2ed0SStéphane Rochoy { 233*7f8d2ed0SStéphane Rochoy .grpnum = 1, 234*7f8d2ed0SStéphane Rochoy .pinbits = { 0, 1, 2, 3, 4, 5, 6, 7 }, 235*7f8d2ed0SStéphane Rochoy .enable_ldn = 0x07, 236*7f8d2ed0SStéphane Rochoy .enable_reg = 0x30, 237*7f8d2ed0SStéphane Rochoy .enable_mask = 0x02, 238*7f8d2ed0SStéphane Rochoy .data_ldn = 0x07, 239*7f8d2ed0SStéphane Rochoy .ppod_reg = 0xe1, 240*7f8d2ed0SStéphane Rochoy .caps = NCT_GPIO_CAPS, 241*7f8d2ed0SStéphane Rochoy .npins = 8, 242*7f8d2ed0SStéphane Rochoy .iobase = 0xe4, 243*7f8d2ed0SStéphane Rochoy }, 244*7f8d2ed0SStéphane Rochoy }, 2459d1208bbSOleksandr Tymoshenko }, 2466b7b2d80SAdrian Chadd }; 2476b7b2d80SAdrian Chadd 248*7f8d2ed0SStéphane Rochoy static const char * 249*7f8d2ed0SStéphane Rochoy io2str(uint8_t ioport) 2506b7b2d80SAdrian Chadd { 251*7f8d2ed0SStéphane Rochoy switch (ioport) { 252*7f8d2ed0SStéphane Rochoy case NCT_IO_GSR: return ("grpsel"); 253*7f8d2ed0SStéphane Rochoy case NCT_IO_IOR: return ("io"); 254*7f8d2ed0SStéphane Rochoy case NCT_IO_DAT: return ("data"); 255*7f8d2ed0SStéphane Rochoy case NCT_IO_INV: return ("inv"); 256*7f8d2ed0SStéphane Rochoy case NCT_IO_DST: return ("status"); 257*7f8d2ed0SStéphane Rochoy default: return ("?"); 258*7f8d2ed0SStéphane Rochoy } 259*7f8d2ed0SStéphane Rochoy } 2606b7b2d80SAdrian Chadd 261*7f8d2ed0SStéphane Rochoy static void 262*7f8d2ed0SStéphane Rochoy nct_io_set_group(struct nct_softc *sc, uint8_t grpnum) 263*7f8d2ed0SStéphane Rochoy { 264155514eaSAndriy Gapon GPIO_ASSERT_LOCKED(sc); 265*7f8d2ed0SStéphane Rochoy 266*7f8d2ed0SStéphane Rochoy if (grpnum == sc->curgrp) 267*7f8d2ed0SStéphane Rochoy return; 268*7f8d2ed0SStéphane Rochoy 269*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x ioport %d\n", 270*7f8d2ed0SStéphane Rochoy io2str(NCT_IO_GSR), grpnum, NCT_IO_GSR); 271*7f8d2ed0SStéphane Rochoy bus_write_1(sc->iores, NCT_IO_GSR, grpnum); 272*7f8d2ed0SStéphane Rochoy sc->curgrp = grpnum; 273155514eaSAndriy Gapon } 2746b7b2d80SAdrian Chadd 275155514eaSAndriy Gapon static uint8_t 276*7f8d2ed0SStéphane Rochoy nct_io_read(struct nct_softc *sc, uint8_t grpnum, uint8_t reg) 277155514eaSAndriy Gapon { 278155514eaSAndriy Gapon uint8_t val; 279155514eaSAndriy Gapon 280*7f8d2ed0SStéphane Rochoy nct_io_set_group(sc, grpnum); 281155514eaSAndriy Gapon 282*7f8d2ed0SStéphane Rochoy val = bus_read_1(sc->iores, reg); 283*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(sc->dev, "read %s 0x%x ioport %d\n", 284*7f8d2ed0SStéphane Rochoy io2str(reg), val, reg); 285155514eaSAndriy Gapon return (val); 286155514eaSAndriy Gapon } 287155514eaSAndriy Gapon 288*7f8d2ed0SStéphane Rochoy static void 289*7f8d2ed0SStéphane Rochoy nct_io_write(struct nct_softc *sc, uint8_t grpnum, uint8_t reg, uint8_t val) 290155514eaSAndriy Gapon { 291*7f8d2ed0SStéphane Rochoy nct_io_set_group(sc, grpnum); 292*7f8d2ed0SStéphane Rochoy 293*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x ioport %d\n", 294*7f8d2ed0SStéphane Rochoy io2str(reg), val, reg); 295*7f8d2ed0SStéphane Rochoy bus_write_1(sc->iores, reg, val); 296*7f8d2ed0SStéphane Rochoy } 297*7f8d2ed0SStéphane Rochoy 298*7f8d2ed0SStéphane Rochoy static uint8_t 299*7f8d2ed0SStéphane Rochoy nct_get_ioreg(struct nct_softc *sc, reg_t reg, uint8_t grpnum) 300*7f8d2ed0SStéphane Rochoy { 301*7f8d2ed0SStéphane Rochoy uint8_t iobase; 302*7f8d2ed0SStéphane Rochoy 303*7f8d2ed0SStéphane Rochoy if (sc->iores != NULL) 304*7f8d2ed0SStéphane Rochoy iobase = NCT_IO_IOR; 305*7f8d2ed0SStéphane Rochoy else 306*7f8d2ed0SStéphane Rochoy iobase = sc->grpmap[grpnum]->iobase; 307*7f8d2ed0SStéphane Rochoy return (iobase + reg); 308*7f8d2ed0SStéphane Rochoy } 309*7f8d2ed0SStéphane Rochoy 310*7f8d2ed0SStéphane Rochoy static const char * 311*7f8d2ed0SStéphane Rochoy reg2str(reg_t reg) 312*7f8d2ed0SStéphane Rochoy { 313*7f8d2ed0SStéphane Rochoy switch (reg) { 314*7f8d2ed0SStéphane Rochoy case REG_IOR: return ("io"); 315*7f8d2ed0SStéphane Rochoy case REG_DAT: return ("data"); 316*7f8d2ed0SStéphane Rochoy case REG_INV: return ("inv"); 317*7f8d2ed0SStéphane Rochoy default: return ("?"); 318*7f8d2ed0SStéphane Rochoy } 319*7f8d2ed0SStéphane Rochoy } 320*7f8d2ed0SStéphane Rochoy 321*7f8d2ed0SStéphane Rochoy static uint8_t 322*7f8d2ed0SStéphane Rochoy nct_read_reg(struct nct_softc *sc, reg_t reg, uint8_t grpnum) 323*7f8d2ed0SStéphane Rochoy { 324*7f8d2ed0SStéphane Rochoy struct nct_gpio_group *gp; 325*7f8d2ed0SStéphane Rochoy uint8_t ioreg; 326155514eaSAndriy Gapon uint8_t val; 327155514eaSAndriy Gapon 328*7f8d2ed0SStéphane Rochoy ioreg = nct_get_ioreg(sc, reg, grpnum); 329155514eaSAndriy Gapon 330*7f8d2ed0SStéphane Rochoy if (sc->iores != NULL) 331*7f8d2ed0SStéphane Rochoy return (nct_io_read(sc, grpnum, ioreg)); 332*7f8d2ed0SStéphane Rochoy 333*7f8d2ed0SStéphane Rochoy gp = sc->grpmap[grpnum]; 334*7f8d2ed0SStéphane Rochoy val = superio_ldn_read(sc->dev, gp->data_ldn, ioreg); 335*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(sc->dev, "read %s 0x%x from group GPIO%u ioreg 0x%x\n", 336*7f8d2ed0SStéphane Rochoy reg2str(reg), val, grpnum, ioreg); 337*7f8d2ed0SStéphane Rochoy return (val); 338155514eaSAndriy Gapon } 339155514eaSAndriy Gapon 340155514eaSAndriy Gapon static int 341155514eaSAndriy Gapon nct_get_pin_cache(struct nct_softc *sc, uint32_t pin_num, uint8_t *cache) 342155514eaSAndriy Gapon { 343155514eaSAndriy Gapon uint8_t bit; 344155514eaSAndriy Gapon uint8_t group; 345155514eaSAndriy Gapon uint8_t val; 346155514eaSAndriy Gapon 347*7f8d2ed0SStéphane Rochoy KASSERT(NCT_PIN_IS_VALID(sc, pin_num), ("%s: invalid pin number %d", 348155514eaSAndriy Gapon __func__, pin_num)); 349155514eaSAndriy Gapon 350*7f8d2ed0SStéphane Rochoy group = NCT_PIN_GRPNUM(sc, pin_num); 351*7f8d2ed0SStéphane Rochoy bit = NCT_PIN_BIT(sc, pin_num); 352155514eaSAndriy Gapon val = cache[group]; 353155514eaSAndriy Gapon return (GET_BIT(val, bit)); 354155514eaSAndriy Gapon } 355155514eaSAndriy Gapon 356155514eaSAndriy Gapon static void 357*7f8d2ed0SStéphane Rochoy nct_write_reg(struct nct_softc *sc, reg_t reg, uint8_t grpnum, uint8_t val) 358155514eaSAndriy Gapon { 359*7f8d2ed0SStéphane Rochoy struct nct_gpio_group *gp; 360155514eaSAndriy Gapon uint8_t ioreg; 361155514eaSAndriy Gapon 362*7f8d2ed0SStéphane Rochoy ioreg = nct_get_ioreg(sc, reg, grpnum); 363*7f8d2ed0SStéphane Rochoy 364*7f8d2ed0SStéphane Rochoy if (sc->iores != NULL) { 365*7f8d2ed0SStéphane Rochoy nct_io_write(sc, grpnum, ioreg, val); 366*7f8d2ed0SStéphane Rochoy return; 367*7f8d2ed0SStéphane Rochoy } 368*7f8d2ed0SStéphane Rochoy 369*7f8d2ed0SStéphane Rochoy gp = sc->grpmap[grpnum]; 370*7f8d2ed0SStéphane Rochoy superio_ldn_write(sc->dev, gp->data_ldn, ioreg, val); 371*7f8d2ed0SStéphane Rochoy 372*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x to group GPIO%u ioreg 0x%x\n", 373*7f8d2ed0SStéphane Rochoy reg2str(reg), val, grpnum, ioreg); 374155514eaSAndriy Gapon } 375155514eaSAndriy Gapon 376155514eaSAndriy Gapon static void 377155514eaSAndriy Gapon nct_set_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num, bool val) 378155514eaSAndriy Gapon { 379155514eaSAndriy Gapon uint8_t *cache; 380155514eaSAndriy Gapon uint8_t bit; 381155514eaSAndriy Gapon uint8_t bitval; 382155514eaSAndriy Gapon uint8_t group; 383155514eaSAndriy Gapon uint8_t mask; 384155514eaSAndriy Gapon 385*7f8d2ed0SStéphane Rochoy KASSERT(NCT_PIN_IS_VALID(sc, pin_num), 386155514eaSAndriy Gapon ("%s: invalid pin number %d", __func__, pin_num)); 387155514eaSAndriy Gapon KASSERT(reg == REG_IOR || reg == REG_INV, 388155514eaSAndriy Gapon ("%s: unsupported register %d", __func__, reg)); 389155514eaSAndriy Gapon 390*7f8d2ed0SStéphane Rochoy group = NCT_PIN_GRPNUM(sc, pin_num); 391*7f8d2ed0SStéphane Rochoy bit = NCT_PIN_BIT(sc, pin_num); 392155514eaSAndriy Gapon mask = (uint8_t)1 << bit; 393155514eaSAndriy Gapon bitval = (uint8_t)val << bit; 394155514eaSAndriy Gapon 395155514eaSAndriy Gapon if (reg == REG_IOR) 396155514eaSAndriy Gapon cache = &sc->cache.ior[group]; 397155514eaSAndriy Gapon else 398155514eaSAndriy Gapon cache = &sc->cache.inv[group]; 399155514eaSAndriy Gapon if ((*cache & mask) == bitval) 400155514eaSAndriy Gapon return; 401155514eaSAndriy Gapon *cache &= ~mask; 402155514eaSAndriy Gapon *cache |= bitval; 403155514eaSAndriy Gapon nct_write_reg(sc, reg, group, *cache); 4046b7b2d80SAdrian Chadd } 4056b7b2d80SAdrian Chadd 4066b7b2d80SAdrian Chadd /* 407155514eaSAndriy Gapon * Set a pin to input (val is true) or output (val is false) mode. 4086b7b2d80SAdrian Chadd */ 4096b7b2d80SAdrian Chadd static void 410155514eaSAndriy Gapon nct_set_pin_input(struct nct_softc *sc, uint32_t pin_num, bool val) 4116b7b2d80SAdrian Chadd { 412155514eaSAndriy Gapon nct_set_pin_reg(sc, REG_IOR, pin_num, val); 4136b7b2d80SAdrian Chadd } 4146b7b2d80SAdrian Chadd 4156b7b2d80SAdrian Chadd /* 4166b7b2d80SAdrian Chadd * Check whether a pin is configured as an input. 4176b7b2d80SAdrian Chadd */ 4186b7b2d80SAdrian Chadd static bool 4196b7b2d80SAdrian Chadd nct_pin_is_input(struct nct_softc *sc, uint32_t pin_num) 4206b7b2d80SAdrian Chadd { 421155514eaSAndriy Gapon return (nct_get_pin_cache(sc, pin_num, sc->cache.ior)); 4226b7b2d80SAdrian Chadd } 4236b7b2d80SAdrian Chadd 4246b7b2d80SAdrian Chadd /* 425155514eaSAndriy Gapon * Set a pin to inverted (val is true) or normal (val is false) mode. 4266b7b2d80SAdrian Chadd */ 4276b7b2d80SAdrian Chadd static void 428155514eaSAndriy Gapon nct_set_pin_inverted(struct nct_softc *sc, uint32_t pin_num, bool val) 4296b7b2d80SAdrian Chadd { 430155514eaSAndriy Gapon nct_set_pin_reg(sc, REG_INV, pin_num, val); 4316b7b2d80SAdrian Chadd } 4326b7b2d80SAdrian Chadd 4336b7b2d80SAdrian Chadd static bool 4346b7b2d80SAdrian Chadd nct_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num) 4356b7b2d80SAdrian Chadd { 436155514eaSAndriy Gapon return (nct_get_pin_cache(sc, pin_num, sc->cache.inv)); 4376b7b2d80SAdrian Chadd } 4386b7b2d80SAdrian Chadd 439155514eaSAndriy Gapon /* 440155514eaSAndriy Gapon * Write a value to an output pin. 441155514eaSAndriy Gapon * NB: the hardware remembers last output value across switching from 442155514eaSAndriy Gapon * output mode to input mode and back. 443155514eaSAndriy Gapon * Writes to a pin in input mode are not allowed here as they cannot 444155514eaSAndriy Gapon * have any effect and would corrupt the output value cache. 445155514eaSAndriy Gapon */ 446155514eaSAndriy Gapon static void 447155514eaSAndriy Gapon nct_write_pin(struct nct_softc *sc, uint32_t pin_num, bool val) 448155514eaSAndriy Gapon { 449155514eaSAndriy Gapon uint8_t bit; 450155514eaSAndriy Gapon uint8_t group; 451155514eaSAndriy Gapon 452155514eaSAndriy Gapon KASSERT(!nct_pin_is_input(sc, pin_num), ("attempt to write input pin")); 453*7f8d2ed0SStéphane Rochoy group = NCT_PIN_GRPNUM(sc, pin_num); 454*7f8d2ed0SStéphane Rochoy bit = NCT_PIN_BIT(sc, pin_num); 455*7f8d2ed0SStéphane Rochoy 456155514eaSAndriy Gapon if (GET_BIT(sc->cache.out_known[group], bit) && 457155514eaSAndriy Gapon GET_BIT(sc->cache.out[group], bit) == val) { 458155514eaSAndriy Gapon /* The pin is already in requested state. */ 459155514eaSAndriy Gapon return; 460155514eaSAndriy Gapon } 461155514eaSAndriy Gapon sc->cache.out_known[group] |= 1 << bit; 462155514eaSAndriy Gapon if (val) 463155514eaSAndriy Gapon sc->cache.out[group] |= 1 << bit; 464155514eaSAndriy Gapon else 465155514eaSAndriy Gapon sc->cache.out[group] &= ~(1 << bit); 466155514eaSAndriy Gapon nct_write_reg(sc, REG_DAT, group, sc->cache.out[group]); 467155514eaSAndriy Gapon } 468155514eaSAndriy Gapon 469*7f8d2ed0SStéphane Rochoy static bool 470*7f8d2ed0SStéphane Rochoy nct_get_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num) 471*7f8d2ed0SStéphane Rochoy { 472*7f8d2ed0SStéphane Rochoy uint8_t bit; 473*7f8d2ed0SStéphane Rochoy uint8_t group; 474*7f8d2ed0SStéphane Rochoy uint8_t val; 475*7f8d2ed0SStéphane Rochoy bool b; 476*7f8d2ed0SStéphane Rochoy 477*7f8d2ed0SStéphane Rochoy KASSERT(NCT_PIN_IS_VALID(sc, pin_num), ("%s: invalid pin number %d", 478*7f8d2ed0SStéphane Rochoy __func__, pin_num)); 479*7f8d2ed0SStéphane Rochoy 480*7f8d2ed0SStéphane Rochoy group = NCT_PIN_GRPNUM(sc, pin_num); 481*7f8d2ed0SStéphane Rochoy bit = NCT_PIN_BIT(sc, pin_num); 482*7f8d2ed0SStéphane Rochoy val = nct_read_reg(sc, reg, group); 483*7f8d2ed0SStéphane Rochoy b = GET_BIT(val, bit); 484*7f8d2ed0SStéphane Rochoy 485*7f8d2ed0SStéphane Rochoy if (__predict_false(bootverbose)) { 486*7f8d2ed0SStéphane Rochoy if (nct_pin_is_input(sc, pin_num)) 487*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(sc->dev, "read %d from input pin %u<GPIO%u%u>\n", 488*7f8d2ed0SStéphane Rochoy b, pin_num, group, bit); 489*7f8d2ed0SStéphane Rochoy else 490*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(sc->dev, 491*7f8d2ed0SStéphane Rochoy "read %d from output pin %u<GPIO%u%u>, cache miss\n", 492*7f8d2ed0SStéphane Rochoy b, pin_num, group, bit); 493*7f8d2ed0SStéphane Rochoy } 494*7f8d2ed0SStéphane Rochoy 495*7f8d2ed0SStéphane Rochoy return (b); 496*7f8d2ed0SStéphane Rochoy } 497*7f8d2ed0SStéphane Rochoy 498155514eaSAndriy Gapon /* 499155514eaSAndriy Gapon * NB: state of an input pin cannot be cached, of course. 500155514eaSAndriy Gapon * For an output we can either take the value from the cache if it's valid 501155514eaSAndriy Gapon * or read the state from the hadrware and cache it. 502155514eaSAndriy Gapon */ 503155514eaSAndriy Gapon static bool 504155514eaSAndriy Gapon nct_read_pin(struct nct_softc *sc, uint32_t pin_num) 505155514eaSAndriy Gapon { 506155514eaSAndriy Gapon uint8_t bit; 507155514eaSAndriy Gapon uint8_t group; 508155514eaSAndriy Gapon bool val; 509155514eaSAndriy Gapon 510*7f8d2ed0SStéphane Rochoy if (nct_pin_is_input(sc, pin_num)) { 511155514eaSAndriy Gapon return (nct_get_pin_reg(sc, REG_DAT, pin_num)); 512*7f8d2ed0SStéphane Rochoy } 513155514eaSAndriy Gapon 514*7f8d2ed0SStéphane Rochoy group = NCT_PIN_GRPNUM(sc, pin_num); 515*7f8d2ed0SStéphane Rochoy bit = NCT_PIN_BIT(sc, pin_num); 516*7f8d2ed0SStéphane Rochoy 517*7f8d2ed0SStéphane Rochoy if (GET_BIT(sc->cache.out_known[group], bit)) { 518*7f8d2ed0SStéphane Rochoy val = GET_BIT(sc->cache.out[group], bit); 519*7f8d2ed0SStéphane Rochoy 520*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(sc->dev, 521*7f8d2ed0SStéphane Rochoy "read %d from output pin %u<GPIO%u%u>, cache hit\n", 522*7f8d2ed0SStéphane Rochoy val, pin_num, group, bit); 523*7f8d2ed0SStéphane Rochoy 524*7f8d2ed0SStéphane Rochoy return (val); 525*7f8d2ed0SStéphane Rochoy } 526155514eaSAndriy Gapon 527155514eaSAndriy Gapon val = nct_get_pin_reg(sc, REG_DAT, pin_num); 528155514eaSAndriy Gapon sc->cache.out_known[group] |= 1 << bit; 529155514eaSAndriy Gapon if (val) 530155514eaSAndriy Gapon sc->cache.out[group] |= 1 << bit; 531155514eaSAndriy Gapon else 532155514eaSAndriy Gapon sc->cache.out[group] &= ~(1 << bit); 533155514eaSAndriy Gapon return (val); 534155514eaSAndriy Gapon } 535155514eaSAndriy Gapon 536155514eaSAndriy Gapon static uint8_t 537*7f8d2ed0SStéphane Rochoy nct_ppod_reg(struct nct_softc *sc, uint32_t pin_num) 538155514eaSAndriy Gapon { 539*7f8d2ed0SStéphane Rochoy uint8_t group = NCT_PIN_GRPNUM(sc, pin_num); 540*7f8d2ed0SStéphane Rochoy 541*7f8d2ed0SStéphane Rochoy return (sc->grpmap[group]->ppod_reg); 542155514eaSAndriy Gapon } 543155514eaSAndriy Gapon 544155514eaSAndriy Gapon /* 545155514eaSAndriy Gapon * NB: PP/OD can be configured only via configuration registers. 546155514eaSAndriy Gapon * Also, the registers are in a different logical device. 547155514eaSAndriy Gapon * So, this is a special case. No caching too. 548155514eaSAndriy Gapon */ 5496b7b2d80SAdrian Chadd static void 5506b7b2d80SAdrian Chadd nct_set_pin_opendrain(struct nct_softc *sc, uint32_t pin_num) 5516b7b2d80SAdrian Chadd { 5526b7b2d80SAdrian Chadd uint8_t reg; 5536b7b2d80SAdrian Chadd uint8_t outcfg; 5546b7b2d80SAdrian Chadd 555*7f8d2ed0SStéphane Rochoy reg = nct_ppod_reg(sc, pin_num); 556*7f8d2ed0SStéphane Rochoy outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg); 557*7f8d2ed0SStéphane Rochoy outcfg |= NCT_PIN_BITMASK(pin_num); 558*7f8d2ed0SStéphane Rochoy superio_ldn_write(sc->dev, 0xf, reg, outcfg); 5596b7b2d80SAdrian Chadd } 5606b7b2d80SAdrian Chadd 5616b7b2d80SAdrian Chadd static void 5626b7b2d80SAdrian Chadd nct_set_pin_pushpull(struct nct_softc *sc, uint32_t pin_num) 5636b7b2d80SAdrian Chadd { 5646b7b2d80SAdrian Chadd uint8_t reg; 5656b7b2d80SAdrian Chadd uint8_t outcfg; 5666b7b2d80SAdrian Chadd 567*7f8d2ed0SStéphane Rochoy reg = nct_ppod_reg(sc, pin_num); 568*7f8d2ed0SStéphane Rochoy outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg); 569*7f8d2ed0SStéphane Rochoy outcfg &= ~NCT_PIN_BITMASK(pin_num); 570*7f8d2ed0SStéphane Rochoy superio_ldn_write(sc->dev, 0xf, reg, outcfg); 5716b7b2d80SAdrian Chadd } 5726b7b2d80SAdrian Chadd 5736b7b2d80SAdrian Chadd static bool 5746b7b2d80SAdrian Chadd nct_pin_is_opendrain(struct nct_softc *sc, uint32_t pin_num) 5756b7b2d80SAdrian Chadd { 5766b7b2d80SAdrian Chadd uint8_t reg; 5776b7b2d80SAdrian Chadd uint8_t outcfg; 5786b7b2d80SAdrian Chadd 579*7f8d2ed0SStéphane Rochoy reg = nct_ppod_reg(sc, pin_num); 580*7f8d2ed0SStéphane Rochoy outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg); 581*7f8d2ed0SStéphane Rochoy return (outcfg & NCT_PIN_BITMASK(pin_num)); 582*7f8d2ed0SStéphane Rochoy } 583*7f8d2ed0SStéphane Rochoy 584*7f8d2ed0SStéphane Rochoy static struct nct_device * 585*7f8d2ed0SStéphane Rochoy nct_lookup_device(device_t dev) 586*7f8d2ed0SStéphane Rochoy { 587*7f8d2ed0SStéphane Rochoy struct nct_device *nctdevp; 588*7f8d2ed0SStéphane Rochoy uint16_t devid; 589*7f8d2ed0SStéphane Rochoy int i, extid; 590*7f8d2ed0SStéphane Rochoy 591*7f8d2ed0SStéphane Rochoy devid = superio_devid(dev); 592*7f8d2ed0SStéphane Rochoy extid = superio_extid(dev); 593*7f8d2ed0SStéphane Rochoy for (i = 0, nctdevp = nct_devices; i < nitems(nct_devices); i++, nctdevp++) { 594*7f8d2ed0SStéphane Rochoy if (devid == nctdevp->devid && nctdevp->extid == extid) 595*7f8d2ed0SStéphane Rochoy return (nctdevp); 596*7f8d2ed0SStéphane Rochoy } 597*7f8d2ed0SStéphane Rochoy return (NULL); 5986b7b2d80SAdrian Chadd } 5996b7b2d80SAdrian Chadd 6006b7b2d80SAdrian Chadd static int 6016b7b2d80SAdrian Chadd nct_probe(device_t dev) 6026b7b2d80SAdrian Chadd { 603*7f8d2ed0SStéphane Rochoy struct nct_device *nctdevp; 604*7f8d2ed0SStéphane Rochoy uint8_t ldn; 6056b7b2d80SAdrian Chadd 606*7f8d2ed0SStéphane Rochoy ldn = superio_get_ldn(dev); 6076b7b2d80SAdrian Chadd 608*7f8d2ed0SStéphane Rochoy if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON) { 609*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not a Nuvoton device\n", ldn); 610e3df342aSAndriy Gapon return (ENXIO); 611*7f8d2ed0SStéphane Rochoy } 612*7f8d2ed0SStéphane Rochoy if (superio_get_type(dev) != SUPERIO_DEV_GPIO) { 613*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not a GPIO device\n", ldn); 614*7f8d2ed0SStéphane Rochoy return (ENXIO); 615*7f8d2ed0SStéphane Rochoy } 6166b7b2d80SAdrian Chadd 617*7f8d2ed0SStéphane Rochoy nctdevp = nct_lookup_device(dev); 618*7f8d2ed0SStéphane Rochoy if (nctdevp == NULL) { 619*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not supported\n", ldn); 620*7f8d2ed0SStéphane Rochoy return (ENXIO); 621*7f8d2ed0SStéphane Rochoy } 622*7f8d2ed0SStéphane Rochoy device_set_desc(dev, nctdevp->descr); 6236b7b2d80SAdrian Chadd return (BUS_PROBE_DEFAULT); 6246b7b2d80SAdrian Chadd } 6256b7b2d80SAdrian Chadd 6266b7b2d80SAdrian Chadd static int 6276b7b2d80SAdrian Chadd nct_attach(device_t dev) 6286b7b2d80SAdrian Chadd { 6296b7b2d80SAdrian Chadd struct nct_softc *sc; 630*7f8d2ed0SStéphane Rochoy struct nct_gpio_group *gp; 631*7f8d2ed0SStéphane Rochoy uint32_t pin_num; 632*7f8d2ed0SStéphane Rochoy uint8_t v; 633*7f8d2ed0SStéphane Rochoy int flags, i, g; 6346b7b2d80SAdrian Chadd 6356b7b2d80SAdrian Chadd sc = device_get_softc(dev); 636e3df342aSAndriy Gapon sc->dev = dev; 637*7f8d2ed0SStéphane Rochoy sc->nctdevp = nct_lookup_device(dev); 638*7f8d2ed0SStéphane Rochoy 639*7f8d2ed0SStéphane Rochoy flags = 0; 640*7f8d2ed0SStéphane Rochoy (void)resource_int_value(device_get_name(dev), device_get_unit(dev), "flags", &flags); 641*7f8d2ed0SStéphane Rochoy 642*7f8d2ed0SStéphane Rochoy if ((flags & NCT_PREFER_INDIRECT_CHANNEL) == 0) { 643*7f8d2ed0SStéphane Rochoy uint16_t iobase; 644*7f8d2ed0SStéphane Rochoy device_t dev_8; 6456b7b2d80SAdrian Chadd 646155514eaSAndriy Gapon /* 647155514eaSAndriy Gapon * As strange as it may seem, I/O port base is configured in the 648155514eaSAndriy Gapon * Logical Device 8 which is primarily used for WDT, but also plays 649155514eaSAndriy Gapon * a role in GPIO configuration. 650155514eaSAndriy Gapon */ 651155514eaSAndriy Gapon iobase = 0; 652155514eaSAndriy Gapon dev_8 = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_WDT, 8); 653155514eaSAndriy Gapon if (dev_8 != NULL) 654155514eaSAndriy Gapon iobase = superio_get_iobase(dev_8); 655155514eaSAndriy Gapon if (iobase != 0 && iobase != 0xffff) { 656*7f8d2ed0SStéphane Rochoy int err; 657*7f8d2ed0SStéphane Rochoy 658*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(dev, "iobase %#x\n", iobase); 659155514eaSAndriy Gapon sc->curgrp = -1; 660155514eaSAndriy Gapon sc->iorid = 0; 661155514eaSAndriy Gapon err = bus_set_resource(dev, SYS_RES_IOPORT, sc->iorid, 662155514eaSAndriy Gapon iobase, 7); 663155514eaSAndriy Gapon if (err == 0) { 664155514eaSAndriy Gapon sc->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 665155514eaSAndriy Gapon &sc->iorid, RF_ACTIVE); 666155514eaSAndriy Gapon if (sc->iores == NULL) { 667155514eaSAndriy Gapon device_printf(dev, "can't map i/o space, " 668*7f8d2ed0SStéphane Rochoy "iobase=%#x\n", iobase); 669155514eaSAndriy Gapon } 670155514eaSAndriy Gapon } else { 671155514eaSAndriy Gapon device_printf(dev, 672*7f8d2ed0SStéphane Rochoy "failed to set io port resource at %#x\n", iobase); 673155514eaSAndriy Gapon } 674155514eaSAndriy Gapon } 675*7f8d2ed0SStéphane Rochoy } 676*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(dev, "iores %p %s channel\n", 677*7f8d2ed0SStéphane Rochoy sc->iores, (sc->iores ? "direct" : "indirect")); 678155514eaSAndriy Gapon 679*7f8d2ed0SStéphane Rochoy /* Enable GPIO groups */ 680*7f8d2ed0SStéphane Rochoy for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) { 681*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(dev, 682*7f8d2ed0SStéphane Rochoy "GPIO%d: %d pins, enable with mask 0x%x via ldn 0x%x reg 0x%x\n", 683*7f8d2ed0SStéphane Rochoy gp->grpnum, gp->npins, gp->enable_mask, gp->enable_ldn, 684*7f8d2ed0SStéphane Rochoy gp->enable_reg); 685*7f8d2ed0SStéphane Rochoy v = superio_ldn_read(dev, gp->enable_ldn, gp->enable_reg); 686*7f8d2ed0SStéphane Rochoy v |= gp->enable_mask; 687*7f8d2ed0SStéphane Rochoy superio_ldn_write(dev, gp->enable_ldn, gp->enable_reg, v); 688*7f8d2ed0SStéphane Rochoy } 689e3df342aSAndriy Gapon 690e3df342aSAndriy Gapon GPIO_LOCK_INIT(sc); 691e3df342aSAndriy Gapon GPIO_LOCK(sc); 6926b7b2d80SAdrian Chadd 693*7f8d2ed0SStéphane Rochoy pin_num = 0; 694*7f8d2ed0SStéphane Rochoy sc->npins = 0; 695*7f8d2ed0SStéphane Rochoy for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) { 696*7f8d2ed0SStéphane Rochoy sc->npins += gp->npins; 697*7f8d2ed0SStéphane Rochoy for (i = 0; i < gp->npins; i++, pin_num++) { 698*7f8d2ed0SStéphane Rochoy struct gpio_pin *pin; 699*7f8d2ed0SStéphane Rochoy 700*7f8d2ed0SStéphane Rochoy sc->pinmap[pin_num].group = gp; 701*7f8d2ed0SStéphane Rochoy sc->pinmap[pin_num].grpnum = gp->grpnum; 702*7f8d2ed0SStéphane Rochoy sc->pinmap[pin_num].bit = gp->pinbits[i]; 703*7f8d2ed0SStéphane Rochoy 704*7f8d2ed0SStéphane Rochoy sc->grpmap[gp->grpnum] = gp; 705*7f8d2ed0SStéphane Rochoy 706*7f8d2ed0SStéphane Rochoy pin = &sc->pins[pin_num]; 707*7f8d2ed0SStéphane Rochoy pin->gp_pin = pin_num; 708*7f8d2ed0SStéphane Rochoy pin->gp_caps = gp->caps; 709*7f8d2ed0SStéphane Rochoy pin->gp_flags = 0; 710*7f8d2ed0SStéphane Rochoy 711*7f8d2ed0SStéphane Rochoy snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%u%u", 712*7f8d2ed0SStéphane Rochoy gp->grpnum, gp->pinbits[i]); 713*7f8d2ed0SStéphane Rochoy 714*7f8d2ed0SStéphane Rochoy if (nct_pin_is_input(sc, pin_num)) 715*7f8d2ed0SStéphane Rochoy pin->gp_flags |= GPIO_PIN_INPUT; 716*7f8d2ed0SStéphane Rochoy else 717*7f8d2ed0SStéphane Rochoy pin->gp_flags |= GPIO_PIN_OUTPUT; 718*7f8d2ed0SStéphane Rochoy 719*7f8d2ed0SStéphane Rochoy if (nct_pin_is_opendrain(sc, pin_num)) 720*7f8d2ed0SStéphane Rochoy pin->gp_flags |= GPIO_PIN_OPENDRAIN; 721*7f8d2ed0SStéphane Rochoy else 722*7f8d2ed0SStéphane Rochoy pin->gp_flags |= GPIO_PIN_PUSHPULL; 723*7f8d2ed0SStéphane Rochoy 724*7f8d2ed0SStéphane Rochoy if (nct_pin_is_inverted(sc, pin_num)) 725*7f8d2ed0SStéphane Rochoy pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT); 726*7f8d2ed0SStéphane Rochoy } 727*7f8d2ed0SStéphane Rochoy } 728*7f8d2ed0SStéphane Rochoy NCT_VERBOSE_PRINTF(dev, "%d pins available\n", sc->npins); 729155514eaSAndriy Gapon 730155514eaSAndriy Gapon /* 731155514eaSAndriy Gapon * Caching input values is meaningless as an input can be changed at any 732155514eaSAndriy Gapon * time by an external agent. But outputs are controlled by this 733155514eaSAndriy Gapon * driver, so it can cache their state. Also, the hardware remembers 734155514eaSAndriy Gapon * the output state of a pin when the pin is switched to input mode and 735155514eaSAndriy Gapon * then back to output mode. So, the cache stays valid. 736155514eaSAndriy Gapon * The only problem is with pins that are in input mode at the attach 737155514eaSAndriy Gapon * time. For them the output state is not known until it is set by the 738155514eaSAndriy Gapon * driver for the first time. 739155514eaSAndriy Gapon * 'out' and 'out_known' bits form a tri-state output cache: 740155514eaSAndriy Gapon * |-----+-----------+---------| 741155514eaSAndriy Gapon * | out | out_known | cache | 742155514eaSAndriy Gapon * |-----+-----------+---------| 743155514eaSAndriy Gapon * | X | 0 | invalid | 744155514eaSAndriy Gapon * | 0 | 1 | 0 | 745155514eaSAndriy Gapon * | 1 | 1 | 1 | 746155514eaSAndriy Gapon * |-----+-----------+---------| 747155514eaSAndriy Gapon */ 748*7f8d2ed0SStéphane Rochoy for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) { 749*7f8d2ed0SStéphane Rochoy sc->cache.inv[gp->grpnum] = nct_read_reg(sc, REG_INV, gp->grpnum); 750*7f8d2ed0SStéphane Rochoy sc->cache.ior[gp->grpnum] = nct_read_reg(sc, REG_IOR, gp->grpnum); 751*7f8d2ed0SStéphane Rochoy sc->cache.out[gp->grpnum] = nct_read_reg(sc, REG_DAT, gp->grpnum); 752*7f8d2ed0SStéphane Rochoy sc->cache.out_known[gp->grpnum] = ~sc->cache.ior[gp->grpnum]; 7536b7b2d80SAdrian Chadd } 754*7f8d2ed0SStéphane Rochoy 7556b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 7566b7b2d80SAdrian Chadd 7576b7b2d80SAdrian Chadd sc->busdev = gpiobus_attach_bus(dev); 7586b7b2d80SAdrian Chadd if (sc->busdev == NULL) { 759*7f8d2ed0SStéphane Rochoy device_printf(dev, "failed to attach to gpiobus\n"); 7606b7b2d80SAdrian Chadd GPIO_LOCK_DESTROY(sc); 7616b7b2d80SAdrian Chadd return (ENXIO); 7626b7b2d80SAdrian Chadd } 7636b7b2d80SAdrian Chadd 7646b7b2d80SAdrian Chadd return (0); 7656b7b2d80SAdrian Chadd } 7666b7b2d80SAdrian Chadd 7676b7b2d80SAdrian Chadd static int 7686b7b2d80SAdrian Chadd nct_detach(device_t dev) 7696b7b2d80SAdrian Chadd { 7706b7b2d80SAdrian Chadd struct nct_softc *sc; 7716b7b2d80SAdrian Chadd 7726b7b2d80SAdrian Chadd sc = device_get_softc(dev); 7736b7b2d80SAdrian Chadd gpiobus_detach_bus(dev); 7746b7b2d80SAdrian Chadd 775155514eaSAndriy Gapon if (sc->iores != NULL) 776155514eaSAndriy Gapon bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->iores); 7776b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 7786b7b2d80SAdrian Chadd GPIO_LOCK_DESTROY(sc); 7796b7b2d80SAdrian Chadd 7806b7b2d80SAdrian Chadd return (0); 7816b7b2d80SAdrian Chadd } 7826b7b2d80SAdrian Chadd 7836b7b2d80SAdrian Chadd static device_t 7846b7b2d80SAdrian Chadd nct_gpio_get_bus(device_t dev) 7856b7b2d80SAdrian Chadd { 7866b7b2d80SAdrian Chadd struct nct_softc *sc; 7876b7b2d80SAdrian Chadd 7886b7b2d80SAdrian Chadd sc = device_get_softc(dev); 7896b7b2d80SAdrian Chadd 7906b7b2d80SAdrian Chadd return (sc->busdev); 7916b7b2d80SAdrian Chadd } 7926b7b2d80SAdrian Chadd 7936b7b2d80SAdrian Chadd static int 794*7f8d2ed0SStéphane Rochoy nct_gpio_pin_max(device_t dev, int *maxpin) 7956b7b2d80SAdrian Chadd { 796*7f8d2ed0SStéphane Rochoy struct nct_softc *sc; 7976b7b2d80SAdrian Chadd 798*7f8d2ed0SStéphane Rochoy sc = device_get_softc(dev); 799*7f8d2ed0SStéphane Rochoy *maxpin = sc->npins - 1; 8006b7b2d80SAdrian Chadd return (0); 8016b7b2d80SAdrian Chadd } 8026b7b2d80SAdrian Chadd 8036b7b2d80SAdrian Chadd static int 8046b7b2d80SAdrian Chadd nct_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value) 8056b7b2d80SAdrian Chadd { 8066b7b2d80SAdrian Chadd struct nct_softc *sc; 8076b7b2d80SAdrian Chadd 808*7f8d2ed0SStéphane Rochoy sc = device_get_softc(dev); 809*7f8d2ed0SStéphane Rochoy 810*7f8d2ed0SStéphane Rochoy if (!NCT_PIN_IS_VALID(sc, pin_num)) 8116b7b2d80SAdrian Chadd return (EINVAL); 8126b7b2d80SAdrian Chadd 8136b7b2d80SAdrian Chadd GPIO_LOCK(sc); 814155514eaSAndriy Gapon if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) { 815155514eaSAndriy Gapon GPIO_UNLOCK(sc); 816155514eaSAndriy Gapon return (EINVAL); 817155514eaSAndriy Gapon } 8186b7b2d80SAdrian Chadd nct_write_pin(sc, pin_num, pin_value); 8196b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 8206b7b2d80SAdrian Chadd 8216b7b2d80SAdrian Chadd return (0); 8226b7b2d80SAdrian Chadd } 8236b7b2d80SAdrian Chadd 8246b7b2d80SAdrian Chadd static int 8256b7b2d80SAdrian Chadd nct_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value) 8266b7b2d80SAdrian Chadd { 8276b7b2d80SAdrian Chadd struct nct_softc *sc; 8286b7b2d80SAdrian Chadd 829*7f8d2ed0SStéphane Rochoy sc = device_get_softc(dev); 830*7f8d2ed0SStéphane Rochoy 831*7f8d2ed0SStéphane Rochoy if (!NCT_PIN_IS_VALID(sc, pin_num)) 8326b7b2d80SAdrian Chadd return (EINVAL); 8336b7b2d80SAdrian Chadd 8346b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 8356b7b2d80SAdrian Chadd GPIO_LOCK(sc); 8366b7b2d80SAdrian Chadd *pin_value = nct_read_pin(sc, pin_num); 8376b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 8386b7b2d80SAdrian Chadd 8396b7b2d80SAdrian Chadd return (0); 8406b7b2d80SAdrian Chadd } 8416b7b2d80SAdrian Chadd 8426b7b2d80SAdrian Chadd static int 8436b7b2d80SAdrian Chadd nct_gpio_pin_toggle(device_t dev, uint32_t pin_num) 8446b7b2d80SAdrian Chadd { 8456b7b2d80SAdrian Chadd struct nct_softc *sc; 8466b7b2d80SAdrian Chadd 847*7f8d2ed0SStéphane Rochoy sc = device_get_softc(dev); 848*7f8d2ed0SStéphane Rochoy 849*7f8d2ed0SStéphane Rochoy if (!NCT_PIN_IS_VALID(sc, pin_num)) 8506b7b2d80SAdrian Chadd return (EINVAL); 8516b7b2d80SAdrian Chadd 8526b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 8536b7b2d80SAdrian Chadd GPIO_LOCK(sc); 854155514eaSAndriy Gapon if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) { 855155514eaSAndriy Gapon GPIO_UNLOCK(sc); 856155514eaSAndriy Gapon return (EINVAL); 857155514eaSAndriy Gapon } 8586b7b2d80SAdrian Chadd if (nct_read_pin(sc, pin_num)) 8596b7b2d80SAdrian Chadd nct_write_pin(sc, pin_num, 0); 8606b7b2d80SAdrian Chadd else 8616b7b2d80SAdrian Chadd nct_write_pin(sc, pin_num, 1); 8626b7b2d80SAdrian Chadd 8636b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 8646b7b2d80SAdrian Chadd 8656b7b2d80SAdrian Chadd return (0); 8666b7b2d80SAdrian Chadd } 8676b7b2d80SAdrian Chadd 8686b7b2d80SAdrian Chadd static int 8696b7b2d80SAdrian Chadd nct_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps) 8706b7b2d80SAdrian Chadd { 8716b7b2d80SAdrian Chadd struct nct_softc *sc; 8726b7b2d80SAdrian Chadd 873*7f8d2ed0SStéphane Rochoy sc = device_get_softc(dev); 874*7f8d2ed0SStéphane Rochoy 875*7f8d2ed0SStéphane Rochoy if (!NCT_PIN_IS_VALID(sc, pin_num)) 8766b7b2d80SAdrian Chadd return (EINVAL); 8776b7b2d80SAdrian Chadd 8786b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 8796b7b2d80SAdrian Chadd GPIO_LOCK(sc); 8806b7b2d80SAdrian Chadd *caps = sc->pins[pin_num].gp_caps; 8816b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 8826b7b2d80SAdrian Chadd 8836b7b2d80SAdrian Chadd return (0); 8846b7b2d80SAdrian Chadd } 8856b7b2d80SAdrian Chadd 8866b7b2d80SAdrian Chadd static int 8876b7b2d80SAdrian Chadd nct_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags) 8886b7b2d80SAdrian Chadd { 8896b7b2d80SAdrian Chadd struct nct_softc *sc; 8906b7b2d80SAdrian Chadd 891*7f8d2ed0SStéphane Rochoy sc = device_get_softc(dev); 892*7f8d2ed0SStéphane Rochoy 893*7f8d2ed0SStéphane Rochoy if (!NCT_PIN_IS_VALID(sc, pin_num)) 8946b7b2d80SAdrian Chadd return (EINVAL); 8956b7b2d80SAdrian Chadd 8966b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 8976b7b2d80SAdrian Chadd GPIO_LOCK(sc); 8986b7b2d80SAdrian Chadd *flags = sc->pins[pin_num].gp_flags; 8996b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 9006b7b2d80SAdrian Chadd 9016b7b2d80SAdrian Chadd return (0); 9026b7b2d80SAdrian Chadd } 9036b7b2d80SAdrian Chadd 9046b7b2d80SAdrian Chadd static int 9056b7b2d80SAdrian Chadd nct_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name) 9066b7b2d80SAdrian Chadd { 9076b7b2d80SAdrian Chadd struct nct_softc *sc; 9086b7b2d80SAdrian Chadd 909*7f8d2ed0SStéphane Rochoy sc = device_get_softc(dev); 910*7f8d2ed0SStéphane Rochoy 911*7f8d2ed0SStéphane Rochoy if (!NCT_PIN_IS_VALID(sc, pin_num)) 9126b7b2d80SAdrian Chadd return (EINVAL); 9136b7b2d80SAdrian Chadd 9146b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 9156b7b2d80SAdrian Chadd GPIO_LOCK(sc); 9166b7b2d80SAdrian Chadd memcpy(name, sc->pins[pin_num].gp_name, GPIOMAXNAME); 9176b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 9186b7b2d80SAdrian Chadd 9196b7b2d80SAdrian Chadd return (0); 9206b7b2d80SAdrian Chadd } 9216b7b2d80SAdrian Chadd 9226b7b2d80SAdrian Chadd static int 9236b7b2d80SAdrian Chadd nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags) 9246b7b2d80SAdrian Chadd { 9256b7b2d80SAdrian Chadd struct nct_softc *sc; 9266b7b2d80SAdrian Chadd struct gpio_pin *pin; 9276b7b2d80SAdrian Chadd 928*7f8d2ed0SStéphane Rochoy sc = device_get_softc(dev); 929*7f8d2ed0SStéphane Rochoy 930*7f8d2ed0SStéphane Rochoy if (!NCT_PIN_IS_VALID(sc, pin_num)) 9316b7b2d80SAdrian Chadd return (EINVAL); 9326b7b2d80SAdrian Chadd 9336b7b2d80SAdrian Chadd pin = &sc->pins[pin_num]; 9346b7b2d80SAdrian Chadd if ((flags & pin->gp_caps) != flags) 9356b7b2d80SAdrian Chadd return (EINVAL); 9366b7b2d80SAdrian Chadd 9376b7b2d80SAdrian Chadd if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == 9386b7b2d80SAdrian Chadd (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { 9396b7b2d80SAdrian Chadd return (EINVAL); 9406b7b2d80SAdrian Chadd } 9416b7b2d80SAdrian Chadd if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) == 9426b7b2d80SAdrian Chadd (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) { 943155514eaSAndriy Gapon return (EINVAL); 944155514eaSAndriy Gapon } 945155514eaSAndriy Gapon if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) == 946155514eaSAndriy Gapon (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) { 9476b7b2d80SAdrian Chadd return (EINVAL); 9486b7b2d80SAdrian Chadd } 9496b7b2d80SAdrian Chadd 950155514eaSAndriy Gapon GPIO_ASSERT_UNLOCKED(sc); 951155514eaSAndriy Gapon GPIO_LOCK(sc); 952*7f8d2ed0SStéphane Rochoy 953*7f8d2ed0SStéphane Rochoy /* input or output */ 954155514eaSAndriy Gapon if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) != 0) { 955155514eaSAndriy Gapon nct_set_pin_input(sc, pin_num, (flags & GPIO_PIN_INPUT) != 0); 956155514eaSAndriy Gapon pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); 957155514eaSAndriy Gapon pin->gp_flags |= flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); 958155514eaSAndriy Gapon } 959*7f8d2ed0SStéphane Rochoy 960*7f8d2ed0SStéphane Rochoy /* invert */ 961155514eaSAndriy Gapon if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) != 0) { 962*7f8d2ed0SStéphane Rochoy nct_set_pin_inverted(sc, pin_num, 1); 963*7f8d2ed0SStéphane Rochoy pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT); 964*7f8d2ed0SStéphane Rochoy } else { 965*7f8d2ed0SStéphane Rochoy nct_set_pin_inverted(sc, pin_num, 0); 966155514eaSAndriy Gapon pin->gp_flags &= ~(GPIO_PIN_INVIN | GPIO_PIN_INVOUT); 967155514eaSAndriy Gapon } 968*7f8d2ed0SStéphane Rochoy 969*7f8d2ed0SStéphane Rochoy /* Open drain or push pull */ 970155514eaSAndriy Gapon if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) != 0) { 9716b7b2d80SAdrian Chadd if (flags & GPIO_PIN_OPENDRAIN) 9726b7b2d80SAdrian Chadd nct_set_pin_opendrain(sc, pin_num); 9736b7b2d80SAdrian Chadd else 9746b7b2d80SAdrian Chadd nct_set_pin_pushpull(sc, pin_num); 975155514eaSAndriy Gapon pin->gp_flags &= ~(GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL); 976155514eaSAndriy Gapon pin->gp_flags |= 977155514eaSAndriy Gapon flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL); 9786b7b2d80SAdrian Chadd } 9796b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 9806b7b2d80SAdrian Chadd 9816b7b2d80SAdrian Chadd return (0); 9826b7b2d80SAdrian Chadd } 9836b7b2d80SAdrian Chadd 9846b7b2d80SAdrian Chadd static device_method_t nct_methods[] = { 9856b7b2d80SAdrian Chadd /* Device interface */ 9866b7b2d80SAdrian Chadd DEVMETHOD(device_probe, nct_probe), 9876b7b2d80SAdrian Chadd DEVMETHOD(device_attach, nct_attach), 9886b7b2d80SAdrian Chadd DEVMETHOD(device_detach, nct_detach), 9896b7b2d80SAdrian Chadd 9906b7b2d80SAdrian Chadd /* GPIO */ 9916b7b2d80SAdrian Chadd DEVMETHOD(gpio_get_bus, nct_gpio_get_bus), 9926b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_max, nct_gpio_pin_max), 9936b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_get, nct_gpio_pin_get), 9946b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_set, nct_gpio_pin_set), 9956b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_toggle, nct_gpio_pin_toggle), 9966b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_getname, nct_gpio_pin_getname), 9976b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_getcaps, nct_gpio_pin_getcaps), 9986b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_getflags, nct_gpio_pin_getflags), 9996b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_setflags, nct_gpio_pin_setflags), 10006b7b2d80SAdrian Chadd 10016b7b2d80SAdrian Chadd DEVMETHOD_END 10026b7b2d80SAdrian Chadd }; 10036b7b2d80SAdrian Chadd 1004e3df342aSAndriy Gapon static driver_t nct_driver = { 10056b7b2d80SAdrian Chadd "gpio", 10066b7b2d80SAdrian Chadd nct_methods, 10076b7b2d80SAdrian Chadd sizeof(struct nct_softc) 10086b7b2d80SAdrian Chadd }; 10096b7b2d80SAdrian Chadd 101084c5f982SJohn Baldwin DRIVER_MODULE(nctgpio, superio, nct_driver, NULL, NULL); 10116b7b2d80SAdrian Chadd MODULE_DEPEND(nctgpio, gpiobus, 1, 1, 1); 1012e3df342aSAndriy Gapon MODULE_DEPEND(nctgpio, superio, 1, 1, 1); 1013e3df342aSAndriy Gapon MODULE_VERSION(nctgpio, 1); 1014