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> 50*e3df342aSAndriy 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 726b7b2d80SAdrian Chadd 736b7b2d80SAdrian Chadd #define NCT_MAX_PIN 15 746b7b2d80SAdrian Chadd #define NCT_IS_VALID_PIN(_p) ((_p) >= 0 && (_p) <= NCT_MAX_PIN) 756b7b2d80SAdrian Chadd 766b7b2d80SAdrian Chadd #define NCT_PIN_BIT(_p) (1 << ((_p) % 8)) 776b7b2d80SAdrian Chadd 786b7b2d80SAdrian Chadd #define NCT_GPIO_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ 796b7b2d80SAdrian Chadd GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \ 806b7b2d80SAdrian Chadd GPIO_PIN_INVIN | GPIO_PIN_INVOUT) 816b7b2d80SAdrian Chadd 826b7b2d80SAdrian Chadd struct nct_softc { 836b7b2d80SAdrian Chadd device_t dev; 84*e3df342aSAndriy Gapon device_t dev_f; 856b7b2d80SAdrian Chadd device_t busdev; 866b7b2d80SAdrian Chadd struct mtx mtx; 87523af57eSConrad Meyer struct gpio_pin pins[NCT_MAX_PIN + 1]; 886b7b2d80SAdrian Chadd }; 896b7b2d80SAdrian Chadd 906b7b2d80SAdrian Chadd #define GPIO_LOCK_INIT(_sc) mtx_init(&(_sc)->mtx, \ 916b7b2d80SAdrian Chadd device_get_nameunit(dev), NULL, MTX_DEF) 926b7b2d80SAdrian Chadd #define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->mtx) 936b7b2d80SAdrian Chadd #define GPIO_LOCK(_sc) mtx_lock(&(_sc)->mtx) 946b7b2d80SAdrian Chadd #define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->mtx) 956b7b2d80SAdrian Chadd #define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_OWNED) 966b7b2d80SAdrian Chadd #define GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->mtx, MA_NOTOWNED) 976b7b2d80SAdrian Chadd 986b7b2d80SAdrian Chadd struct nuvoton_vendor_device_id { 996b7b2d80SAdrian Chadd uint16_t chip_id; 1006b7b2d80SAdrian Chadd const char * descr; 1016b7b2d80SAdrian Chadd } nct_devs[] = { 1026b7b2d80SAdrian Chadd { 1036b7b2d80SAdrian Chadd .chip_id = 0x1061, 1046b7b2d80SAdrian Chadd .descr = "Nuvoton NCT5104D", 1056b7b2d80SAdrian Chadd }, 1066b7b2d80SAdrian Chadd { 1076b7b2d80SAdrian Chadd .chip_id = 0xc452, 1086b7b2d80SAdrian Chadd .descr = "Nuvoton NCT5104D (PC-Engines APU)", 1096b7b2d80SAdrian Chadd }, 1109d1208bbSOleksandr Tymoshenko { 1119d1208bbSOleksandr Tymoshenko .chip_id = 0xc453, 1129d1208bbSOleksandr Tymoshenko .descr = "Nuvoton NCT5104D (PC-Engines APU3)", 1139d1208bbSOleksandr Tymoshenko }, 1146b7b2d80SAdrian Chadd }; 1156b7b2d80SAdrian Chadd 1166b7b2d80SAdrian Chadd /* 1176b7b2d80SAdrian Chadd * Get the GPIO Input/Output register address 1186b7b2d80SAdrian Chadd * for a pin. 1196b7b2d80SAdrian Chadd */ 1206b7b2d80SAdrian Chadd static uint8_t 1216b7b2d80SAdrian Chadd nct_ior_addr(uint32_t pin_num) 1226b7b2d80SAdrian Chadd { 1236b7b2d80SAdrian Chadd uint8_t addr; 1246b7b2d80SAdrian Chadd 1256b7b2d80SAdrian Chadd addr = NCT_LD7_GPIO0_IOR; 1266b7b2d80SAdrian Chadd if (pin_num > 7) 1276b7b2d80SAdrian Chadd addr = NCT_LD7_GPIO1_IOR; 1286b7b2d80SAdrian Chadd 1296b7b2d80SAdrian Chadd return (addr); 1306b7b2d80SAdrian Chadd } 1316b7b2d80SAdrian Chadd 1326b7b2d80SAdrian Chadd /* 1336b7b2d80SAdrian Chadd * Get the GPIO Data register address for a pin. 1346b7b2d80SAdrian Chadd */ 1356b7b2d80SAdrian Chadd static uint8_t 1366b7b2d80SAdrian Chadd nct_dat_addr(uint32_t pin_num) 1376b7b2d80SAdrian Chadd { 1386b7b2d80SAdrian Chadd uint8_t addr; 1396b7b2d80SAdrian Chadd 1406b7b2d80SAdrian Chadd addr = NCT_LD7_GPIO0_DAT; 1416b7b2d80SAdrian Chadd if (pin_num > 7) 1426b7b2d80SAdrian Chadd addr = NCT_LD7_GPIO1_DAT; 1436b7b2d80SAdrian Chadd 1446b7b2d80SAdrian Chadd return (addr); 1456b7b2d80SAdrian Chadd } 1466b7b2d80SAdrian Chadd 1476b7b2d80SAdrian Chadd /* 1486b7b2d80SAdrian Chadd * Get the GPIO Inversion register address 1496b7b2d80SAdrian Chadd * for a pin. 1506b7b2d80SAdrian Chadd */ 1516b7b2d80SAdrian Chadd static uint8_t 1526b7b2d80SAdrian Chadd nct_inv_addr(uint32_t pin_num) 1536b7b2d80SAdrian Chadd { 1546b7b2d80SAdrian Chadd uint8_t addr; 1556b7b2d80SAdrian Chadd 1566b7b2d80SAdrian Chadd addr = NCT_LD7_GPIO0_INV; 1576b7b2d80SAdrian Chadd if (pin_num > 7) 1586b7b2d80SAdrian Chadd addr = NCT_LD7_GPIO1_INV; 1596b7b2d80SAdrian Chadd 1606b7b2d80SAdrian Chadd return (addr); 1616b7b2d80SAdrian Chadd } 1626b7b2d80SAdrian Chadd 1636b7b2d80SAdrian Chadd /* 1646b7b2d80SAdrian Chadd * Get the GPIO Output Configuration/Mode 1656b7b2d80SAdrian Chadd * register address for a pin. 1666b7b2d80SAdrian Chadd */ 1676b7b2d80SAdrian Chadd static uint8_t 1686b7b2d80SAdrian Chadd nct_outcfg_addr(uint32_t pin_num) 1696b7b2d80SAdrian Chadd { 1706b7b2d80SAdrian Chadd uint8_t addr; 1716b7b2d80SAdrian Chadd 1726b7b2d80SAdrian Chadd addr = NCT_LDF_GPIO0_OUTCFG; 1736b7b2d80SAdrian Chadd if (pin_num > 7) 1746b7b2d80SAdrian Chadd addr = NCT_LDF_GPIO1_OUTCFG; 1756b7b2d80SAdrian Chadd 1766b7b2d80SAdrian Chadd return (addr); 1776b7b2d80SAdrian Chadd } 1786b7b2d80SAdrian Chadd 1796b7b2d80SAdrian Chadd /* 1806b7b2d80SAdrian Chadd * Set a pin to output mode. 1816b7b2d80SAdrian Chadd */ 1826b7b2d80SAdrian Chadd static void 1836b7b2d80SAdrian Chadd nct_set_pin_is_output(struct nct_softc *sc, uint32_t pin_num) 1846b7b2d80SAdrian Chadd { 1856b7b2d80SAdrian Chadd uint8_t reg; 1866b7b2d80SAdrian Chadd uint8_t ior; 1876b7b2d80SAdrian Chadd 1886b7b2d80SAdrian Chadd reg = nct_ior_addr(pin_num); 189*e3df342aSAndriy Gapon ior = superio_read(sc->dev, reg); 1906b7b2d80SAdrian Chadd ior &= ~(NCT_PIN_BIT(pin_num)); 191*e3df342aSAndriy Gapon superio_write(sc->dev, reg, ior); 1926b7b2d80SAdrian Chadd } 1936b7b2d80SAdrian Chadd 1946b7b2d80SAdrian Chadd /* 1956b7b2d80SAdrian Chadd * Set a pin to input mode. 1966b7b2d80SAdrian Chadd */ 1976b7b2d80SAdrian Chadd static void 1986b7b2d80SAdrian Chadd nct_set_pin_is_input(struct nct_softc *sc, uint32_t pin_num) 1996b7b2d80SAdrian Chadd { 2006b7b2d80SAdrian Chadd uint8_t reg; 2016b7b2d80SAdrian Chadd uint8_t ior; 2026b7b2d80SAdrian Chadd 2036b7b2d80SAdrian Chadd reg = nct_ior_addr(pin_num); 204*e3df342aSAndriy Gapon ior = superio_read(sc->dev, reg); 2056b7b2d80SAdrian Chadd ior |= NCT_PIN_BIT(pin_num); 206*e3df342aSAndriy Gapon superio_write(sc->dev, reg, ior); 2076b7b2d80SAdrian Chadd } 2086b7b2d80SAdrian Chadd 2096b7b2d80SAdrian Chadd /* 2106b7b2d80SAdrian Chadd * Check whether a pin is configured as an input. 2116b7b2d80SAdrian Chadd */ 2126b7b2d80SAdrian Chadd static bool 2136b7b2d80SAdrian Chadd nct_pin_is_input(struct nct_softc *sc, uint32_t pin_num) 2146b7b2d80SAdrian Chadd { 2156b7b2d80SAdrian Chadd uint8_t reg; 2166b7b2d80SAdrian Chadd uint8_t ior; 2176b7b2d80SAdrian Chadd 2186b7b2d80SAdrian Chadd reg = nct_ior_addr(pin_num); 219*e3df342aSAndriy Gapon ior = superio_read(sc->dev, reg); 2206b7b2d80SAdrian Chadd 2216b7b2d80SAdrian Chadd return (ior & NCT_PIN_BIT(pin_num)); 2226b7b2d80SAdrian Chadd } 2236b7b2d80SAdrian Chadd 2246b7b2d80SAdrian Chadd /* 2256b7b2d80SAdrian Chadd * Write a value to an output pin. 2266b7b2d80SAdrian Chadd */ 2276b7b2d80SAdrian Chadd static void 2286b7b2d80SAdrian Chadd nct_write_pin(struct nct_softc *sc, uint32_t pin_num, uint8_t data) 2296b7b2d80SAdrian Chadd { 2306b7b2d80SAdrian Chadd uint8_t reg; 2316b7b2d80SAdrian Chadd uint8_t value; 2326b7b2d80SAdrian Chadd 2336b7b2d80SAdrian Chadd reg = nct_dat_addr(pin_num); 234*e3df342aSAndriy Gapon value = superio_read(sc->dev, reg); 2356b7b2d80SAdrian Chadd if (data) 2366b7b2d80SAdrian Chadd value |= NCT_PIN_BIT(pin_num); 2376b7b2d80SAdrian Chadd else 2386b7b2d80SAdrian Chadd value &= ~(NCT_PIN_BIT(pin_num)); 2396b7b2d80SAdrian Chadd 240*e3df342aSAndriy Gapon superio_write(sc->dev, reg, value); 2416b7b2d80SAdrian Chadd } 2426b7b2d80SAdrian Chadd 2436b7b2d80SAdrian Chadd static bool 2446b7b2d80SAdrian Chadd nct_read_pin(struct nct_softc *sc, uint32_t pin_num) 2456b7b2d80SAdrian Chadd { 2466b7b2d80SAdrian Chadd uint8_t reg; 2476b7b2d80SAdrian Chadd 2486b7b2d80SAdrian Chadd reg = nct_dat_addr(pin_num); 2496b7b2d80SAdrian Chadd 250*e3df342aSAndriy Gapon return (superio_read(sc->dev, reg) & NCT_PIN_BIT(pin_num)); 2516b7b2d80SAdrian Chadd } 2526b7b2d80SAdrian Chadd 2536b7b2d80SAdrian Chadd static void 2546b7b2d80SAdrian Chadd nct_set_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num) 2556b7b2d80SAdrian Chadd { 2566b7b2d80SAdrian Chadd uint8_t reg; 2576b7b2d80SAdrian Chadd uint8_t inv; 2586b7b2d80SAdrian Chadd 2596b7b2d80SAdrian Chadd reg = nct_inv_addr(pin_num); 260*e3df342aSAndriy Gapon inv = superio_read(sc->dev, reg); 2616b7b2d80SAdrian Chadd inv |= (NCT_PIN_BIT(pin_num)); 262*e3df342aSAndriy Gapon superio_write(sc->dev, reg, inv); 2636b7b2d80SAdrian Chadd } 2646b7b2d80SAdrian Chadd 2656b7b2d80SAdrian Chadd static void 2666b7b2d80SAdrian Chadd nct_set_pin_not_inverted(struct nct_softc *sc, uint32_t pin_num) 2676b7b2d80SAdrian Chadd { 2686b7b2d80SAdrian Chadd uint8_t reg; 2696b7b2d80SAdrian Chadd uint8_t inv; 2706b7b2d80SAdrian Chadd 2716b7b2d80SAdrian Chadd reg = nct_inv_addr(pin_num); 272*e3df342aSAndriy Gapon inv = superio_read(sc->dev, reg); 2736b7b2d80SAdrian Chadd inv &= ~(NCT_PIN_BIT(pin_num)); 274*e3df342aSAndriy Gapon superio_write(sc->dev, reg, inv); 2756b7b2d80SAdrian Chadd } 2766b7b2d80SAdrian Chadd 2776b7b2d80SAdrian Chadd static bool 2786b7b2d80SAdrian Chadd nct_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num) 2796b7b2d80SAdrian Chadd { 2806b7b2d80SAdrian Chadd uint8_t reg; 2816b7b2d80SAdrian Chadd uint8_t inv; 2826b7b2d80SAdrian Chadd 2836b7b2d80SAdrian Chadd reg = nct_inv_addr(pin_num); 284*e3df342aSAndriy Gapon inv = superio_read(sc->dev, reg); 2856b7b2d80SAdrian Chadd 2866b7b2d80SAdrian Chadd return (inv & NCT_PIN_BIT(pin_num)); 2876b7b2d80SAdrian Chadd } 2886b7b2d80SAdrian Chadd 2896b7b2d80SAdrian Chadd static void 2906b7b2d80SAdrian Chadd nct_set_pin_opendrain(struct nct_softc *sc, uint32_t pin_num) 2916b7b2d80SAdrian Chadd { 2926b7b2d80SAdrian Chadd uint8_t reg; 2936b7b2d80SAdrian Chadd uint8_t outcfg; 2946b7b2d80SAdrian Chadd 2956b7b2d80SAdrian Chadd reg = nct_outcfg_addr(pin_num); 296*e3df342aSAndriy Gapon outcfg = superio_read(sc->dev_f, reg); 297*e3df342aSAndriy Gapon outcfg |= NCT_PIN_BIT(pin_num); 298*e3df342aSAndriy Gapon superio_write(sc->dev_f, reg, outcfg); 2996b7b2d80SAdrian Chadd } 3006b7b2d80SAdrian Chadd 3016b7b2d80SAdrian Chadd static void 3026b7b2d80SAdrian Chadd nct_set_pin_pushpull(struct nct_softc *sc, uint32_t pin_num) 3036b7b2d80SAdrian Chadd { 3046b7b2d80SAdrian Chadd uint8_t reg; 3056b7b2d80SAdrian Chadd uint8_t outcfg; 3066b7b2d80SAdrian Chadd 3076b7b2d80SAdrian Chadd reg = nct_outcfg_addr(pin_num); 308*e3df342aSAndriy Gapon outcfg = superio_read(sc->dev_f, reg); 309*e3df342aSAndriy Gapon outcfg &= ~NCT_PIN_BIT(pin_num); 310*e3df342aSAndriy Gapon superio_write(sc->dev_f, reg, outcfg); 3116b7b2d80SAdrian Chadd } 3126b7b2d80SAdrian Chadd 3136b7b2d80SAdrian Chadd static bool 3146b7b2d80SAdrian Chadd nct_pin_is_opendrain(struct nct_softc *sc, uint32_t pin_num) 3156b7b2d80SAdrian Chadd { 3166b7b2d80SAdrian Chadd uint8_t reg; 3176b7b2d80SAdrian Chadd uint8_t outcfg; 3186b7b2d80SAdrian Chadd 3196b7b2d80SAdrian Chadd reg = nct_outcfg_addr(pin_num); 320*e3df342aSAndriy Gapon outcfg = superio_read(sc->dev_f, reg); 3216b7b2d80SAdrian Chadd return (outcfg & NCT_PIN_BIT(pin_num)); 3226b7b2d80SAdrian Chadd } 3236b7b2d80SAdrian Chadd 3246b7b2d80SAdrian Chadd static int 3256b7b2d80SAdrian Chadd nct_probe(device_t dev) 3266b7b2d80SAdrian Chadd { 327*e3df342aSAndriy Gapon int j; 3286b7b2d80SAdrian Chadd uint16_t chipid; 3296b7b2d80SAdrian Chadd 330*e3df342aSAndriy Gapon if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON) 331*e3df342aSAndriy Gapon return (ENXIO); 332*e3df342aSAndriy Gapon if (superio_get_type(dev) != SUPERIO_DEV_GPIO) 3336b7b2d80SAdrian Chadd return (ENXIO); 3346b7b2d80SAdrian Chadd 335*e3df342aSAndriy Gapon /* 336*e3df342aSAndriy Gapon * There are several GPIO devices, we attach only to one of them 337*e3df342aSAndriy Gapon * and use the rest without attaching. 338*e3df342aSAndriy Gapon */ 339*e3df342aSAndriy Gapon if (superio_get_ldn(dev) != NCT_LDN_GPIO) 340*e3df342aSAndriy Gapon return (ENXIO); 3416b7b2d80SAdrian Chadd 342*e3df342aSAndriy Gapon chipid = superio_devid(dev); 34373a1170aSPedro F. Giffuni for (j = 0; j < nitems(nct_devs); j++) { 3446b7b2d80SAdrian Chadd if (chipid == nct_devs[j].chip_id) { 345*e3df342aSAndriy Gapon device_set_desc(dev, "Nuvoton GPIO controller"); 3466b7b2d80SAdrian Chadd return (BUS_PROBE_DEFAULT); 3476b7b2d80SAdrian Chadd } 3486b7b2d80SAdrian Chadd } 3496b7b2d80SAdrian Chadd return (ENXIO); 3506b7b2d80SAdrian Chadd } 3516b7b2d80SAdrian Chadd 3526b7b2d80SAdrian Chadd static int 3536b7b2d80SAdrian Chadd nct_attach(device_t dev) 3546b7b2d80SAdrian Chadd { 3556b7b2d80SAdrian Chadd struct nct_softc *sc; 3566b7b2d80SAdrian Chadd int i; 3576b7b2d80SAdrian Chadd 3586b7b2d80SAdrian Chadd sc = device_get_softc(dev); 359*e3df342aSAndriy Gapon sc->dev = dev; 360*e3df342aSAndriy Gapon sc->dev_f = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_GPIO, 361*e3df342aSAndriy Gapon NCT_LDN_GPIO_MODE); 362*e3df342aSAndriy Gapon if (sc->dev_f == NULL) { 363*e3df342aSAndriy Gapon device_printf(dev, "failed to find LDN F\n"); 3646b7b2d80SAdrian Chadd return (ENXIO); 3656b7b2d80SAdrian Chadd } 3666b7b2d80SAdrian Chadd 3676b7b2d80SAdrian Chadd /* Enable gpio0 and gpio1. */ 368*e3df342aSAndriy Gapon superio_dev_enable(dev, 0x03); 369*e3df342aSAndriy Gapon 370*e3df342aSAndriy Gapon GPIO_LOCK_INIT(sc); 371*e3df342aSAndriy Gapon GPIO_LOCK(sc); 3726b7b2d80SAdrian Chadd 3736b7b2d80SAdrian Chadd for (i = 0; i <= NCT_MAX_PIN; i++) { 3746b7b2d80SAdrian Chadd struct gpio_pin *pin; 3756b7b2d80SAdrian Chadd 3766b7b2d80SAdrian Chadd pin = &sc->pins[i]; 3776b7b2d80SAdrian Chadd pin->gp_pin = i; 3786b7b2d80SAdrian Chadd pin->gp_caps = NCT_GPIO_CAPS; 3796b7b2d80SAdrian Chadd pin->gp_flags = 0; 3806b7b2d80SAdrian Chadd 38102226256SAndriy Gapon snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%02o", i); 3826b7b2d80SAdrian Chadd pin->gp_name[GPIOMAXNAME - 1] = '\0'; 3836b7b2d80SAdrian Chadd 3846b7b2d80SAdrian Chadd if (nct_pin_is_input(sc, i)) 3856b7b2d80SAdrian Chadd pin->gp_flags |= GPIO_PIN_INPUT; 3866b7b2d80SAdrian Chadd else 3876b7b2d80SAdrian Chadd pin->gp_flags |= GPIO_PIN_OUTPUT; 3886b7b2d80SAdrian Chadd 3896b7b2d80SAdrian Chadd if (nct_pin_is_opendrain(sc, i)) 3906b7b2d80SAdrian Chadd pin->gp_flags |= GPIO_PIN_OPENDRAIN; 3916b7b2d80SAdrian Chadd else 3926b7b2d80SAdrian Chadd pin->gp_flags |= GPIO_PIN_PUSHPULL; 3936b7b2d80SAdrian Chadd 3946b7b2d80SAdrian Chadd if (nct_pin_is_inverted(sc, i)) 3956b7b2d80SAdrian Chadd pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT); 3966b7b2d80SAdrian Chadd } 3976b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 3986b7b2d80SAdrian Chadd 3996b7b2d80SAdrian Chadd sc->busdev = gpiobus_attach_bus(dev); 4006b7b2d80SAdrian Chadd if (sc->busdev == NULL) { 4016b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 4026b7b2d80SAdrian Chadd GPIO_LOCK_DESTROY(sc); 4036b7b2d80SAdrian Chadd return (ENXIO); 4046b7b2d80SAdrian Chadd } 4056b7b2d80SAdrian Chadd 4066b7b2d80SAdrian Chadd return (0); 4076b7b2d80SAdrian Chadd } 4086b7b2d80SAdrian Chadd 4096b7b2d80SAdrian Chadd static int 4106b7b2d80SAdrian Chadd nct_detach(device_t dev) 4116b7b2d80SAdrian Chadd { 4126b7b2d80SAdrian Chadd struct nct_softc *sc; 4136b7b2d80SAdrian Chadd 4146b7b2d80SAdrian Chadd sc = device_get_softc(dev); 4156b7b2d80SAdrian Chadd gpiobus_detach_bus(dev); 4166b7b2d80SAdrian Chadd 4176b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 4186b7b2d80SAdrian Chadd GPIO_LOCK_DESTROY(sc); 4196b7b2d80SAdrian Chadd 4206b7b2d80SAdrian Chadd return (0); 4216b7b2d80SAdrian Chadd } 4226b7b2d80SAdrian Chadd 4236b7b2d80SAdrian Chadd static device_t 4246b7b2d80SAdrian Chadd nct_gpio_get_bus(device_t dev) 4256b7b2d80SAdrian Chadd { 4266b7b2d80SAdrian Chadd struct nct_softc *sc; 4276b7b2d80SAdrian Chadd 4286b7b2d80SAdrian Chadd sc = device_get_softc(dev); 4296b7b2d80SAdrian Chadd 4306b7b2d80SAdrian Chadd return (sc->busdev); 4316b7b2d80SAdrian Chadd } 4326b7b2d80SAdrian Chadd 4336b7b2d80SAdrian Chadd static int 4346b7b2d80SAdrian Chadd nct_gpio_pin_max(device_t dev, int *npins) 4356b7b2d80SAdrian Chadd { 4366b7b2d80SAdrian Chadd *npins = NCT_MAX_PIN; 4376b7b2d80SAdrian Chadd 4386b7b2d80SAdrian Chadd return (0); 4396b7b2d80SAdrian Chadd } 4406b7b2d80SAdrian Chadd 4416b7b2d80SAdrian Chadd static int 4426b7b2d80SAdrian Chadd nct_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value) 4436b7b2d80SAdrian Chadd { 4446b7b2d80SAdrian Chadd struct nct_softc *sc; 4456b7b2d80SAdrian Chadd 4466b7b2d80SAdrian Chadd if (!NCT_IS_VALID_PIN(pin_num)) 4476b7b2d80SAdrian Chadd return (EINVAL); 4486b7b2d80SAdrian Chadd 4496b7b2d80SAdrian Chadd sc = device_get_softc(dev); 4506b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 4516b7b2d80SAdrian Chadd GPIO_LOCK(sc); 4526b7b2d80SAdrian Chadd nct_write_pin(sc, pin_num, pin_value); 4536b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 4546b7b2d80SAdrian Chadd 4556b7b2d80SAdrian Chadd return (0); 4566b7b2d80SAdrian Chadd } 4576b7b2d80SAdrian Chadd 4586b7b2d80SAdrian Chadd static int 4596b7b2d80SAdrian Chadd nct_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value) 4606b7b2d80SAdrian Chadd { 4616b7b2d80SAdrian Chadd struct nct_softc *sc; 4626b7b2d80SAdrian Chadd 4636b7b2d80SAdrian Chadd if (!NCT_IS_VALID_PIN(pin_num)) 4646b7b2d80SAdrian Chadd return (EINVAL); 4656b7b2d80SAdrian Chadd 4666b7b2d80SAdrian Chadd sc = device_get_softc(dev); 4676b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 4686b7b2d80SAdrian Chadd GPIO_LOCK(sc); 4696b7b2d80SAdrian Chadd *pin_value = nct_read_pin(sc, pin_num); 4706b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 4716b7b2d80SAdrian Chadd 4726b7b2d80SAdrian Chadd return (0); 4736b7b2d80SAdrian Chadd } 4746b7b2d80SAdrian Chadd 4756b7b2d80SAdrian Chadd static int 4766b7b2d80SAdrian Chadd nct_gpio_pin_toggle(device_t dev, uint32_t pin_num) 4776b7b2d80SAdrian Chadd { 4786b7b2d80SAdrian Chadd struct nct_softc *sc; 4796b7b2d80SAdrian Chadd 4806b7b2d80SAdrian Chadd if (!NCT_IS_VALID_PIN(pin_num)) 4816b7b2d80SAdrian Chadd return (EINVAL); 4826b7b2d80SAdrian Chadd 4836b7b2d80SAdrian Chadd sc = device_get_softc(dev); 4846b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 4856b7b2d80SAdrian Chadd GPIO_LOCK(sc); 4866b7b2d80SAdrian Chadd if (nct_read_pin(sc, pin_num)) 4876b7b2d80SAdrian Chadd nct_write_pin(sc, pin_num, 0); 4886b7b2d80SAdrian Chadd else 4896b7b2d80SAdrian Chadd nct_write_pin(sc, pin_num, 1); 4906b7b2d80SAdrian Chadd 4916b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 4926b7b2d80SAdrian Chadd 4936b7b2d80SAdrian Chadd return (0); 4946b7b2d80SAdrian Chadd } 4956b7b2d80SAdrian Chadd 4966b7b2d80SAdrian Chadd static int 4976b7b2d80SAdrian Chadd nct_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps) 4986b7b2d80SAdrian Chadd { 4996b7b2d80SAdrian Chadd struct nct_softc *sc; 5006b7b2d80SAdrian Chadd 5016b7b2d80SAdrian Chadd if (!NCT_IS_VALID_PIN(pin_num)) 5026b7b2d80SAdrian Chadd return (EINVAL); 5036b7b2d80SAdrian Chadd 5046b7b2d80SAdrian Chadd sc = device_get_softc(dev); 5056b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 5066b7b2d80SAdrian Chadd GPIO_LOCK(sc); 5076b7b2d80SAdrian Chadd *caps = sc->pins[pin_num].gp_caps; 5086b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 5096b7b2d80SAdrian Chadd 5106b7b2d80SAdrian Chadd return (0); 5116b7b2d80SAdrian Chadd } 5126b7b2d80SAdrian Chadd 5136b7b2d80SAdrian Chadd static int 5146b7b2d80SAdrian Chadd nct_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags) 5156b7b2d80SAdrian Chadd { 5166b7b2d80SAdrian Chadd struct nct_softc *sc; 5176b7b2d80SAdrian Chadd 5186b7b2d80SAdrian Chadd if (!NCT_IS_VALID_PIN(pin_num)) 5196b7b2d80SAdrian Chadd return (EINVAL); 5206b7b2d80SAdrian Chadd 5216b7b2d80SAdrian Chadd sc = device_get_softc(dev); 5226b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 5236b7b2d80SAdrian Chadd GPIO_LOCK(sc); 5246b7b2d80SAdrian Chadd *flags = sc->pins[pin_num].gp_flags; 5256b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 5266b7b2d80SAdrian Chadd 5276b7b2d80SAdrian Chadd return (0); 5286b7b2d80SAdrian Chadd } 5296b7b2d80SAdrian Chadd 5306b7b2d80SAdrian Chadd static int 5316b7b2d80SAdrian Chadd nct_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name) 5326b7b2d80SAdrian Chadd { 5336b7b2d80SAdrian Chadd struct nct_softc *sc; 5346b7b2d80SAdrian Chadd 5356b7b2d80SAdrian Chadd if (!NCT_IS_VALID_PIN(pin_num)) 5366b7b2d80SAdrian Chadd return (EINVAL); 5376b7b2d80SAdrian Chadd 5386b7b2d80SAdrian Chadd sc = device_get_softc(dev); 5396b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 5406b7b2d80SAdrian Chadd GPIO_LOCK(sc); 5416b7b2d80SAdrian Chadd memcpy(name, sc->pins[pin_num].gp_name, GPIOMAXNAME); 5426b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 5436b7b2d80SAdrian Chadd 5446b7b2d80SAdrian Chadd return (0); 5456b7b2d80SAdrian Chadd } 5466b7b2d80SAdrian Chadd 5476b7b2d80SAdrian Chadd static int 5486b7b2d80SAdrian Chadd nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags) 5496b7b2d80SAdrian Chadd { 5506b7b2d80SAdrian Chadd struct nct_softc *sc; 5516b7b2d80SAdrian Chadd struct gpio_pin *pin; 5526b7b2d80SAdrian Chadd 5536b7b2d80SAdrian Chadd if (!NCT_IS_VALID_PIN(pin_num)) 5546b7b2d80SAdrian Chadd return (EINVAL); 5556b7b2d80SAdrian Chadd 5566b7b2d80SAdrian Chadd sc = device_get_softc(dev); 5576b7b2d80SAdrian Chadd pin = &sc->pins[pin_num]; 5586b7b2d80SAdrian Chadd if ((flags & pin->gp_caps) != flags) 5596b7b2d80SAdrian Chadd return (EINVAL); 5606b7b2d80SAdrian Chadd 5616b7b2d80SAdrian Chadd GPIO_ASSERT_UNLOCKED(sc); 5626b7b2d80SAdrian Chadd GPIO_LOCK(sc); 5636b7b2d80SAdrian Chadd if (flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { 5646b7b2d80SAdrian Chadd if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == 5656b7b2d80SAdrian Chadd (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { 5666b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 5676b7b2d80SAdrian Chadd return (EINVAL); 5686b7b2d80SAdrian Chadd } 5696b7b2d80SAdrian Chadd 5706b7b2d80SAdrian Chadd if (flags & GPIO_PIN_INPUT) 5716b7b2d80SAdrian Chadd nct_set_pin_is_input(sc, pin_num); 5726b7b2d80SAdrian Chadd else 5736b7b2d80SAdrian Chadd nct_set_pin_is_output(sc, pin_num); 5746b7b2d80SAdrian Chadd } 5756b7b2d80SAdrian Chadd 5766b7b2d80SAdrian Chadd if (flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) { 5776b7b2d80SAdrian Chadd if (flags & GPIO_PIN_INPUT) { 5786b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 5796b7b2d80SAdrian Chadd return (EINVAL); 5806b7b2d80SAdrian Chadd } 5816b7b2d80SAdrian Chadd 5826b7b2d80SAdrian Chadd if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) == 5836b7b2d80SAdrian Chadd (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) { 5846b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 5856b7b2d80SAdrian Chadd return (EINVAL); 5866b7b2d80SAdrian Chadd } 5876b7b2d80SAdrian Chadd 5886b7b2d80SAdrian Chadd if (flags & GPIO_PIN_OPENDRAIN) 5896b7b2d80SAdrian Chadd nct_set_pin_opendrain(sc, pin_num); 5906b7b2d80SAdrian Chadd else 5916b7b2d80SAdrian Chadd nct_set_pin_pushpull(sc, pin_num); 5926b7b2d80SAdrian Chadd } 5936b7b2d80SAdrian Chadd 5946b7b2d80SAdrian Chadd if (flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) { 5956b7b2d80SAdrian Chadd if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) != 5966b7b2d80SAdrian Chadd (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) { 5976b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 5986b7b2d80SAdrian Chadd return (EINVAL); 5996b7b2d80SAdrian Chadd } 6006b7b2d80SAdrian Chadd 6016b7b2d80SAdrian Chadd if (flags & GPIO_PIN_INVIN) 6026b7b2d80SAdrian Chadd nct_set_pin_is_inverted(sc, pin_num); 6036b7b2d80SAdrian Chadd else 6046b7b2d80SAdrian Chadd nct_set_pin_not_inverted(sc, pin_num); 6056b7b2d80SAdrian Chadd } 6066b7b2d80SAdrian Chadd 6076b7b2d80SAdrian Chadd pin->gp_flags = flags; 6086b7b2d80SAdrian Chadd GPIO_UNLOCK(sc); 6096b7b2d80SAdrian Chadd 6106b7b2d80SAdrian Chadd return (0); 6116b7b2d80SAdrian Chadd } 6126b7b2d80SAdrian Chadd 6136b7b2d80SAdrian Chadd static device_method_t nct_methods[] = { 6146b7b2d80SAdrian Chadd /* Device interface */ 6156b7b2d80SAdrian Chadd DEVMETHOD(device_probe, nct_probe), 6166b7b2d80SAdrian Chadd DEVMETHOD(device_attach, nct_attach), 6176b7b2d80SAdrian Chadd DEVMETHOD(device_detach, nct_detach), 6186b7b2d80SAdrian Chadd 6196b7b2d80SAdrian Chadd /* GPIO */ 6206b7b2d80SAdrian Chadd DEVMETHOD(gpio_get_bus, nct_gpio_get_bus), 6216b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_max, nct_gpio_pin_max), 6226b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_get, nct_gpio_pin_get), 6236b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_set, nct_gpio_pin_set), 6246b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_toggle, nct_gpio_pin_toggle), 6256b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_getname, nct_gpio_pin_getname), 6266b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_getcaps, nct_gpio_pin_getcaps), 6276b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_getflags, nct_gpio_pin_getflags), 6286b7b2d80SAdrian Chadd DEVMETHOD(gpio_pin_setflags, nct_gpio_pin_setflags), 6296b7b2d80SAdrian Chadd 6306b7b2d80SAdrian Chadd DEVMETHOD_END 6316b7b2d80SAdrian Chadd }; 6326b7b2d80SAdrian Chadd 633*e3df342aSAndriy Gapon static driver_t nct_driver = { 6346b7b2d80SAdrian Chadd "gpio", 6356b7b2d80SAdrian Chadd nct_methods, 6366b7b2d80SAdrian Chadd sizeof(struct nct_softc) 6376b7b2d80SAdrian Chadd }; 6386b7b2d80SAdrian Chadd 6396b7b2d80SAdrian Chadd static devclass_t nct_devclass; 6406b7b2d80SAdrian Chadd 641*e3df342aSAndriy Gapon DRIVER_MODULE(nctgpio, superio, nct_driver, nct_devclass, NULL, NULL); 6426b7b2d80SAdrian Chadd MODULE_DEPEND(nctgpio, gpiobus, 1, 1, 1); 643*e3df342aSAndriy Gapon MODULE_DEPEND(nctgpio, superio, 1, 1, 1); 644*e3df342aSAndriy Gapon MODULE_VERSION(nctgpio, 1); 645*e3df342aSAndriy Gapon 646