xref: /freebsd/sys/dev/nctgpio/nctgpio.c (revision 523af57ea24624de9430a0bf980b515ba7148b53)
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/rman.h>
466b7b2d80SAdrian Chadd #include <sys/gpio.h>
476b7b2d80SAdrian Chadd 
486b7b2d80SAdrian Chadd #include <isa/isavar.h>
496b7b2d80SAdrian Chadd 
506b7b2d80SAdrian Chadd #include <machine/bus.h>
516b7b2d80SAdrian Chadd #include <machine/resource.h>
526b7b2d80SAdrian Chadd 
536b7b2d80SAdrian Chadd #include <dev/gpio/gpiobusvar.h>
546b7b2d80SAdrian Chadd 
556b7b2d80SAdrian Chadd #include "gpio_if.h"
566b7b2d80SAdrian Chadd 
576b7b2d80SAdrian Chadd /*
586b7b2d80SAdrian Chadd  * Global configuration registers (CR).
596b7b2d80SAdrian Chadd  */
606b7b2d80SAdrian Chadd #define NCT_CR_LDN			0x07	/* Logical Device Number */
616b7b2d80SAdrian Chadd #define NCT_CR_CHIP_ID			0x20 	/* Chip ID */
626b7b2d80SAdrian Chadd #define NCT_CR_CHIP_ID_H		0x20 	/* Chip ID (high byte) */
636b7b2d80SAdrian Chadd #define NCT_CR_CHIP_ID_L		0x21 	/* Chip ID (low byte) */
646b7b2d80SAdrian Chadd #define NCT_CR_OPT_1			0x26	/* Global Options (1) */
656b7b2d80SAdrian Chadd 
666b7b2d80SAdrian Chadd /* Logical Device Numbers. */
676b7b2d80SAdrian Chadd #define NCT_LDN_GPIO			0x07
686b7b2d80SAdrian Chadd #define NCT_LDN_GPIO_CFG		0x08
696b7b2d80SAdrian Chadd #define NCT_LDN_GPIO_MODE		0x0f
706b7b2d80SAdrian Chadd 
716b7b2d80SAdrian Chadd /* Logical Device 7 */
726b7b2d80SAdrian Chadd #define NCT_LD7_GPIO_ENABLE		0x30
736b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_IOR		0xe0
746b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_DAT		0xe1
756b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_INV		0xe2
766b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_DST		0xe3
776b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_IOR		0xe4
786b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_DAT		0xe5
796b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_INV		0xe6
806b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_DST		0xe7
816b7b2d80SAdrian Chadd 
826b7b2d80SAdrian Chadd /* Logical Device F */
836b7b2d80SAdrian Chadd #define NCT_LDF_GPIO0_OUTCFG		0xe0
846b7b2d80SAdrian Chadd #define NCT_LDF_GPIO1_OUTCFG		0xe1
856b7b2d80SAdrian Chadd 
866b7b2d80SAdrian Chadd #define NCT_EXTFUNC_ENTER		0x87
876b7b2d80SAdrian Chadd #define NCT_EXTFUNC_EXIT		0xaa
886b7b2d80SAdrian Chadd 
896b7b2d80SAdrian Chadd #define NCT_MAX_PIN			15
906b7b2d80SAdrian Chadd #define NCT_IS_VALID_PIN(_p)	((_p) >= 0 && (_p) <= NCT_MAX_PIN)
916b7b2d80SAdrian Chadd 
926b7b2d80SAdrian Chadd #define NCT_PIN_BIT(_p)         (1 << ((_p) % 8))
936b7b2d80SAdrian Chadd 
946b7b2d80SAdrian Chadd #define NCT_GPIO_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
956b7b2d80SAdrian Chadd 	GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \
966b7b2d80SAdrian Chadd 	GPIO_PIN_INVIN | GPIO_PIN_INVOUT)
976b7b2d80SAdrian Chadd 
986b7b2d80SAdrian Chadd struct nct_softc {
996b7b2d80SAdrian Chadd 	device_t			dev;
1006b7b2d80SAdrian Chadd 	device_t			busdev;
1016b7b2d80SAdrian Chadd 	struct mtx			mtx;
1026b7b2d80SAdrian Chadd 	struct resource			*portres;
1036b7b2d80SAdrian Chadd 	int				rid;
104*523af57eSConrad Meyer 	struct gpio_pin			pins[NCT_MAX_PIN + 1];
1056b7b2d80SAdrian Chadd };
1066b7b2d80SAdrian Chadd 
1076b7b2d80SAdrian Chadd #define GPIO_LOCK_INIT(_sc)	mtx_init(&(_sc)->mtx,		\
1086b7b2d80SAdrian Chadd 		device_get_nameunit(dev), NULL, MTX_DEF)
1096b7b2d80SAdrian Chadd #define GPIO_LOCK_DESTROY(_sc)		mtx_destroy(&(_sc)->mtx)
1106b7b2d80SAdrian Chadd #define GPIO_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
1116b7b2d80SAdrian Chadd #define GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->mtx)
1126b7b2d80SAdrian Chadd #define GPIO_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_OWNED)
1136b7b2d80SAdrian Chadd #define GPIO_ASSERT_UNLOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_NOTOWNED)
1146b7b2d80SAdrian Chadd 
1156b7b2d80SAdrian Chadd #define NCT_BARRIER_WRITE(_sc)	\
1166b7b2d80SAdrian Chadd 	bus_barrier((_sc)->portres, 0, 2, BUS_SPACE_BARRIER_WRITE)
1176b7b2d80SAdrian Chadd 
1186b7b2d80SAdrian Chadd #define NCT_BARRIER_READ_WRITE(_sc)	\
1196b7b2d80SAdrian Chadd 	bus_barrier((_sc)->portres, 0, 2, \
1206b7b2d80SAdrian Chadd 		BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)
1216b7b2d80SAdrian Chadd 
1226b7b2d80SAdrian Chadd static void	ext_cfg_enter(struct nct_softc *);
1236b7b2d80SAdrian Chadd static void	ext_cfg_exit(struct nct_softc *);
1246b7b2d80SAdrian Chadd 
1256b7b2d80SAdrian Chadd /*
1266b7b2d80SAdrian Chadd  * Potential Extended Function Enable Register addresses.
1276b7b2d80SAdrian Chadd  * Same address as EFIR.
1286b7b2d80SAdrian Chadd  */
1296b7b2d80SAdrian Chadd uint8_t probe_addrs[] = {0x2e, 0x4e};
1306b7b2d80SAdrian Chadd 
1316b7b2d80SAdrian Chadd struct nuvoton_vendor_device_id {
1326b7b2d80SAdrian Chadd 	uint16_t		chip_id;
1336b7b2d80SAdrian Chadd 	const char *		descr;
1346b7b2d80SAdrian Chadd } nct_devs[] = {
1356b7b2d80SAdrian Chadd 	{
1366b7b2d80SAdrian Chadd 		.chip_id	= 0x1061,
1376b7b2d80SAdrian Chadd 		.descr		= "Nuvoton NCT5104D",
1386b7b2d80SAdrian Chadd 	},
1396b7b2d80SAdrian Chadd 	{
1406b7b2d80SAdrian Chadd 		.chip_id	= 0xc452,
1416b7b2d80SAdrian Chadd 		.descr		= "Nuvoton NCT5104D (PC-Engines APU)",
1426b7b2d80SAdrian Chadd 	},
1436b7b2d80SAdrian Chadd };
1446b7b2d80SAdrian Chadd 
1456b7b2d80SAdrian Chadd static void
1466b7b2d80SAdrian Chadd write_cfg_reg_1(struct nct_softc *sc, uint8_t reg, uint8_t value)
1476b7b2d80SAdrian Chadd {
1486b7b2d80SAdrian Chadd 	GPIO_ASSERT_LOCKED(sc);
1496b7b2d80SAdrian Chadd 	bus_write_1(sc->portres, 0, reg);
1506b7b2d80SAdrian Chadd 	NCT_BARRIER_WRITE(sc);
1516b7b2d80SAdrian Chadd 	bus_write_1(sc->portres, 1, value);
1526b7b2d80SAdrian Chadd 	NCT_BARRIER_WRITE(sc);
1536b7b2d80SAdrian Chadd }
1546b7b2d80SAdrian Chadd 
1556b7b2d80SAdrian Chadd static uint8_t
1566b7b2d80SAdrian Chadd read_cfg_reg_1(struct nct_softc *sc, uint8_t reg)
1576b7b2d80SAdrian Chadd {
1586b7b2d80SAdrian Chadd 	uint8_t value;
1596b7b2d80SAdrian Chadd 
1606b7b2d80SAdrian Chadd 	GPIO_ASSERT_LOCKED(sc);
1616b7b2d80SAdrian Chadd 	bus_write_1(sc->portres, 0, reg);
1626b7b2d80SAdrian Chadd 	NCT_BARRIER_READ_WRITE(sc);
1636b7b2d80SAdrian Chadd 	value = bus_read_1(sc->portres, 1);
1646b7b2d80SAdrian Chadd 	NCT_BARRIER_READ_WRITE(sc);
1656b7b2d80SAdrian Chadd 
1666b7b2d80SAdrian Chadd 	return (value);
1676b7b2d80SAdrian Chadd }
1686b7b2d80SAdrian Chadd 
1696b7b2d80SAdrian Chadd static uint16_t
1706b7b2d80SAdrian Chadd read_cfg_reg_2(struct nct_softc *sc, uint8_t reg)
1716b7b2d80SAdrian Chadd {
1726b7b2d80SAdrian Chadd 	uint16_t value;
1736b7b2d80SAdrian Chadd 
1746b7b2d80SAdrian Chadd 	value = read_cfg_reg_1(sc, reg) << 8;
1756b7b2d80SAdrian Chadd 	value |= read_cfg_reg_1(sc, reg + 1);
1766b7b2d80SAdrian Chadd 
1776b7b2d80SAdrian Chadd 	return (value);
1786b7b2d80SAdrian Chadd }
1796b7b2d80SAdrian Chadd 
1806b7b2d80SAdrian Chadd /*
1816b7b2d80SAdrian Chadd  * Enable extended function mode.
1826b7b2d80SAdrian Chadd  *
1836b7b2d80SAdrian Chadd  */
1846b7b2d80SAdrian Chadd static void
1856b7b2d80SAdrian Chadd ext_cfg_enter(struct nct_softc *sc)
1866b7b2d80SAdrian Chadd {
1876b7b2d80SAdrian Chadd 	GPIO_ASSERT_LOCKED(sc);
1886b7b2d80SAdrian Chadd 	bus_write_1(sc->portres, 0, NCT_EXTFUNC_ENTER);
1896b7b2d80SAdrian Chadd 	NCT_BARRIER_WRITE(sc);
1906b7b2d80SAdrian Chadd 	bus_write_1(sc->portres, 0, NCT_EXTFUNC_ENTER);
1916b7b2d80SAdrian Chadd 	NCT_BARRIER_WRITE(sc);
1926b7b2d80SAdrian Chadd }
1936b7b2d80SAdrian Chadd 
1946b7b2d80SAdrian Chadd /*
1956b7b2d80SAdrian Chadd  * Disable extended function mode.
1966b7b2d80SAdrian Chadd  *
1976b7b2d80SAdrian Chadd  */
1986b7b2d80SAdrian Chadd static void
1996b7b2d80SAdrian Chadd ext_cfg_exit(struct nct_softc *sc)
2006b7b2d80SAdrian Chadd {
2016b7b2d80SAdrian Chadd 	GPIO_ASSERT_LOCKED(sc);
2026b7b2d80SAdrian Chadd 	bus_write_1(sc->portres, 0, NCT_EXTFUNC_EXIT);
2036b7b2d80SAdrian Chadd 	NCT_BARRIER_WRITE(sc);
2046b7b2d80SAdrian Chadd }
2056b7b2d80SAdrian Chadd 
2066b7b2d80SAdrian Chadd /*
2076b7b2d80SAdrian Chadd  * Select a Logical Device.
2086b7b2d80SAdrian Chadd  */
2096b7b2d80SAdrian Chadd static void
2106b7b2d80SAdrian Chadd select_ldn(struct nct_softc *sc, uint8_t ldn)
2116b7b2d80SAdrian Chadd {
2126b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, NCT_CR_LDN, ldn);
2136b7b2d80SAdrian Chadd }
2146b7b2d80SAdrian Chadd 
2156b7b2d80SAdrian Chadd /*
2166b7b2d80SAdrian Chadd  * Get the GPIO Input/Output register address
2176b7b2d80SAdrian Chadd  * for a pin.
2186b7b2d80SAdrian Chadd  */
2196b7b2d80SAdrian Chadd static uint8_t
2206b7b2d80SAdrian Chadd nct_ior_addr(uint32_t pin_num)
2216b7b2d80SAdrian Chadd {
2226b7b2d80SAdrian Chadd 	uint8_t addr;
2236b7b2d80SAdrian Chadd 
2246b7b2d80SAdrian Chadd 	addr = NCT_LD7_GPIO0_IOR;
2256b7b2d80SAdrian Chadd 	if (pin_num > 7)
2266b7b2d80SAdrian Chadd 		addr = NCT_LD7_GPIO1_IOR;
2276b7b2d80SAdrian Chadd 
2286b7b2d80SAdrian Chadd 	return (addr);
2296b7b2d80SAdrian Chadd }
2306b7b2d80SAdrian Chadd 
2316b7b2d80SAdrian Chadd /*
2326b7b2d80SAdrian Chadd  * Get the GPIO Data register address for a pin.
2336b7b2d80SAdrian Chadd  */
2346b7b2d80SAdrian Chadd static uint8_t
2356b7b2d80SAdrian Chadd nct_dat_addr(uint32_t pin_num)
2366b7b2d80SAdrian Chadd {
2376b7b2d80SAdrian Chadd 	uint8_t addr;
2386b7b2d80SAdrian Chadd 
2396b7b2d80SAdrian Chadd 	addr = NCT_LD7_GPIO0_DAT;
2406b7b2d80SAdrian Chadd 	if (pin_num > 7)
2416b7b2d80SAdrian Chadd 		addr = NCT_LD7_GPIO1_DAT;
2426b7b2d80SAdrian Chadd 
2436b7b2d80SAdrian Chadd 	return (addr);
2446b7b2d80SAdrian Chadd }
2456b7b2d80SAdrian Chadd 
2466b7b2d80SAdrian Chadd /*
2476b7b2d80SAdrian Chadd  * Get the GPIO Inversion register address
2486b7b2d80SAdrian Chadd  * for a pin.
2496b7b2d80SAdrian Chadd  */
2506b7b2d80SAdrian Chadd static uint8_t
2516b7b2d80SAdrian Chadd nct_inv_addr(uint32_t pin_num)
2526b7b2d80SAdrian Chadd {
2536b7b2d80SAdrian Chadd 	uint8_t addr;
2546b7b2d80SAdrian Chadd 
2556b7b2d80SAdrian Chadd 	addr = NCT_LD7_GPIO0_INV;
2566b7b2d80SAdrian Chadd 	if (pin_num > 7)
2576b7b2d80SAdrian Chadd 		addr = NCT_LD7_GPIO1_INV;
2586b7b2d80SAdrian Chadd 
2596b7b2d80SAdrian Chadd 	return (addr);
2606b7b2d80SAdrian Chadd }
2616b7b2d80SAdrian Chadd 
2626b7b2d80SAdrian Chadd /*
2636b7b2d80SAdrian Chadd  * Get the GPIO Output Configuration/Mode
2646b7b2d80SAdrian Chadd  * register address for a pin.
2656b7b2d80SAdrian Chadd  */
2666b7b2d80SAdrian Chadd static uint8_t
2676b7b2d80SAdrian Chadd nct_outcfg_addr(uint32_t pin_num)
2686b7b2d80SAdrian Chadd {
2696b7b2d80SAdrian Chadd 	uint8_t addr;
2706b7b2d80SAdrian Chadd 
2716b7b2d80SAdrian Chadd 	addr = NCT_LDF_GPIO0_OUTCFG;
2726b7b2d80SAdrian Chadd 	if (pin_num > 7)
2736b7b2d80SAdrian Chadd 		addr = NCT_LDF_GPIO1_OUTCFG;
2746b7b2d80SAdrian Chadd 
2756b7b2d80SAdrian Chadd 	return (addr);
2766b7b2d80SAdrian Chadd }
2776b7b2d80SAdrian Chadd 
2786b7b2d80SAdrian Chadd /*
2796b7b2d80SAdrian Chadd  * Set a pin to output mode.
2806b7b2d80SAdrian Chadd  */
2816b7b2d80SAdrian Chadd static void
2826b7b2d80SAdrian Chadd nct_set_pin_is_output(struct nct_softc *sc, uint32_t pin_num)
2836b7b2d80SAdrian Chadd {
2846b7b2d80SAdrian Chadd 	uint8_t reg;
2856b7b2d80SAdrian Chadd 	uint8_t ior;
2866b7b2d80SAdrian Chadd 
2876b7b2d80SAdrian Chadd 	reg = nct_ior_addr(pin_num);
2886b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
2896b7b2d80SAdrian Chadd 	ior = read_cfg_reg_1(sc, reg);
2906b7b2d80SAdrian Chadd 	ior &= ~(NCT_PIN_BIT(pin_num));
2916b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, reg, ior);
2926b7b2d80SAdrian Chadd }
2936b7b2d80SAdrian Chadd 
2946b7b2d80SAdrian Chadd /*
2956b7b2d80SAdrian Chadd  * Set a pin to input mode.
2966b7b2d80SAdrian Chadd  */
2976b7b2d80SAdrian Chadd static void
2986b7b2d80SAdrian Chadd nct_set_pin_is_input(struct nct_softc *sc, uint32_t pin_num)
2996b7b2d80SAdrian Chadd {
3006b7b2d80SAdrian Chadd 	uint8_t reg;
3016b7b2d80SAdrian Chadd 	uint8_t ior;
3026b7b2d80SAdrian Chadd 
3036b7b2d80SAdrian Chadd 	reg = nct_ior_addr(pin_num);
3046b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
3056b7b2d80SAdrian Chadd 	ior = read_cfg_reg_1(sc, reg);
3066b7b2d80SAdrian Chadd 	ior |= NCT_PIN_BIT(pin_num);
3076b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, reg, ior);
3086b7b2d80SAdrian Chadd }
3096b7b2d80SAdrian Chadd 
3106b7b2d80SAdrian Chadd /*
3116b7b2d80SAdrian Chadd  * Check whether a pin is configured as an input.
3126b7b2d80SAdrian Chadd  */
3136b7b2d80SAdrian Chadd static bool
3146b7b2d80SAdrian Chadd nct_pin_is_input(struct nct_softc *sc, uint32_t pin_num)
3156b7b2d80SAdrian Chadd {
3166b7b2d80SAdrian Chadd 	uint8_t reg;
3176b7b2d80SAdrian Chadd 	uint8_t ior;
3186b7b2d80SAdrian Chadd 
3196b7b2d80SAdrian Chadd 	reg = nct_ior_addr(pin_num);
3206b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
3216b7b2d80SAdrian Chadd 	ior = read_cfg_reg_1(sc, reg);
3226b7b2d80SAdrian Chadd 
3236b7b2d80SAdrian Chadd 	return (ior & NCT_PIN_BIT(pin_num));
3246b7b2d80SAdrian Chadd }
3256b7b2d80SAdrian Chadd 
3266b7b2d80SAdrian Chadd /*
3276b7b2d80SAdrian Chadd  * Write a value to an output pin.
3286b7b2d80SAdrian Chadd  */
3296b7b2d80SAdrian Chadd static void
3306b7b2d80SAdrian Chadd nct_write_pin(struct nct_softc *sc, uint32_t pin_num, uint8_t data)
3316b7b2d80SAdrian Chadd {
3326b7b2d80SAdrian Chadd 	uint8_t reg;
3336b7b2d80SAdrian Chadd 	uint8_t value;
3346b7b2d80SAdrian Chadd 
3356b7b2d80SAdrian Chadd 	reg = nct_dat_addr(pin_num);
3366b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
3376b7b2d80SAdrian Chadd 	value = read_cfg_reg_1(sc, reg);
3386b7b2d80SAdrian Chadd 	if (data)
3396b7b2d80SAdrian Chadd 		value |= NCT_PIN_BIT(pin_num);
3406b7b2d80SAdrian Chadd 	else
3416b7b2d80SAdrian Chadd 		value &= ~(NCT_PIN_BIT(pin_num));
3426b7b2d80SAdrian Chadd 
3436b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, reg, value);
3446b7b2d80SAdrian Chadd }
3456b7b2d80SAdrian Chadd 
3466b7b2d80SAdrian Chadd static bool
3476b7b2d80SAdrian Chadd nct_read_pin(struct nct_softc *sc, uint32_t pin_num)
3486b7b2d80SAdrian Chadd {
3496b7b2d80SAdrian Chadd 	uint8_t reg;
3506b7b2d80SAdrian Chadd 
3516b7b2d80SAdrian Chadd 	reg = nct_dat_addr(pin_num);
3526b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
3536b7b2d80SAdrian Chadd 
3546b7b2d80SAdrian Chadd 	return (read_cfg_reg_1(sc, reg) & NCT_PIN_BIT(pin_num));
3556b7b2d80SAdrian Chadd }
3566b7b2d80SAdrian Chadd 
3576b7b2d80SAdrian Chadd static void
3586b7b2d80SAdrian Chadd nct_set_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num)
3596b7b2d80SAdrian Chadd {
3606b7b2d80SAdrian Chadd 	uint8_t reg;
3616b7b2d80SAdrian Chadd 	uint8_t inv;
3626b7b2d80SAdrian Chadd 
3636b7b2d80SAdrian Chadd 	reg = nct_inv_addr(pin_num);
3646b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
3656b7b2d80SAdrian Chadd 	inv = read_cfg_reg_1(sc, reg);
3666b7b2d80SAdrian Chadd 	inv |= (NCT_PIN_BIT(pin_num));
3676b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, reg, inv);
3686b7b2d80SAdrian Chadd }
3696b7b2d80SAdrian Chadd 
3706b7b2d80SAdrian Chadd static void
3716b7b2d80SAdrian Chadd nct_set_pin_not_inverted(struct nct_softc *sc, uint32_t pin_num)
3726b7b2d80SAdrian Chadd {
3736b7b2d80SAdrian Chadd 	uint8_t reg;
3746b7b2d80SAdrian Chadd 	uint8_t inv;
3756b7b2d80SAdrian Chadd 
3766b7b2d80SAdrian Chadd 	reg = nct_inv_addr(pin_num);
3776b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
3786b7b2d80SAdrian Chadd 	inv = read_cfg_reg_1(sc, reg);
3796b7b2d80SAdrian Chadd 	inv &= ~(NCT_PIN_BIT(pin_num));
3806b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, reg, inv);
3816b7b2d80SAdrian Chadd }
3826b7b2d80SAdrian Chadd 
3836b7b2d80SAdrian Chadd static bool
3846b7b2d80SAdrian Chadd nct_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num)
3856b7b2d80SAdrian Chadd {
3866b7b2d80SAdrian Chadd 	uint8_t reg;
3876b7b2d80SAdrian Chadd 	uint8_t inv;
3886b7b2d80SAdrian Chadd 
3896b7b2d80SAdrian Chadd 	reg = nct_inv_addr(pin_num);
3906b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
3916b7b2d80SAdrian Chadd 	inv = read_cfg_reg_1(sc, reg);
3926b7b2d80SAdrian Chadd 
3936b7b2d80SAdrian Chadd 	return (inv & NCT_PIN_BIT(pin_num));
3946b7b2d80SAdrian Chadd }
3956b7b2d80SAdrian Chadd 
3966b7b2d80SAdrian Chadd static void
3976b7b2d80SAdrian Chadd nct_set_pin_opendrain(struct nct_softc *sc, uint32_t pin_num)
3986b7b2d80SAdrian Chadd {
3996b7b2d80SAdrian Chadd 	uint8_t reg;
4006b7b2d80SAdrian Chadd 	uint8_t outcfg;
4016b7b2d80SAdrian Chadd 
4026b7b2d80SAdrian Chadd 	reg = nct_outcfg_addr(pin_num);
4036b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO_MODE);
4046b7b2d80SAdrian Chadd 	outcfg = read_cfg_reg_1(sc, reg);
4056b7b2d80SAdrian Chadd 	outcfg |= (NCT_PIN_BIT(pin_num));
4066b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, reg, outcfg);
4076b7b2d80SAdrian Chadd }
4086b7b2d80SAdrian Chadd 
4096b7b2d80SAdrian Chadd static void
4106b7b2d80SAdrian Chadd nct_set_pin_pushpull(struct nct_softc *sc, uint32_t pin_num)
4116b7b2d80SAdrian Chadd {
4126b7b2d80SAdrian Chadd 	uint8_t reg;
4136b7b2d80SAdrian Chadd 	uint8_t outcfg;
4146b7b2d80SAdrian Chadd 
4156b7b2d80SAdrian Chadd 	reg = nct_outcfg_addr(pin_num);
4166b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO_MODE);
4176b7b2d80SAdrian Chadd 	outcfg = read_cfg_reg_1(sc, reg);
4186b7b2d80SAdrian Chadd 	outcfg &= ~(NCT_PIN_BIT(pin_num));
4196b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, reg, outcfg);
4206b7b2d80SAdrian Chadd }
4216b7b2d80SAdrian Chadd 
4226b7b2d80SAdrian Chadd static bool
4236b7b2d80SAdrian Chadd nct_pin_is_opendrain(struct nct_softc *sc, uint32_t pin_num)
4246b7b2d80SAdrian Chadd {
4256b7b2d80SAdrian Chadd 	uint8_t reg;
4266b7b2d80SAdrian Chadd 	uint8_t outcfg;
4276b7b2d80SAdrian Chadd 
4286b7b2d80SAdrian Chadd 	reg = nct_outcfg_addr(pin_num);
4296b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO_MODE);
4306b7b2d80SAdrian Chadd 	outcfg = read_cfg_reg_1(sc, reg);
4316b7b2d80SAdrian Chadd 
4326b7b2d80SAdrian Chadd 	return (outcfg & NCT_PIN_BIT(pin_num));
4336b7b2d80SAdrian Chadd }
4346b7b2d80SAdrian Chadd 
4356b7b2d80SAdrian Chadd static void
4366b7b2d80SAdrian Chadd nct_identify(driver_t *driver, device_t parent)
4376b7b2d80SAdrian Chadd {
4386b7b2d80SAdrian Chadd 	if (device_find_child(parent, driver->name, 0) != NULL)
4396b7b2d80SAdrian Chadd 		return;
4406b7b2d80SAdrian Chadd 
4416b7b2d80SAdrian Chadd 	BUS_ADD_CHILD(parent, 0, driver->name, 0);
4426b7b2d80SAdrian Chadd }
4436b7b2d80SAdrian Chadd 
4446b7b2d80SAdrian Chadd static int
4456b7b2d80SAdrian Chadd nct_probe(device_t dev)
4466b7b2d80SAdrian Chadd {
4476b7b2d80SAdrian Chadd 	int i, j;
4486b7b2d80SAdrian Chadd 	int rc;
4496b7b2d80SAdrian Chadd 	struct nct_softc *sc;
4506b7b2d80SAdrian Chadd 	uint16_t chipid;
4516b7b2d80SAdrian Chadd 
4526b7b2d80SAdrian Chadd 	/* Make sure we do not claim some ISA PNP device. */
4536b7b2d80SAdrian Chadd 	if (isa_get_logicalid(dev) != 0)
4546b7b2d80SAdrian Chadd 		return (ENXIO);
4556b7b2d80SAdrian Chadd 
4566b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
4576b7b2d80SAdrian Chadd 
45873a1170aSPedro F. Giffuni 	for (i = 0; i < nitems(probe_addrs); i++) {
4596b7b2d80SAdrian Chadd 		sc->rid = 0;
4606b7b2d80SAdrian Chadd 		sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid,
4616b7b2d80SAdrian Chadd 			probe_addrs[i], probe_addrs[i] + 1, 2, RF_ACTIVE);
4626b7b2d80SAdrian Chadd 		if (sc->portres == NULL)
4636b7b2d80SAdrian Chadd 			continue;
4646b7b2d80SAdrian Chadd 
4656b7b2d80SAdrian Chadd 		GPIO_LOCK_INIT(sc);
4666b7b2d80SAdrian Chadd 
4676b7b2d80SAdrian Chadd 		GPIO_ASSERT_UNLOCKED(sc);
4686b7b2d80SAdrian Chadd 		GPIO_LOCK(sc);
4696b7b2d80SAdrian Chadd 		ext_cfg_enter(sc);
4706b7b2d80SAdrian Chadd 		chipid = read_cfg_reg_2(sc, NCT_CR_CHIP_ID);
4716b7b2d80SAdrian Chadd 		ext_cfg_exit(sc);
4726b7b2d80SAdrian Chadd 		GPIO_UNLOCK(sc);
4736b7b2d80SAdrian Chadd 
4746b7b2d80SAdrian Chadd 		GPIO_LOCK_DESTROY(sc);
4756b7b2d80SAdrian Chadd 
4766b7b2d80SAdrian Chadd 		bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
4776b7b2d80SAdrian Chadd 		bus_delete_resource(dev, SYS_RES_IOPORT, sc->rid);
4786b7b2d80SAdrian Chadd 
47973a1170aSPedro F. Giffuni 		for (j = 0; j < nitems(nct_devs); j++) {
4806b7b2d80SAdrian Chadd 			if (chipid == nct_devs[j].chip_id) {
4816b7b2d80SAdrian Chadd 				rc = bus_set_resource(dev, SYS_RES_IOPORT, 0, probe_addrs[i], 2);
4826b7b2d80SAdrian Chadd 				if (rc != 0) {
4836b7b2d80SAdrian Chadd 					device_printf(dev, "bus_set_resource failed for address 0x%02X\n", probe_addrs[i]);
4846b7b2d80SAdrian Chadd 					continue;
4856b7b2d80SAdrian Chadd 				}
4866b7b2d80SAdrian Chadd 				device_set_desc(dev, nct_devs[j].descr);
4876b7b2d80SAdrian Chadd 				return (BUS_PROBE_DEFAULT);
4886b7b2d80SAdrian Chadd 			}
4896b7b2d80SAdrian Chadd 		}
4906b7b2d80SAdrian Chadd 	}
4916b7b2d80SAdrian Chadd 	return (ENXIO);
4926b7b2d80SAdrian Chadd }
4936b7b2d80SAdrian Chadd 
4946b7b2d80SAdrian Chadd static int
4956b7b2d80SAdrian Chadd nct_attach(device_t dev)
4966b7b2d80SAdrian Chadd {
4976b7b2d80SAdrian Chadd 	struct nct_softc *sc;
4986b7b2d80SAdrian Chadd 	int i;
4996b7b2d80SAdrian Chadd 
5006b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
5016b7b2d80SAdrian Chadd 
5026b7b2d80SAdrian Chadd 	sc->rid = 0;
5036b7b2d80SAdrian Chadd 	sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid,
5046b7b2d80SAdrian Chadd 		0ul, ~0ul, 2, RF_ACTIVE);
5056b7b2d80SAdrian Chadd 	if (sc->portres == NULL) {
5066b7b2d80SAdrian Chadd 		device_printf(dev, "cannot allocate ioport\n");
5076b7b2d80SAdrian Chadd 		return (ENXIO);
5086b7b2d80SAdrian Chadd 	}
5096b7b2d80SAdrian Chadd 
5106b7b2d80SAdrian Chadd 	GPIO_LOCK_INIT(sc);
5116b7b2d80SAdrian Chadd 
5126b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
5136b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
5146b7b2d80SAdrian Chadd 	ext_cfg_enter(sc);
5156b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
5166b7b2d80SAdrian Chadd 	/* Enable gpio0 and gpio1. */
5176b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, NCT_LD7_GPIO_ENABLE,
5186b7b2d80SAdrian Chadd 		read_cfg_reg_1(sc, NCT_LD7_GPIO_ENABLE) | 0x03);
5196b7b2d80SAdrian Chadd 
5206b7b2d80SAdrian Chadd 	for (i = 0; i <= NCT_MAX_PIN; i++) {
5216b7b2d80SAdrian Chadd 		struct gpio_pin *pin;
5226b7b2d80SAdrian Chadd 
5236b7b2d80SAdrian Chadd 		pin = &sc->pins[i];
5246b7b2d80SAdrian Chadd 		pin->gp_pin = i;
5256b7b2d80SAdrian Chadd 		pin->gp_caps = NCT_GPIO_CAPS;
5266b7b2d80SAdrian Chadd 		pin->gp_flags = 0;
5276b7b2d80SAdrian Chadd 
5286b7b2d80SAdrian Chadd 		snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%02u", i);
5296b7b2d80SAdrian Chadd 		pin->gp_name[GPIOMAXNAME - 1] = '\0';
5306b7b2d80SAdrian Chadd 
5316b7b2d80SAdrian Chadd 		if (nct_pin_is_input(sc, i))
5326b7b2d80SAdrian Chadd 			pin->gp_flags |= GPIO_PIN_INPUT;
5336b7b2d80SAdrian Chadd 		else
5346b7b2d80SAdrian Chadd 			pin->gp_flags |= GPIO_PIN_OUTPUT;
5356b7b2d80SAdrian Chadd 
5366b7b2d80SAdrian Chadd 		if (nct_pin_is_opendrain(sc, i))
5376b7b2d80SAdrian Chadd 			pin->gp_flags |= GPIO_PIN_OPENDRAIN;
5386b7b2d80SAdrian Chadd 		else
5396b7b2d80SAdrian Chadd 			pin->gp_flags |= GPIO_PIN_PUSHPULL;
5406b7b2d80SAdrian Chadd 
5416b7b2d80SAdrian Chadd 		if (nct_pin_is_inverted(sc, i))
5426b7b2d80SAdrian Chadd 			pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
5436b7b2d80SAdrian Chadd 	}
5446b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
5456b7b2d80SAdrian Chadd 
5466b7b2d80SAdrian Chadd 	sc->busdev = gpiobus_attach_bus(dev);
5476b7b2d80SAdrian Chadd 	if (sc->busdev == NULL) {
5486b7b2d80SAdrian Chadd 		GPIO_ASSERT_UNLOCKED(sc);
5496b7b2d80SAdrian Chadd 		GPIO_LOCK(sc);
5506b7b2d80SAdrian Chadd 		ext_cfg_exit(sc);
5516b7b2d80SAdrian Chadd 		GPIO_UNLOCK(sc);
5526b7b2d80SAdrian Chadd 		bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
5536b7b2d80SAdrian Chadd 		GPIO_LOCK_DESTROY(sc);
5546b7b2d80SAdrian Chadd 
5556b7b2d80SAdrian Chadd 		return (ENXIO);
5566b7b2d80SAdrian Chadd 	}
5576b7b2d80SAdrian Chadd 
5586b7b2d80SAdrian Chadd 	return (0);
5596b7b2d80SAdrian Chadd }
5606b7b2d80SAdrian Chadd 
5616b7b2d80SAdrian Chadd static int
5626b7b2d80SAdrian Chadd nct_detach(device_t dev)
5636b7b2d80SAdrian Chadd {
5646b7b2d80SAdrian Chadd 	struct nct_softc *sc;
5656b7b2d80SAdrian Chadd 
5666b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
5676b7b2d80SAdrian Chadd 	gpiobus_detach_bus(dev);
5686b7b2d80SAdrian Chadd 
5696b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
5706b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
5716b7b2d80SAdrian Chadd 	ext_cfg_exit(sc);
5726b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
5736b7b2d80SAdrian Chadd 
5746b7b2d80SAdrian Chadd 	/* Cleanup resources. */
5756b7b2d80SAdrian Chadd 	bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
5766b7b2d80SAdrian Chadd 
5776b7b2d80SAdrian Chadd 	GPIO_LOCK_DESTROY(sc);
5786b7b2d80SAdrian Chadd 
5796b7b2d80SAdrian Chadd 	return (0);
5806b7b2d80SAdrian Chadd }
5816b7b2d80SAdrian Chadd 
5826b7b2d80SAdrian Chadd static device_t
5836b7b2d80SAdrian Chadd nct_gpio_get_bus(device_t dev)
5846b7b2d80SAdrian Chadd {
5856b7b2d80SAdrian Chadd 	struct nct_softc *sc;
5866b7b2d80SAdrian Chadd 
5876b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
5886b7b2d80SAdrian Chadd 
5896b7b2d80SAdrian Chadd 	return (sc->busdev);
5906b7b2d80SAdrian Chadd }
5916b7b2d80SAdrian Chadd 
5926b7b2d80SAdrian Chadd static int
5936b7b2d80SAdrian Chadd nct_gpio_pin_max(device_t dev, int *npins)
5946b7b2d80SAdrian Chadd {
5956b7b2d80SAdrian Chadd 	*npins = NCT_MAX_PIN;
5966b7b2d80SAdrian Chadd 
5976b7b2d80SAdrian Chadd 	return (0);
5986b7b2d80SAdrian Chadd }
5996b7b2d80SAdrian Chadd 
6006b7b2d80SAdrian Chadd static int
6016b7b2d80SAdrian Chadd nct_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value)
6026b7b2d80SAdrian Chadd {
6036b7b2d80SAdrian Chadd 	struct nct_softc *sc;
6046b7b2d80SAdrian Chadd 
6056b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
6066b7b2d80SAdrian Chadd 		return (EINVAL);
6076b7b2d80SAdrian Chadd 
6086b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
6096b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
6106b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
6116b7b2d80SAdrian Chadd 	nct_write_pin(sc, pin_num, pin_value);
6126b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
6136b7b2d80SAdrian Chadd 
6146b7b2d80SAdrian Chadd 	return (0);
6156b7b2d80SAdrian Chadd }
6166b7b2d80SAdrian Chadd 
6176b7b2d80SAdrian Chadd static int
6186b7b2d80SAdrian Chadd nct_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value)
6196b7b2d80SAdrian Chadd {
6206b7b2d80SAdrian Chadd 	struct nct_softc *sc;
6216b7b2d80SAdrian Chadd 
6226b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
6236b7b2d80SAdrian Chadd 		return (EINVAL);
6246b7b2d80SAdrian Chadd 
6256b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
6266b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
6276b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
6286b7b2d80SAdrian Chadd 	*pin_value = nct_read_pin(sc, pin_num);
6296b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
6306b7b2d80SAdrian Chadd 
6316b7b2d80SAdrian Chadd 	return (0);
6326b7b2d80SAdrian Chadd }
6336b7b2d80SAdrian Chadd 
6346b7b2d80SAdrian Chadd static int
6356b7b2d80SAdrian Chadd nct_gpio_pin_toggle(device_t dev, uint32_t pin_num)
6366b7b2d80SAdrian Chadd {
6376b7b2d80SAdrian Chadd 	struct nct_softc *sc;
6386b7b2d80SAdrian Chadd 
6396b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
6406b7b2d80SAdrian Chadd 		return (EINVAL);
6416b7b2d80SAdrian Chadd 
6426b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
6436b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
6446b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
6456b7b2d80SAdrian Chadd 	if (nct_read_pin(sc, pin_num))
6466b7b2d80SAdrian Chadd 		nct_write_pin(sc, pin_num, 0);
6476b7b2d80SAdrian Chadd 	else
6486b7b2d80SAdrian Chadd 		nct_write_pin(sc, pin_num, 1);
6496b7b2d80SAdrian Chadd 
6506b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
6516b7b2d80SAdrian Chadd 
6526b7b2d80SAdrian Chadd 	return (0);
6536b7b2d80SAdrian Chadd }
6546b7b2d80SAdrian Chadd 
6556b7b2d80SAdrian Chadd static int
6566b7b2d80SAdrian Chadd nct_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps)
6576b7b2d80SAdrian Chadd {
6586b7b2d80SAdrian Chadd 	struct nct_softc *sc;
6596b7b2d80SAdrian Chadd 
6606b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
6616b7b2d80SAdrian Chadd 		return (EINVAL);
6626b7b2d80SAdrian Chadd 
6636b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
6646b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
6656b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
6666b7b2d80SAdrian Chadd 	*caps = sc->pins[pin_num].gp_caps;
6676b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
6686b7b2d80SAdrian Chadd 
6696b7b2d80SAdrian Chadd 	return (0);
6706b7b2d80SAdrian Chadd }
6716b7b2d80SAdrian Chadd 
6726b7b2d80SAdrian Chadd static int
6736b7b2d80SAdrian Chadd nct_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags)
6746b7b2d80SAdrian Chadd {
6756b7b2d80SAdrian Chadd 	struct nct_softc *sc;
6766b7b2d80SAdrian Chadd 
6776b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
6786b7b2d80SAdrian Chadd 		return (EINVAL);
6796b7b2d80SAdrian Chadd 
6806b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
6816b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
6826b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
6836b7b2d80SAdrian Chadd 	*flags = sc->pins[pin_num].gp_flags;
6846b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
6856b7b2d80SAdrian Chadd 
6866b7b2d80SAdrian Chadd 	return (0);
6876b7b2d80SAdrian Chadd }
6886b7b2d80SAdrian Chadd 
6896b7b2d80SAdrian Chadd static int
6906b7b2d80SAdrian Chadd nct_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name)
6916b7b2d80SAdrian Chadd {
6926b7b2d80SAdrian Chadd 	struct nct_softc *sc;
6936b7b2d80SAdrian Chadd 
6946b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
6956b7b2d80SAdrian Chadd 		return (EINVAL);
6966b7b2d80SAdrian Chadd 
6976b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
6986b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
6996b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
7006b7b2d80SAdrian Chadd 	memcpy(name, sc->pins[pin_num].gp_name, GPIOMAXNAME);
7016b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
7026b7b2d80SAdrian Chadd 
7036b7b2d80SAdrian Chadd 	return (0);
7046b7b2d80SAdrian Chadd }
7056b7b2d80SAdrian Chadd 
7066b7b2d80SAdrian Chadd static int
7076b7b2d80SAdrian Chadd nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags)
7086b7b2d80SAdrian Chadd {
7096b7b2d80SAdrian Chadd 	struct nct_softc *sc;
7106b7b2d80SAdrian Chadd 	struct gpio_pin *pin;
7116b7b2d80SAdrian Chadd 
7126b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
7136b7b2d80SAdrian Chadd 		return (EINVAL);
7146b7b2d80SAdrian Chadd 
7156b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
7166b7b2d80SAdrian Chadd 	pin = &sc->pins[pin_num];
7176b7b2d80SAdrian Chadd 	if ((flags & pin->gp_caps) != flags)
7186b7b2d80SAdrian Chadd 		return (EINVAL);
7196b7b2d80SAdrian Chadd 
7206b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
7216b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
7226b7b2d80SAdrian Chadd 	if (flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
7236b7b2d80SAdrian Chadd 		if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
7246b7b2d80SAdrian Chadd 			(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
7256b7b2d80SAdrian Chadd 				GPIO_UNLOCK(sc);
7266b7b2d80SAdrian Chadd 				return (EINVAL);
7276b7b2d80SAdrian Chadd 		}
7286b7b2d80SAdrian Chadd 
7296b7b2d80SAdrian Chadd 		if (flags & GPIO_PIN_INPUT)
7306b7b2d80SAdrian Chadd 			nct_set_pin_is_input(sc, pin_num);
7316b7b2d80SAdrian Chadd 		else
7326b7b2d80SAdrian Chadd 			nct_set_pin_is_output(sc, pin_num);
7336b7b2d80SAdrian Chadd 	}
7346b7b2d80SAdrian Chadd 
7356b7b2d80SAdrian Chadd 	if (flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) {
7366b7b2d80SAdrian Chadd 		if (flags & GPIO_PIN_INPUT) {
7376b7b2d80SAdrian Chadd 			GPIO_UNLOCK(sc);
7386b7b2d80SAdrian Chadd 			return (EINVAL);
7396b7b2d80SAdrian Chadd 		}
7406b7b2d80SAdrian Chadd 
7416b7b2d80SAdrian Chadd 		if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) ==
7426b7b2d80SAdrian Chadd 			(GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) {
7436b7b2d80SAdrian Chadd 				GPIO_UNLOCK(sc);
7446b7b2d80SAdrian Chadd 				return (EINVAL);
7456b7b2d80SAdrian Chadd 		}
7466b7b2d80SAdrian Chadd 
7476b7b2d80SAdrian Chadd 		if (flags & GPIO_PIN_OPENDRAIN)
7486b7b2d80SAdrian Chadd 			nct_set_pin_opendrain(sc, pin_num);
7496b7b2d80SAdrian Chadd 		else
7506b7b2d80SAdrian Chadd 			nct_set_pin_pushpull(sc, pin_num);
7516b7b2d80SAdrian Chadd 	}
7526b7b2d80SAdrian Chadd 
7536b7b2d80SAdrian Chadd 	if (flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) {
7546b7b2d80SAdrian Chadd 		if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) !=
7556b7b2d80SAdrian Chadd 			(GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) {
7566b7b2d80SAdrian Chadd 				GPIO_UNLOCK(sc);
7576b7b2d80SAdrian Chadd 				return (EINVAL);
7586b7b2d80SAdrian Chadd 		}
7596b7b2d80SAdrian Chadd 
7606b7b2d80SAdrian Chadd 		if (flags & GPIO_PIN_INVIN)
7616b7b2d80SAdrian Chadd 			nct_set_pin_is_inverted(sc, pin_num);
7626b7b2d80SAdrian Chadd 		else
7636b7b2d80SAdrian Chadd 			nct_set_pin_not_inverted(sc, pin_num);
7646b7b2d80SAdrian Chadd 	}
7656b7b2d80SAdrian Chadd 
7666b7b2d80SAdrian Chadd 	pin->gp_flags = flags;
7676b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
7686b7b2d80SAdrian Chadd 
7696b7b2d80SAdrian Chadd 	return (0);
7706b7b2d80SAdrian Chadd }
7716b7b2d80SAdrian Chadd 
7726b7b2d80SAdrian Chadd static device_method_t nct_methods[] = {
7736b7b2d80SAdrian Chadd 	/* Device interface */
7746b7b2d80SAdrian Chadd 	DEVMETHOD(device_identify,	nct_identify),
7756b7b2d80SAdrian Chadd 	DEVMETHOD(device_probe,		nct_probe),
7766b7b2d80SAdrian Chadd 	DEVMETHOD(device_attach,	nct_attach),
7776b7b2d80SAdrian Chadd 	DEVMETHOD(device_detach,	nct_detach),
7786b7b2d80SAdrian Chadd 
7796b7b2d80SAdrian Chadd 	/* GPIO */
7806b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_get_bus,			nct_gpio_get_bus),
7816b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_max,			nct_gpio_pin_max),
7826b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_get,			nct_gpio_pin_get),
7836b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_set,			nct_gpio_pin_set),
7846b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_toggle,		nct_gpio_pin_toggle),
7856b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getname,		nct_gpio_pin_getname),
7866b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getcaps,		nct_gpio_pin_getcaps),
7876b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getflags,	nct_gpio_pin_getflags),
7886b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_setflags,	nct_gpio_pin_setflags),
7896b7b2d80SAdrian Chadd 
7906b7b2d80SAdrian Chadd 	DEVMETHOD_END
7916b7b2d80SAdrian Chadd };
7926b7b2d80SAdrian Chadd 
7936b7b2d80SAdrian Chadd static driver_t nct_isa_driver = {
7946b7b2d80SAdrian Chadd 	"gpio",
7956b7b2d80SAdrian Chadd 	nct_methods,
7966b7b2d80SAdrian Chadd 	sizeof(struct nct_softc)
7976b7b2d80SAdrian Chadd };
7986b7b2d80SAdrian Chadd 
7996b7b2d80SAdrian Chadd static devclass_t nct_devclass;
8006b7b2d80SAdrian Chadd 
8016b7b2d80SAdrian Chadd DRIVER_MODULE(nctgpio, isa, nct_isa_driver, nct_devclass, NULL, NULL);
8026b7b2d80SAdrian Chadd MODULE_DEPEND(nctgpio, gpiobus, 1, 1, 1);
803