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 356b7b2d80SAdrian Chadd #include <sys/cdefs.h> 366b7b2d80SAdrian Chadd 376b7b2d80SAdrian Chadd #include <sys/param.h> 386b7b2d80SAdrian Chadd #include <sys/kernel.h> 396b7b2d80SAdrian Chadd #include <sys/systm.h> 406b7b2d80SAdrian Chadd #include <sys/bus.h> 416b7b2d80SAdrian Chadd #include <sys/eventhandler.h> 426b7b2d80SAdrian Chadd #include <sys/lock.h> 436b7b2d80SAdrian Chadd 446b7b2d80SAdrian Chadd #include <sys/module.h> 456b7b2d80SAdrian Chadd #include <sys/gpio.h> 466b7b2d80SAdrian Chadd 476b7b2d80SAdrian Chadd #include <machine/bus.h> 486b7b2d80SAdrian Chadd 496b7b2d80SAdrian Chadd #include <dev/gpio/gpiobusvar.h> 50e3df342aSAndriy Gapon #include <dev/superio/superio.h> 516b7b2d80SAdrian Chadd 526b7b2d80SAdrian Chadd #include "gpio_if.h" 536b7b2d80SAdrian Chadd 546b7b2d80SAdrian Chadd /* Logical Device Numbers. */ 556b7b2d80SAdrian Chadd #define NCT_LDN_GPIO 0x07 566b7b2d80SAdrian Chadd #define NCT_LDN_GPIO_MODE 0x0f 576b7b2d80SAdrian Chadd 586b7b2d80SAdrian Chadd /* Logical Device 7 */ 596b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_IOR 0xe0 606b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_DAT 0xe1 616b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_INV 0xe2 626b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_DST 0xe3 636b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_IOR 0xe4 646b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_DAT 0xe5 656b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_INV 0xe6 666b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_DST 0xe7 676b7b2d80SAdrian Chadd 686b7b2d80SAdrian Chadd /* Logical Device F */ 696b7b2d80SAdrian Chadd #define NCT_LDF_GPIO0_OUTCFG 0xe0 706b7b2d80SAdrian Chadd #define NCT_LDF_GPIO1_OUTCFG 0xe1 716b7b2d80SAdrian Chadd 72*155514eaSAndriy Gapon /* Direct I/O port access. */ 73*155514eaSAndriy Gapon #define NCT_IO_GSR 0 74*155514eaSAndriy Gapon #define NCT_IO_IOR 1 75*155514eaSAndriy Gapon #define NCT_IO_DAT 2 76*155514eaSAndriy Gapon #define NCT_IO_INV 3 776b7b2d80SAdrian Chadd 786b7b2d80SAdrian Chadd #define NCT_MAX_PIN 15 796b7b2d80SAdrian Chadd #define NCT_IS_VALID_PIN(_p) ((_p) >= 0 && (_p) <= NCT_MAX_PIN) 806b7b2d80SAdrian Chadd 81*155514eaSAndriy Gapon #define NCT_PIN_BIT(_p) (1 << ((_p) & 7)) 826b7b2d80SAdrian Chadd 836b7b2d80SAdrian Chadd #define NCT_GPIO_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ 846b7b2d80SAdrian Chadd GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \ 856b7b2d80SAdrian Chadd GPIO_PIN_INVIN | GPIO_PIN_INVOUT) 866b7b2d80SAdrian Chadd 87*155514eaSAndriy Gapon /* 88*155514eaSAndriy Gapon * Note that the values are important. 89*155514eaSAndriy Gapon * They match actual register offsets. 90*155514eaSAndriy Gapon */ 91*155514eaSAndriy Gapon typedef enum { 92*155514eaSAndriy Gapon REG_IOR = 0, 93*155514eaSAndriy Gapon REG_DAT = 1, 94*155514eaSAndriy Gapon REG_INV = 2, 95*155514eaSAndriy Gapon } reg_t; 96*155514eaSAndriy Gapon 976b7b2d80SAdrian Chadd struct nct_softc { 986b7b2d80SAdrian Chadd device_t dev; 99e3df342aSAndriy Gapon device_t dev_f; 1006b7b2d80SAdrian Chadd device_t busdev; 1016b7b2d80SAdrian Chadd struct mtx mtx; 102*155514eaSAndriy Gapon struct resource *iores; 103*155514eaSAndriy Gapon int iorid; 104*155514eaSAndriy Gapon int curgrp; 105*155514eaSAndriy Gapon struct { 106*155514eaSAndriy Gapon /* direction, 1: pin is input */ 107*155514eaSAndriy Gapon uint8_t ior[2]; 108*155514eaSAndriy Gapon /* output value */ 109*155514eaSAndriy Gapon uint8_t out[2]; 110*155514eaSAndriy Gapon /* whether out is valid */ 111*155514eaSAndriy Gapon uint8_t out_known[2]; 112*155514eaSAndriy Gapon /* inversion, 1: pin is inverted */ 113*155514eaSAndriy Gapon uint8_t inv[2]; 114*155514eaSAndriy Gapon } cache; 115523af57eSConrad Meyer struct gpio_pin pins[NCT_MAX_PIN + 1]; 1166b7b2d80SAdrian Chadd }; 1176b7b2d80SAdrian Chadd 1186b7b2d80SAdrian Chadd #define GPIO_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \ 1196b7b2d80SAdrian Chadd device_get_nameunit(dev), NULL, MTX_DEF) 1206b7b2d80SAdrian Chadd #define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx) 1216b7b2d80SAdrian Chadd #define GPIO_LOCK(_sc) mtx_lock(&(_sc)->mtx) 1226b7b2d80SAdrian Chadd #define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) 1236b7b2d80SAdrian Chadd #define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED) 1246b7b2d80SAdrian Chadd #define GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED) 1256b7b2d80SAdrian Chadd 1266b7b2d80SAdrian Chadd struct nuvoton_vendor_device_id { 1276b7b2d80SAdrian Chadd uint16_t chip_id; 1286b7b2d80SAdrian Chadd const char * descr; 1296b7b2d80SAdrian Chadd } nct_devs[] = { 1306b7b2d80SAdrian Chadd { 1316b7b2d80SAdrian Chadd .chip_id = 0x1061, 1326b7b2d80SAdrian Chadd .descr = "Nuvoton NCT5104D", 1336b7b2d80SAdrian Chadd }, 1346b7b2d80SAdrian Chadd { 1356b7b2d80SAdrian Chadd .chip_id = 0xc452, 1366b7b2d80SAdrian Chadd .descr = "Nuvoton NCT5104D (PC-Engines APU)", 1376b7b2d80SAdrian Chadd }, 1389d1208bbSOleksandr Tymoshenko { 1399d1208bbSOleksandr Tymoshenko .chip_id = 0xc453, 1409d1208bbSOleksandr Tymoshenko .descr = "Nuvoton NCT5104D (PC-Engines APU3)", 1419d1208bbSOleksandr Tymoshenko }, 1426b7b2d80SAdrian Chadd }; 1436b7b2d80SAdrian Chadd 144*155514eaSAndriy Gapon static void 145*155514eaSAndriy Gapon nct_io_set_group(struct nct_softc *sc, int group) 1466b7b2d80SAdrian Chadd { 1476b7b2d80SAdrian Chadd 148*155514eaSAndriy Gapon GPIO_ASSERT_LOCKED(sc); 149*155514eaSAndriy Gapon if (group != sc->curgrp) { 150*155514eaSAndriy Gapon bus_write_1(sc->iores, NCT_IO_GSR, group); 151*155514eaSAndriy Gapon sc->curgrp = group; 152*155514eaSAndriy Gapon } 153*155514eaSAndriy Gapon } 1546b7b2d80SAdrian Chadd 155*155514eaSAndriy Gapon static uint8_t 156*155514eaSAndriy Gapon nct_io_read(struct nct_softc *sc, int group, uint8_t reg) 157*155514eaSAndriy Gapon { 158*155514eaSAndriy Gapon nct_io_set_group(sc, group); 159*155514eaSAndriy Gapon return (bus_read_1(sc->iores, reg)); 160*155514eaSAndriy Gapon } 161*155514eaSAndriy Gapon 162*155514eaSAndriy Gapon static void 163*155514eaSAndriy Gapon nct_io_write(struct nct_softc *sc, int group, uint8_t reg, uint8_t val) 164*155514eaSAndriy Gapon { 165*155514eaSAndriy Gapon nct_io_set_group(sc, group); 166*155514eaSAndriy Gapon return (bus_write_1(sc->iores, reg, val)); 167*155514eaSAndriy Gapon } 168*155514eaSAndriy Gapon 169*155514eaSAndriy Gapon static uint8_t 170*155514eaSAndriy Gapon nct_get_ioreg(struct nct_softc *sc, reg_t reg, int group) 171*155514eaSAndriy Gapon { 172*155514eaSAndriy Gapon uint8_t ioreg; 173*155514eaSAndriy Gapon 174*155514eaSAndriy Gapon if (sc->iores != NULL) 175*155514eaSAndriy Gapon ioreg = NCT_IO_IOR + reg; 176*155514eaSAndriy Gapon else if (group == 0) 177*155514eaSAndriy Gapon ioreg = NCT_LD7_GPIO0_IOR + reg; 178*155514eaSAndriy Gapon else 179*155514eaSAndriy Gapon ioreg = NCT_LD7_GPIO1_IOR + reg; 180*155514eaSAndriy Gapon return (ioreg); 181*155514eaSAndriy Gapon } 182*155514eaSAndriy Gapon 183*155514eaSAndriy Gapon static uint8_t 184*155514eaSAndriy Gapon nct_read_reg(struct nct_softc *sc, reg_t reg, int group) 185*155514eaSAndriy Gapon { 186*155514eaSAndriy Gapon uint8_t ioreg; 187*155514eaSAndriy Gapon uint8_t val; 188*155514eaSAndriy Gapon 189*155514eaSAndriy Gapon ioreg = nct_get_ioreg(sc, reg, group); 190*155514eaSAndriy Gapon if (sc->iores != NULL) 191*155514eaSAndriy Gapon val = nct_io_read(sc, group, ioreg); 192*155514eaSAndriy Gapon else 193*155514eaSAndriy Gapon val = superio_read(sc->dev, ioreg); 194*155514eaSAndriy Gapon 195*155514eaSAndriy Gapon return (val); 196*155514eaSAndriy Gapon } 197*155514eaSAndriy Gapon 198*155514eaSAndriy Gapon #define GET_BIT(v, b) (((v) >> (b)) & 1) 199*155514eaSAndriy Gapon static bool 200*155514eaSAndriy Gapon nct_get_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num) 201*155514eaSAndriy Gapon { 202*155514eaSAndriy Gapon uint8_t bit; 203*155514eaSAndriy Gapon uint8_t group; 204*155514eaSAndriy Gapon uint8_t val; 205*155514eaSAndriy Gapon 206*155514eaSAndriy Gapon KASSERT(NCT_IS_VALID_PIN(pin_num), ("%s: invalid pin number %d", 207*155514eaSAndriy Gapon __func__, pin_num)); 208*155514eaSAndriy Gapon 209*155514eaSAndriy Gapon group = pin_num >> 3; 210*155514eaSAndriy Gapon bit = pin_num & 7; 211*155514eaSAndriy Gapon val = nct_read_reg(sc, reg, group); 212*155514eaSAndriy Gapon return (GET_BIT(val, bit)); 213*155514eaSAndriy Gapon } 214*155514eaSAndriy Gapon 215*155514eaSAndriy Gapon static int 216*155514eaSAndriy Gapon nct_get_pin_cache(struct nct_softc *sc, uint32_t pin_num, uint8_t *cache) 217*155514eaSAndriy Gapon { 218*155514eaSAndriy Gapon uint8_t bit; 219*155514eaSAndriy Gapon uint8_t group; 220*155514eaSAndriy Gapon uint8_t val; 221*155514eaSAndriy Gapon 222*155514eaSAndriy Gapon KASSERT(NCT_IS_VALID_PIN(pin_num), ("%s: invalid pin number %d", 223*155514eaSAndriy Gapon __func__, pin_num)); 224*155514eaSAndriy Gapon 225*155514eaSAndriy Gapon group = pin_num >> 3; 226*155514eaSAndriy Gapon bit = pin_num & 7; 227*155514eaSAndriy Gapon val = cache[group]; 228*155514eaSAndriy Gapon return (GET_BIT(val, bit)); 229*155514eaSAndriy Gapon } 230*155514eaSAndriy Gapon 231*155514eaSAndriy Gapon static void 232*155514eaSAndriy Gapon nct_write_reg(struct nct_softc *sc, reg_t reg, int group, uint8_t val) 233*155514eaSAndriy Gapon { 234*155514eaSAndriy Gapon uint8_t ioreg; 235*155514eaSAndriy Gapon 236*155514eaSAndriy Gapon ioreg = nct_get_ioreg(sc, reg, group); 237*155514eaSAndriy Gapon if (sc->iores != NULL) 238*155514eaSAndriy Gapon nct_io_write(sc, group, ioreg, val); 239*155514eaSAndriy Gapon else 240*155514eaSAndriy Gapon superio_write(sc->dev, ioreg, val); 241*155514eaSAndriy Gapon } 242*155514eaSAndriy Gapon 243*155514eaSAndriy Gapon static void 244*155514eaSAndriy Gapon nct_set_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num, bool val) 245*155514eaSAndriy Gapon { 246*155514eaSAndriy Gapon uint8_t *cache; 247*155514eaSAndriy Gapon uint8_t bit; 248*155514eaSAndriy Gapon uint8_t bitval; 249*155514eaSAndriy Gapon uint8_t group; 250*155514eaSAndriy Gapon uint8_t mask; 251*155514eaSAndriy Gapon 252*155514eaSAndriy Gapon KASSERT(NCT_IS_VALID_PIN(pin_num), 253*155514eaSAndriy Gapon ("%s: invalid pin number %d", __func__, pin_num)); 254*155514eaSAndriy Gapon KASSERT(reg == REG_IOR || reg == REG_INV, 255*155514eaSAndriy Gapon ("%s: unsupported register %d", __func__, reg)); 256*155514eaSAndriy Gapon 257*155514eaSAndriy Gapon group = pin_num >> 3; 258*155514eaSAndriy Gapon bit = pin_num & 7; 259*155514eaSAndriy Gapon mask = (uint8_t)1 << bit; 260*155514eaSAndriy Gapon bitval = (uint8_t)val << bit; 261*155514eaSAndriy Gapon 262*155514eaSAndriy Gapon if (reg == REG_IOR) 263*155514eaSAndriy Gapon cache = &sc->cache.ior[group]; 264*155514eaSAndriy Gapon else 265*155514eaSAndriy Gapon cache = &sc->cache.inv[group]; 266*155514eaSAndriy Gapon if ((*cache & mask) == bitval) 267*155514eaSAndriy Gapon return; 268*155514eaSAndriy Gapon *cache &= ~mask; 269*155514eaSAndriy Gapon *cache |= bitval; 270*155514eaSAndriy Gapon nct_write_reg(sc, reg, group, *cache); 2716b7b2d80SAdrian Chadd } 2726b7b2d80SAdrian Chadd 2736b7b2d80SAdrian Chadd /* 274*155514eaSAndriy Gapon * Set a pin to input (val is true) or output (val is false) mode. 2756b7b2d80SAdrian Chadd */ 2766b7b2d80SAdrian Chadd static void 277*155514eaSAndriy Gapon nct_set_pin_input(struct nct_softc *sc, uint32_t pin_num, bool val) 2786b7b2d80SAdrian Chadd { 279*155514eaSAndriy Gapon nct_set_pin_reg(sc, REG_IOR, pin_num, val); 2806b7b2d80SAdrian Chadd } 2816b7b2d80SAdrian Chadd 2826b7b2d80SAdrian Chadd /* 2836b7b2d80SAdrian Chadd * Check whether a pin is configured as an input. 2846b7b2d80SAdrian Chadd */ 2856b7b2d80SAdrian Chadd static bool 2866b7b2d80SAdrian Chadd nct_pin_is_input(struct nct_softc *sc, uint32_t pin_num) 2876b7b2d80SAdrian Chadd { 288*155514eaSAndriy Gapon return (nct_get_pin_cache(sc, pin_num, sc->cache.ior)); 2896b7b2d80SAdrian Chadd } 2906b7b2d80SAdrian Chadd 2916b7b2d80SAdrian Chadd /* 292*155514eaSAndriy Gapon * Set a pin to inverted (val is true) or normal (val is false) mode. 2936b7b2d80SAdrian Chadd */ 2946b7b2d80SAdrian Chadd static void 295*155514eaSAndriy Gapon nct_set_pin_inverted(struct nct_softc *sc, uint32_t pin_num, bool val) 2966b7b2d80SAdrian Chadd { 297*155514eaSAndriy Gapon nct_set_pin_reg(sc, REG_INV, pin_num, val); 2986b7b2d80SAdrian Chadd } 2996b7b2d80SAdrian Chadd 3006b7b2d80SAdrian Chadd static bool 3016b7b2d80SAdrian Chadd nct_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num) 3026b7b2d80SAdrian Chadd { 303*155514eaSAndriy Gapon return (nct_get_pin_cache(sc, pin_num, sc->cache.inv)); 3046b7b2d80SAdrian Chadd } 3056b7b2d80SAdrian Chadd 306*155514eaSAndriy Gapon /* 307*155514eaSAndriy Gapon * Write a value to an output pin. 308*155514eaSAndriy Gapon * NB: the hardware remembers last output value across switching from 309*155514eaSAndriy Gapon * output mode to input mode and back. 310*155514eaSAndriy Gapon * Writes to a pin in input mode are not allowed here as they cannot 311*155514eaSAndriy Gapon * have any effect and would corrupt the output value cache. 312*155514eaSAndriy Gapon */ 313*155514eaSAndriy Gapon static void 314*155514eaSAndriy Gapon nct_write_pin(struct nct_softc *sc, uint32_t pin_num, bool val) 315*155514eaSAndriy Gapon { 316*155514eaSAndriy Gapon uint8_t bit; 317*155514eaSAndriy Gapon uint8_t group; 318*155514eaSAndriy Gapon 319*155514eaSAndriy Gapon KASSERT(!nct_pin_is_input(sc, pin_num), ("attempt to write input pin")); 320*155514eaSAndriy Gapon group = pin_num >> 3; 321*155514eaSAndriy Gapon bit = pin_num & 7; 322*155514eaSAndriy Gapon if (GET_BIT(sc->cache.out_known[group], bit) && 323*155514eaSAndriy Gapon GET_BIT(sc->cache.out[group], bit) == val) { 324*155514eaSAndriy Gapon /* The pin is already in requested state. */ 325*155514eaSAndriy Gapon return; 326*155514eaSAndriy Gapon } 327*155514eaSAndriy Gapon sc->cache.out_known[group] |= 1 << bit; 328*155514eaSAndriy Gapon if (val) 329*155514eaSAndriy Gapon sc->cache.out[group] |= 1 << bit; 330*155514eaSAndriy Gapon else 331*155514eaSAndriy Gapon sc->cache.out[group] &= ~(1 << bit); 332*155514eaSAndriy Gapon nct_write_reg(sc, REG_DAT, group, sc->cache.out[group]); 333*155514eaSAndriy Gapon } 334*155514eaSAndriy Gapon 335*155514eaSAndriy Gapon /* 336*155514eaSAndriy Gapon * NB: state of an input pin cannot be cached, of course. 337*155514eaSAndriy Gapon * For an output we can either take the value from the cache if it's valid 338*155514eaSAndriy Gapon * or read the state from the hadrware and cache it. 339*155514eaSAndriy Gapon */ 340*155514eaSAndriy Gapon static bool 341*155514eaSAndriy Gapon nct_read_pin(struct nct_softc *sc, uint32_t pin_num) 342*155514eaSAndriy Gapon { 343*155514eaSAndriy Gapon uint8_t bit; 344*155514eaSAndriy Gapon uint8_t group; 345*155514eaSAndriy Gapon bool val; 346*155514eaSAndriy Gapon 347*155514eaSAndriy Gapon if (nct_pin_is_input(sc, pin_num)) 348*155514eaSAndriy Gapon return (nct_get_pin_reg(sc, REG_DAT, pin_num)); 349*155514eaSAndriy Gapon 350*155514eaSAndriy Gapon group = pin_num >> 3; 351*155514eaSAndriy Gapon bit = pin_num & 7; 352*155514eaSAndriy Gapon if (GET_BIT(sc->cache.out_known[group], bit)) 353*155514eaSAndriy Gapon return (GET_BIT(sc->cache.out[group], bit)); 354*155514eaSAndriy Gapon 355*155514eaSAndriy Gapon val = nct_get_pin_reg(sc, REG_DAT, pin_num); 356*155514eaSAndriy Gapon sc->cache.out_known[group] |= 1 << bit; 357*155514eaSAndriy Gapon if (val) 358*155514eaSAndriy Gapon sc->cache.out[group] |= 1 << bit; 359*155514eaSAndriy Gapon else 360*155514eaSAndriy Gapon sc->cache.out[group] &= ~(1 << bit); 361*155514eaSAndriy Gapon return (val); 362*155514eaSAndriy Gapon } 363*155514eaSAndriy Gapon 364*155514eaSAndriy Gapon static uint8_t 365*155514eaSAndriy Gapon nct_outcfg_addr(uint32_t pin_num) 366*155514eaSAndriy Gapon { 367*155514eaSAndriy Gapon KASSERT(NCT_IS_VALID_PIN(pin_num), ("%s: invalid pin number %d", 368*155514eaSAndriy Gapon __func__, pin_num)); 369*155514eaSAndriy Gapon if ((pin_num >> 3) == 0) 370*155514eaSAndriy Gapon return (NCT_LDF_GPIO0_OUTCFG); 371*155514eaSAndriy Gapon else 372*155514eaSAndriy Gapon return (NCT_LDF_GPIO1_OUTCFG); 373*155514eaSAndriy Gapon } 374*155514eaSAndriy Gapon 375*155514eaSAndriy Gapon /* 376*155514eaSAndriy Gapon * NB: PP/OD can be configured only via configuration registers. 377*155514eaSAndriy Gapon * Also, the registers are in a different logical device. 378*155514eaSAndriy Gapon * So, this is a special case. No caching too. 379*155514eaSAndriy Gapon */ 3806b7b2d80SAdrian Chadd static void 3816b7b2d80SAdrian Chadd nct_set_pin_opendrain(struct nct_softc *sc, uint32_t pin_num) 3826b7b2d80SAdrian Chadd { 3836b7b2d80SAdrian Chadd uint8_t reg; 3846b7b2d80SAdrian Chadd uint8_t outcfg; 3856b7b2d80SAdrian Chadd 3866b7b2d80SAdrian Chadd reg = nct_outcfg_addr(pin_num); 387e3df342aSAndriy Gapon outcfg = superio_read(sc->dev_f, reg); 388e3df342aSAndriy Gapon outcfg |= NCT_PIN_BIT(pin_num); 389e3df342aSAndriy Gapon superio_write(sc->dev_f, reg, outcfg); 3906b7b2d80SAdrian Chadd } 3916b7b2d80SAdrian Chadd 3926b7b2d80SAdrian Chadd static void 3936b7b2d80SAdrian Chadd nct_set_pin_pushpull(struct nct_softc *sc, uint32_t pin_num) 3946b7b2d80SAdrian Chadd { 3956b7b2d80SAdrian Chadd uint8_t reg; 3966b7b2d80SAdrian Chadd uint8_t outcfg; 3976b7b2d80SAdrian Chadd 3986b7b2d80SAdrian Chadd reg = nct_outcfg_addr(pin_num); 399e3df342aSAndriy Gapon outcfg = superio_read(sc->dev_f, reg); 400e3df342aSAndriy Gapon outcfg &= ~NCT_PIN_BIT(pin_num); 401e3df342aSAndriy Gapon superio_write(sc->dev_f, reg, outcfg); 4026b7b2d80SAdrian Chadd } 4036b7b2d80SAdrian Chadd 4046b7b2d80SAdrian Chadd static bool 4056b7b2d80SAdrian Chadd nct_pin_is_opendrain(struct nct_softc *sc, uint32_t pin_num) 4066b7b2d80SAdrian Chadd { 4076b7b2d80SAdrian Chadd uint8_t reg; 4086b7b2d80SAdrian Chadd uint8_t outcfg; 4096b7b2d80SAdrian Chadd 4106b7b2d80SAdrian Chadd reg = nct_outcfg_addr(pin_num); 411e3df342aSAndriy Gapon outcfg = superio_read(sc->dev_f, reg); 4126b7b2d80SAdrian Chadd return (outcfg & NCT_PIN_BIT(pin_num)); 4136b7b2d80SAdrian Chadd } 4146b7b2d80SAdrian Chadd 4156b7b2d80SAdrian Chadd static int 4166b7b2d80SAdrian Chadd nct_probe(device_t dev) 4176b7b2d80SAdrian Chadd { 418e3df342aSAndriy Gapon int j; 4196b7b2d80SAdrian Chadd uint16_t chipid; 4206b7b2d80SAdrian Chadd 421e3df342aSAndriy Gapon if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON) 422e3df342aSAndriy Gapon return (ENXIO); 423e3df342aSAndriy Gapon if (superio_get_type(dev) != SUPERIO_DEV_GPIO) 4246b7b2d80SAdrian Chadd return (ENXIO); 4256b7b2d80SAdrian Chadd 426e3df342aSAndriy Gapon /* 427e3df342aSAndriy Gapon * There are several GPIO devices, we attach only to one of them 428e3df342aSAndriy Gapon * and use the rest without attaching. 429e3df342aSAndriy Gapon */ 430e3df342aSAndriy Gapon if (superio_get_ldn(dev) != NCT_LDN_GPIO) 431e3df342aSAndriy Gapon return (ENXIO); 4326b7b2d80SAdrian Chadd 433e3df342aSAndriy Gapon chipid = superio_devid(dev); 43473a1170aSPedro F. Giffuni for (j = 0; j < nitems(nct_devs); j++) { 4356b7b2d80SAdrian Chadd if (chipid == nct_devs[j].chip_id) { 436e3df342aSAndriy Gapon device_set_desc(dev, "Nuvoton GPIO controller"); 4376b7b2d80SAdrian Chadd return (BUS_PROBE_DEFAULT); 4386b7b2d80SAdrian Chadd } 4396b7b2d80SAdrian Chadd } 4406b7b2d80SAdrian Chadd return (ENXIO); 4416b7b2d80SAdrian Chadd } 4426b7b2d80SAdrian Chadd 4436b7b2d80SAdrian Chadd static int 4446b7b2d80SAdrian Chadd nct_attach(device_t dev) 4456b7b2d80SAdrian Chadd { 4466b7b2d80SAdrian Chadd struct nct_softc *sc; 447*155514eaSAndriy Gapon device_t dev_8; 448*155514eaSAndriy Gapon uint16_t iobase; 449*155514eaSAndriy Gapon int err; 4506b7b2d80SAdrian Chadd int i; 4516b7b2d80SAdrian Chadd 4526b7b2d80SAdrian Chadd sc = device_get_softc(dev); 453e3df342aSAndriy Gapon sc->dev = dev; 454e3df342aSAndriy Gapon sc->dev_f = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_GPIO, 455e3df342aSAndriy Gapon NCT_LDN_GPIO_MODE); 456e3df342aSAndriy Gapon if (sc->dev_f == NULL) { 457e3df342aSAndriy Gapon device_printf(dev, "failed to find LDN F\n"); 4586b7b2d80SAdrian Chadd return (ENXIO); 4596b7b2d80SAdrian Chadd } 4606b7b2d80SAdrian Chadd 461*155514eaSAndriy Gapon /* 462*155514eaSAndriy Gapon * As strange as it may seem, I/O port base is configured in the 463*155514eaSAndriy Gapon * Logical Device 8 which is primarily used for WDT, but also plays 464*155514eaSAndriy Gapon * a role in GPIO configuration. 465*155514eaSAndriy Gapon */ 466*155514eaSAndriy Gapon iobase = 0; 467*155514eaSAndriy Gapon dev_8 = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_WDT, 8); 468*155514eaSAndriy Gapon if (dev_8 != NULL) 469*155514eaSAndriy Gapon iobase = superio_get_iobase(dev_8); 470*155514eaSAndriy Gapon if (iobase != 0 && iobase != 0xffff) { 471*155514eaSAndriy Gapon sc->curgrp = -1; 472*155514eaSAndriy Gapon sc->iorid = 0; 473*155514eaSAndriy Gapon err = bus_set_resource(dev, SYS_RES_IOPORT, sc->iorid, 474*155514eaSAndriy Gapon iobase, 7); 475*155514eaSAndriy Gapon if (err == 0) { 476*155514eaSAndriy Gapon sc->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 477*155514eaSAndriy Gapon &sc->iorid, RF_ACTIVE); 478*155514eaSAndriy Gapon if (sc->iores == NULL) { 479*155514eaSAndriy Gapon device_printf(dev, "can't map i/o space, " 480*155514eaSAndriy Gapon "iobase=0x%04x\n", iobase); 481*155514eaSAndriy Gapon } 482*155514eaSAndriy Gapon } else { 483*155514eaSAndriy Gapon device_printf(dev, 484*155514eaSAndriy Gapon "failed to set io port resource at 0x%x\n", iobase); 485*155514eaSAndriy Gapon } 486*155514eaSAndriy Gapon } 487*155514eaSAndriy Gapon 4886b7b2d80SAdrian Chadd /* Enable gpio0 and gpio1. */ 489e3df342aSAndriy Gapon superio_dev_enable(dev, 0x03); 490e3df342aSAndriy Gapon 491e3df342aSAndriy Gapon GPIO_LOCK_INIT(sc); 492e3df342aSAndriy Gapon GPIO_LOCK(sc); 4936b7b2d80SAdrian Chadd 494*155514eaSAndriy Gapon sc->cache.inv[0] = nct_read_reg(sc, REG_INV, 0); 495*155514eaSAndriy Gapon sc->cache.inv[1] = nct_read_reg(sc, REG_INV, 1); 496*155514eaSAndriy Gapon sc->cache.ior[0] = nct_read_reg(sc, REG_IOR, 0); 497*155514eaSAndriy Gapon sc->cache.ior[1] = nct_read_reg(sc, REG_IOR, 1); 498*155514eaSAndriy Gapon 499*155514eaSAndriy Gapon /* 500*155514eaSAndriy Gapon * Caching input values is meaningless as an input can be changed at any 501*155514eaSAndriy Gapon * time by an external agent. But outputs are controlled by this 502*155514eaSAndriy Gapon * driver, so it can cache their state. Also, the hardware remembers 503*155514eaSAndriy Gapon * the output state of a pin when the pin is switched to input mode and 504*155514eaSAndriy Gapon * then back to output mode. So, the cache stays valid. 505*155514eaSAndriy Gapon * The only problem is with pins that are in input mode at the attach 506*155514eaSAndriy Gapon * time. For them the output state is not known until it is set by the 507*155514eaSAndriy Gapon * driver for the first time. 508*155514eaSAndriy Gapon * 'out' and 'out_known' bits form a tri-state output cache: 509*155514eaSAndriy Gapon * |-----+-----------+---------| 510*155514eaSAndriy Gapon * | out | out_known | cache | 511*155514eaSAndriy Gapon * |-----+-----------+---------| 512*155514eaSAndriy Gapon * | X | 0 | invalid | 513*155514eaSAndriy Gapon * | 0 | 1 | 0 | 514*155514eaSAndriy Gapon * | 1 | 1 | 1 | 515*155514eaSAndriy Gapon * |-----+-----------+---------| 516*155514eaSAndriy Gapon */ 517*155514eaSAndriy Gapon sc->cache.out[0] = nct_read_reg(sc, REG_DAT, 0); 518*155514eaSAndriy Gapon sc->cache.out[1] = nct_read_reg(sc, REG_DAT, 1); 519*155514eaSAndriy Gapon sc->cache.out_known[0] = ~sc->cache.ior[0]; 520*155514eaSAndriy Gapon sc->cache.out_known[1] = ~sc->cache.ior[1]; 521*155514eaSAndriy Gapon 5226b7b2d80SAdrian Chadd for (i = 0; i <= NCT_MAX_PIN; i++) { 5236b7b2d80SAdrian Chadd struct gpio_pin *pin; 5246b7b2d80SAdrian Chadd 5256b7b2d80SAdrian Chadd pin = &sc->pins[i]; 5266b7b2d80SAdrian Chadd pin->gp_pin = i; 5276b7b2d80SAdrian Chadd pin->gp_caps = NCT_GPIO_CAPS; 5286b7b2d80SAdrian Chadd pin->gp_flags = 0; 5296b7b2d80SAdrian Chadd 53002226256SAndriy Gapon snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%02o", i); 5316b7b2d80SAdrian Chadd pin->gp_name[GPIOMAXNAME - 1] = '\0'; 5326b7b2d80SAdrian Chadd 5336b7b2d80SAdrian Chadd if (nct_pin_is_input(sc, i)) 5346b7b2d80SAdrian Chadd pin->gp_flags |= GPIO_PIN_INPUT; 5356b7b2d80SAdrian Chadd else 5366b7b2d80SAdrian Chadd pin->gp_flags |= GPIO_PIN_OUTPUT; 5376b7b2d80SAdrian Chadd 5386b7b2d80SAdrian Chadd if (nct_pin_is_opendrain(sc, i)) 5396b7b2d80SAdrian Chadd pin->gp_flags |= GPIO_PIN_OPENDRAIN; 5406b7b2d80SAdrian Chadd else 5416b7b2d80SAdrian Chadd pin->gp_flags |= GPIO_PIN_PUSHPULL; 5426b7b2d80SAdrian Chadd 5436b7b2d80SAdrian Chadd if (nct_pin_is_inverted(sc, i)) 5446b7b2d80SAdrian Chadd pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT); 5456b7b2d80SAdrian Chadd } 5466b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 5476b7b2d80SAdrian Chadd 5486b7b2d80SAdrian Chadd sc->busdev = gpiobus_attach_bus(dev); 5496b7b2d80SAdrian Chadd if (sc->busdev == NULL) { 5506b7b2d80SAdrian Chadd GPIO_LOCK_DESTROY(sc); 5516b7b2d80SAdrian Chadd return (ENXIO); 5526b7b2d80SAdrian Chadd } 5536b7b2d80SAdrian Chadd 5546b7b2d80SAdrian Chadd return (0); 5556b7b2d80SAdrian Chadd } 5566b7b2d80SAdrian Chadd 5576b7b2d80SAdrian Chadd static int 5586b7b2d80SAdrian Chadd nct_detach(device_t dev) 5596b7b2d80SAdrian Chadd { 5606b7b2d80SAdrian Chadd struct nct_softc *sc; 5616b7b2d80SAdrian Chadd 5626b7b2d80SAdrian Chadd sc = device_get_softc(dev); 5636b7b2d80SAdrian Chadd gpiobus_detach_bus(dev); 5646b7b2d80SAdrian Chadd 565*155514eaSAndriy Gapon if (sc->iores != NULL) 566*155514eaSAndriy Gapon bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->iores); 5676b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 5686b7b2d80SAdrian Chadd GPIO_LOCK_DESTROY(sc); 5696b7b2d80SAdrian Chadd 5706b7b2d80SAdrian Chadd return (0); 5716b7b2d80SAdrian Chadd } 5726b7b2d80SAdrian Chadd 5736b7b2d80SAdrian Chadd static device_t 5746b7b2d80SAdrian Chadd nct_gpio_get_bus(device_t dev) 5756b7b2d80SAdrian Chadd { 5766b7b2d80SAdrian Chadd struct nct_softc *sc; 5776b7b2d80SAdrian Chadd 5786b7b2d80SAdrian Chadd sc = device_get_softc(dev); 5796b7b2d80SAdrian Chadd 5806b7b2d80SAdrian Chadd return (sc->busdev); 5816b7b2d80SAdrian Chadd } 5826b7b2d80SAdrian Chadd 5836b7b2d80SAdrian Chadd static int 5846b7b2d80SAdrian Chadd nct_gpio_pin_max(device_t dev, int *npins) 5856b7b2d80SAdrian Chadd { 5866b7b2d80SAdrian Chadd *npins = NCT_MAX_PIN; 5876b7b2d80SAdrian Chadd 5886b7b2d80SAdrian Chadd return (0); 5896b7b2d80SAdrian Chadd } 5906b7b2d80SAdrian Chadd 5916b7b2d80SAdrian Chadd static int 5926b7b2d80SAdrian Chadd nct_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value) 5936b7b2d80SAdrian Chadd { 5946b7b2d80SAdrian Chadd struct nct_softc *sc; 5956b7b2d80SAdrian Chadd 5966b7b2d80SAdrian Chadd if (!NCT_IS_VALID_PIN(pin_num)) 5976b7b2d80SAdrian Chadd return (EINVAL); 5986b7b2d80SAdrian Chadd 5996b7b2d80SAdrian Chadd sc = device_get_softc(dev); 6006b7b2d80SAdrian Chadd GPIO_LOCK(sc); 601*155514eaSAndriy Gapon if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) { 602*155514eaSAndriy Gapon GPIO_UNLOCK(sc); 603*155514eaSAndriy Gapon return (EINVAL); 604*155514eaSAndriy Gapon } 6056b7b2d80SAdrian Chadd nct_write_pin(sc, pin_num, pin_value); 6066b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 6076b7b2d80SAdrian Chadd 6086b7b2d80SAdrian Chadd return (0); 6096b7b2d80SAdrian Chadd } 6106b7b2d80SAdrian Chadd 6116b7b2d80SAdrian Chadd static int 6126b7b2d80SAdrian Chadd nct_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value) 6136b7b2d80SAdrian Chadd { 6146b7b2d80SAdrian Chadd struct nct_softc *sc; 6156b7b2d80SAdrian Chadd 6166b7b2d80SAdrian Chadd if (!NCT_IS_VALID_PIN(pin_num)) 6176b7b2d80SAdrian Chadd return (EINVAL); 6186b7b2d80SAdrian Chadd 6196b7b2d80SAdrian Chadd sc = device_get_softc(dev); 6206b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 6216b7b2d80SAdrian Chadd GPIO_LOCK(sc); 6226b7b2d80SAdrian Chadd *pin_value = nct_read_pin(sc, pin_num); 6236b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 6246b7b2d80SAdrian Chadd 6256b7b2d80SAdrian Chadd return (0); 6266b7b2d80SAdrian Chadd } 6276b7b2d80SAdrian Chadd 6286b7b2d80SAdrian Chadd static int 6296b7b2d80SAdrian Chadd nct_gpio_pin_toggle(device_t dev, uint32_t pin_num) 6306b7b2d80SAdrian Chadd { 6316b7b2d80SAdrian Chadd struct nct_softc *sc; 6326b7b2d80SAdrian Chadd 6336b7b2d80SAdrian Chadd if (!NCT_IS_VALID_PIN(pin_num)) 6346b7b2d80SAdrian Chadd return (EINVAL); 6356b7b2d80SAdrian Chadd 6366b7b2d80SAdrian Chadd sc = device_get_softc(dev); 6376b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 6386b7b2d80SAdrian Chadd GPIO_LOCK(sc); 639*155514eaSAndriy Gapon if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) { 640*155514eaSAndriy Gapon GPIO_UNLOCK(sc); 641*155514eaSAndriy Gapon return (EINVAL); 642*155514eaSAndriy Gapon } 6436b7b2d80SAdrian Chadd if (nct_read_pin(sc, pin_num)) 6446b7b2d80SAdrian Chadd nct_write_pin(sc, pin_num, 0); 6456b7b2d80SAdrian Chadd else 6466b7b2d80SAdrian Chadd nct_write_pin(sc, pin_num, 1); 6476b7b2d80SAdrian Chadd 6486b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 6496b7b2d80SAdrian Chadd 6506b7b2d80SAdrian Chadd return (0); 6516b7b2d80SAdrian Chadd } 6526b7b2d80SAdrian Chadd 6536b7b2d80SAdrian Chadd static int 6546b7b2d80SAdrian Chadd nct_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps) 6556b7b2d80SAdrian Chadd { 6566b7b2d80SAdrian Chadd struct nct_softc *sc; 6576b7b2d80SAdrian Chadd 6586b7b2d80SAdrian Chadd if (!NCT_IS_VALID_PIN(pin_num)) 6596b7b2d80SAdrian Chadd return (EINVAL); 6606b7b2d80SAdrian Chadd 6616b7b2d80SAdrian Chadd sc = device_get_softc(dev); 6626b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 6636b7b2d80SAdrian Chadd GPIO_LOCK(sc); 6646b7b2d80SAdrian Chadd *caps = sc->pins[pin_num].gp_caps; 6656b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 6666b7b2d80SAdrian Chadd 6676b7b2d80SAdrian Chadd return (0); 6686b7b2d80SAdrian Chadd } 6696b7b2d80SAdrian Chadd 6706b7b2d80SAdrian Chadd static int 6716b7b2d80SAdrian Chadd nct_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags) 6726b7b2d80SAdrian Chadd { 6736b7b2d80SAdrian Chadd struct nct_softc *sc; 6746b7b2d80SAdrian Chadd 6756b7b2d80SAdrian Chadd if (!NCT_IS_VALID_PIN(pin_num)) 6766b7b2d80SAdrian Chadd return (EINVAL); 6776b7b2d80SAdrian Chadd 6786b7b2d80SAdrian Chadd sc = device_get_softc(dev); 6796b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 6806b7b2d80SAdrian Chadd GPIO_LOCK(sc); 6816b7b2d80SAdrian Chadd *flags = sc->pins[pin_num].gp_flags; 6826b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 6836b7b2d80SAdrian Chadd 6846b7b2d80SAdrian Chadd return (0); 6856b7b2d80SAdrian Chadd } 6866b7b2d80SAdrian Chadd 6876b7b2d80SAdrian Chadd static int 6886b7b2d80SAdrian Chadd nct_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name) 6896b7b2d80SAdrian Chadd { 6906b7b2d80SAdrian Chadd struct nct_softc *sc; 6916b7b2d80SAdrian Chadd 6926b7b2d80SAdrian Chadd if (!NCT_IS_VALID_PIN(pin_num)) 6936b7b2d80SAdrian Chadd return (EINVAL); 6946b7b2d80SAdrian Chadd 6956b7b2d80SAdrian Chadd sc = device_get_softc(dev); 6966b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 6976b7b2d80SAdrian Chadd GPIO_LOCK(sc); 6986b7b2d80SAdrian Chadd memcpy(name, sc->pins[pin_num].gp_name, GPIOMAXNAME); 6996b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 7006b7b2d80SAdrian Chadd 7016b7b2d80SAdrian Chadd return (0); 7026b7b2d80SAdrian Chadd } 7036b7b2d80SAdrian Chadd 7046b7b2d80SAdrian Chadd static int 7056b7b2d80SAdrian Chadd nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags) 7066b7b2d80SAdrian Chadd { 7076b7b2d80SAdrian Chadd struct nct_softc *sc; 7086b7b2d80SAdrian Chadd struct gpio_pin *pin; 7096b7b2d80SAdrian Chadd 7106b7b2d80SAdrian Chadd if (!NCT_IS_VALID_PIN(pin_num)) 7116b7b2d80SAdrian Chadd return (EINVAL); 7126b7b2d80SAdrian Chadd 7136b7b2d80SAdrian Chadd sc = device_get_softc(dev); 7146b7b2d80SAdrian Chadd pin = &sc->pins[pin_num]; 7156b7b2d80SAdrian Chadd if ((flags & pin->gp_caps) != flags) 7166b7b2d80SAdrian Chadd return (EINVAL); 7176b7b2d80SAdrian Chadd 7186b7b2d80SAdrian Chadd if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == 7196b7b2d80SAdrian Chadd (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { 7206b7b2d80SAdrian Chadd return (EINVAL); 7216b7b2d80SAdrian Chadd } 7226b7b2d80SAdrian Chadd if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) == 7236b7b2d80SAdrian Chadd (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) { 724*155514eaSAndriy Gapon return (EINVAL); 725*155514eaSAndriy Gapon } 726*155514eaSAndriy Gapon if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) == 727*155514eaSAndriy Gapon (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) { 7286b7b2d80SAdrian Chadd return (EINVAL); 7296b7b2d80SAdrian Chadd } 7306b7b2d80SAdrian Chadd 731*155514eaSAndriy Gapon GPIO_ASSERT_UNLOCKED(sc); 732*155514eaSAndriy Gapon GPIO_LOCK(sc); 733*155514eaSAndriy Gapon if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) != 0) { 734*155514eaSAndriy Gapon nct_set_pin_input(sc, pin_num, (flags & GPIO_PIN_INPUT) != 0); 735*155514eaSAndriy Gapon pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); 736*155514eaSAndriy Gapon pin->gp_flags |= flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); 737*155514eaSAndriy Gapon } 738*155514eaSAndriy Gapon if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) != 0) { 739*155514eaSAndriy Gapon nct_set_pin_inverted(sc, pin_num, 740*155514eaSAndriy Gapon (flags & GPIO_PIN_INVIN) != 0); 741*155514eaSAndriy Gapon pin->gp_flags &= ~(GPIO_PIN_INVIN | GPIO_PIN_INVOUT); 742*155514eaSAndriy Gapon pin->gp_flags |= flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT); 743*155514eaSAndriy Gapon } 744*155514eaSAndriy Gapon if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) != 0) { 7456b7b2d80SAdrian Chadd if (flags & GPIO_PIN_OPENDRAIN) 7466b7b2d80SAdrian Chadd nct_set_pin_opendrain(sc, pin_num); 7476b7b2d80SAdrian Chadd else 7486b7b2d80SAdrian Chadd nct_set_pin_pushpull(sc, pin_num); 749*155514eaSAndriy Gapon pin->gp_flags &= ~(GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL); 750*155514eaSAndriy Gapon pin->gp_flags |= 751*155514eaSAndriy Gapon flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL); 7526b7b2d80SAdrian Chadd } 7536b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 7546b7b2d80SAdrian Chadd 7556b7b2d80SAdrian Chadd return (0); 7566b7b2d80SAdrian Chadd } 7576b7b2d80SAdrian Chadd 7586b7b2d80SAdrian Chadd static device_method_t nct_methods[] = { 7596b7b2d80SAdrian Chadd /* Device interface */ 7606b7b2d80SAdrian Chadd DEVMETHOD(device_probe, nct_probe), 7616b7b2d80SAdrian Chadd DEVMETHOD(device_attach, nct_attach), 7626b7b2d80SAdrian Chadd DEVMETHOD(device_detach, nct_detach), 7636b7b2d80SAdrian Chadd 7646b7b2d80SAdrian Chadd /* GPIO */ 7656b7b2d80SAdrian Chadd DEVMETHOD(gpio_get_bus, nct_gpio_get_bus), 7666b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_max, nct_gpio_pin_max), 7676b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_get, nct_gpio_pin_get), 7686b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_set, nct_gpio_pin_set), 7696b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_toggle, nct_gpio_pin_toggle), 7706b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_getname, nct_gpio_pin_getname), 7716b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_getcaps, nct_gpio_pin_getcaps), 7726b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_getflags, nct_gpio_pin_getflags), 7736b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_setflags, nct_gpio_pin_setflags), 7746b7b2d80SAdrian Chadd 7756b7b2d80SAdrian Chadd DEVMETHOD_END 7766b7b2d80SAdrian Chadd }; 7776b7b2d80SAdrian Chadd 778e3df342aSAndriy Gapon static driver_t nct_driver = { 7796b7b2d80SAdrian Chadd "gpio", 7806b7b2d80SAdrian Chadd nct_methods, 7816b7b2d80SAdrian Chadd sizeof(struct nct_softc) 7826b7b2d80SAdrian Chadd }; 7836b7b2d80SAdrian Chadd 7846b7b2d80SAdrian Chadd static devclass_t nct_devclass; 7856b7b2d80SAdrian Chadd 786e3df342aSAndriy Gapon DRIVER_MODULE(nctgpio, superio, nct_driver, nct_devclass, NULL, NULL); 7876b7b2d80SAdrian Chadd MODULE_DEPEND(nctgpio, gpiobus, 1, 1, 1); 788e3df342aSAndriy Gapon MODULE_DEPEND(nctgpio, superio, 1, 1, 1); 789e3df342aSAndriy Gapon MODULE_VERSION(nctgpio, 1); 790e3df342aSAndriy Gapon 791