xref: /freebsd/sys/dev/nctgpio/nctgpio.c (revision e3df342a32debf2cc8bdc20582afd8adeec35775)
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