xref: /freebsd/sys/dev/nctgpio/nctgpio.c (revision 95ee2897e98f5d444f26ed2334cc7c439f9c16c6)
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  */
276b7b2d80SAdrian Chadd 
286b7b2d80SAdrian Chadd /*
296b7b2d80SAdrian Chadd  * Nuvoton GPIO driver.
306b7b2d80SAdrian Chadd  */
316b7b2d80SAdrian Chadd 
326b7b2d80SAdrian Chadd #include <sys/cdefs.h>
336b7b2d80SAdrian Chadd 
346b7b2d80SAdrian Chadd #include <sys/param.h>
356b7b2d80SAdrian Chadd #include <sys/kernel.h>
366b7b2d80SAdrian Chadd #include <sys/systm.h>
376b7b2d80SAdrian Chadd #include <sys/bus.h>
386b7b2d80SAdrian Chadd #include <sys/eventhandler.h>
396b7b2d80SAdrian Chadd #include <sys/lock.h>
406b7b2d80SAdrian Chadd 
416b7b2d80SAdrian Chadd #include <sys/module.h>
426b7b2d80SAdrian Chadd #include <sys/gpio.h>
436b7b2d80SAdrian Chadd 
446b7b2d80SAdrian Chadd #include <machine/bus.h>
456b7b2d80SAdrian Chadd 
466b7b2d80SAdrian Chadd #include <dev/gpio/gpiobusvar.h>
47e3df342aSAndriy Gapon #include <dev/superio/superio.h>
486b7b2d80SAdrian Chadd 
496b7b2d80SAdrian Chadd #include "gpio_if.h"
506b7b2d80SAdrian Chadd 
517f8d2ed0SStéphane Rochoy #define NCT_PPOD_LDN 0xf /* LDN used to select Push-Pull/Open-Drain */
526b7b2d80SAdrian Chadd 
537f8d2ed0SStéphane Rochoy /* Direct access through GPIO register table */
547f8d2ed0SStéphane Rochoy #define	NCT_IO_GSR			0 /* Group Select */
557f8d2ed0SStéphane Rochoy #define	NCT_IO_IOR			1 /* I/O */
567f8d2ed0SStéphane Rochoy #define	NCT_IO_DAT			2 /* Data */
577f8d2ed0SStéphane Rochoy #define	NCT_IO_INV			3 /* Inversion */
587f8d2ed0SStéphane Rochoy #define	NCT_IO_DST          4 /* Status */
596b7b2d80SAdrian Chadd 
607f8d2ed0SStéphane Rochoy #define NCT_MAX_GROUP   9
617f8d2ed0SStéphane Rochoy #define NCT_MAX_PIN     75
626b7b2d80SAdrian Chadd 
637f8d2ed0SStéphane Rochoy #define NCT_PIN_IS_VALID(_sc, _p)   ((_p) < (_sc)->npins)
647f8d2ed0SStéphane Rochoy #define NCT_PIN_GROUP(_sc, _p)      ((_sc)->pinmap[(_p)].group)
657f8d2ed0SStéphane Rochoy #define NCT_PIN_GRPNUM(_sc, _p)     ((_sc)->pinmap[(_p)].grpnum)
667f8d2ed0SStéphane Rochoy #define NCT_PIN_BIT(_sc, _p)        ((_sc)->pinmap[(_p)].bit)
677f8d2ed0SStéphane Rochoy #define NCT_PIN_BITMASK(_p)         (1 << ((_p) & 7))
686b7b2d80SAdrian Chadd 
696b7b2d80SAdrian Chadd #define NCT_GPIO_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
706b7b2d80SAdrian Chadd 	GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \
716b7b2d80SAdrian Chadd 	GPIO_PIN_INVIN | GPIO_PIN_INVOUT)
726b7b2d80SAdrian Chadd 
737f8d2ed0SStéphane Rochoy #define NCT_PREFER_INDIRECT_CHANNEL 2
747f8d2ed0SStéphane Rochoy 
757f8d2ed0SStéphane Rochoy #define NCT_VERBOSE_PRINTF(dev, ...)            \
767f8d2ed0SStéphane Rochoy 	do {                                        \
777f8d2ed0SStéphane Rochoy 		if (__predict_false(bootverbose))       \
787f8d2ed0SStéphane Rochoy 			device_printf(dev, __VA_ARGS__);    \
797f8d2ed0SStéphane Rochoy 	} while (0)
807f8d2ed0SStéphane Rochoy 
81155514eaSAndriy Gapon /*
82155514eaSAndriy Gapon  * Note that the values are important.
83155514eaSAndriy Gapon  * They match actual register offsets.
84155514eaSAndriy Gapon  */
85155514eaSAndriy Gapon typedef enum {
86155514eaSAndriy Gapon 	REG_IOR = 0,
87155514eaSAndriy Gapon 	REG_DAT = 1,
88155514eaSAndriy Gapon 	REG_INV = 2,
89155514eaSAndriy Gapon } reg_t;
90155514eaSAndriy Gapon 
917f8d2ed0SStéphane Rochoy struct nct_gpio_group {
927f8d2ed0SStéphane Rochoy  	uint32_t    caps;
937f8d2ed0SStéphane Rochoy 	uint8_t     enable_ldn;
947f8d2ed0SStéphane Rochoy 	uint8_t     enable_reg;
957f8d2ed0SStéphane Rochoy 	uint8_t     enable_mask;
967f8d2ed0SStéphane Rochoy 	uint8_t     data_ldn;
977f8d2ed0SStéphane Rochoy 	uint8_t     iobase;
987f8d2ed0SStéphane Rochoy 	uint8_t     ppod_reg; /* Push-Pull/Open-Drain */
997f8d2ed0SStéphane Rochoy 	uint8_t     grpnum;
1007f8d2ed0SStéphane Rochoy 	uint8_t     pinbits[8];
1017f8d2ed0SStéphane Rochoy 	uint8_t     npins;
1027f8d2ed0SStéphane Rochoy };
1037f8d2ed0SStéphane Rochoy 
1046b7b2d80SAdrian Chadd struct nct_softc {
1056b7b2d80SAdrian Chadd 	device_t			dev;
1066b7b2d80SAdrian Chadd 	device_t			busdev;
1076b7b2d80SAdrian Chadd 	struct mtx			mtx;
108155514eaSAndriy Gapon 	struct resource			*iores;
109155514eaSAndriy Gapon 	int				iorid;
110155514eaSAndriy Gapon 	int				curgrp;
111155514eaSAndriy Gapon 	struct {
1127f8d2ed0SStéphane Rochoy 		uint8_t ior[NCT_MAX_GROUP + 1];       /* direction, 1: input 0: output */
1137f8d2ed0SStéphane Rochoy 		uint8_t out[NCT_MAX_GROUP + 1];       /* output value */
1147f8d2ed0SStéphane Rochoy 		uint8_t out_known[NCT_MAX_GROUP + 1]; /* whether out is valid */
1157f8d2ed0SStéphane Rochoy 		uint8_t inv[NCT_MAX_GROUP + 1];       /* inversion, 1: inverted */
116155514eaSAndriy Gapon 	} cache;
117523af57eSConrad Meyer 	struct gpio_pin				pins[NCT_MAX_PIN + 1];
1187f8d2ed0SStéphane Rochoy 	struct nct_device			*nctdevp;
1197f8d2ed0SStéphane Rochoy 	int							npins; /* Total number of pins */
1207f8d2ed0SStéphane Rochoy 
1217f8d2ed0SStéphane Rochoy 	/* Lookup tables */
1227f8d2ed0SStéphane Rochoy 	struct {
1237f8d2ed0SStéphane Rochoy 		struct nct_gpio_group *group;
1247f8d2ed0SStéphane Rochoy 		uint8_t                grpnum;
1257f8d2ed0SStéphane Rochoy 		uint8_t                bit;
1267f8d2ed0SStéphane Rochoy 	} pinmap[NCT_MAX_PIN+1];
1277f8d2ed0SStéphane Rochoy 	struct nct_gpio_group *grpmap[NCT_MAX_GROUP+1];
1286b7b2d80SAdrian Chadd };
1296b7b2d80SAdrian Chadd 
1306b7b2d80SAdrian Chadd #define GPIO_LOCK_INIT(_sc)	mtx_init(&(_sc)->mtx,		\
1316b7b2d80SAdrian Chadd 		device_get_nameunit(dev), NULL, MTX_DEF)
1326b7b2d80SAdrian Chadd #define GPIO_LOCK_DESTROY(_sc)		mtx_destroy(&(_sc)->mtx)
1336b7b2d80SAdrian Chadd #define GPIO_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
1346b7b2d80SAdrian Chadd #define GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->mtx)
1356b7b2d80SAdrian Chadd #define GPIO_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_OWNED)
1366b7b2d80SAdrian Chadd #define GPIO_ASSERT_UNLOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_NOTOWNED)
1376b7b2d80SAdrian Chadd 
1387f8d2ed0SStéphane Rochoy #define GET_BIT(v, b)	(((v) >> (b)) & 1)
1397f8d2ed0SStéphane Rochoy 
1407f8d2ed0SStéphane Rochoy /*
1417f8d2ed0SStéphane Rochoy  * For most devices there are several GPIO devices, we attach only to one of
1427f8d2ed0SStéphane Rochoy  * them and use the rest without attaching.
1437f8d2ed0SStéphane Rochoy  */
1447f8d2ed0SStéphane Rochoy struct nct_device {
1457f8d2ed0SStéphane Rochoy 	uint16_t                  devid;
1467f8d2ed0SStéphane Rochoy 	int                       extid;
1476b7b2d80SAdrian Chadd 	const char               *descr;
1487f8d2ed0SStéphane Rochoy 	int                       ngroups;
1497f8d2ed0SStéphane Rochoy 	struct nct_gpio_group     groups[NCT_MAX_GROUP + 1];
1507f8d2ed0SStéphane Rochoy } nct_devices[] = {
1516b7b2d80SAdrian Chadd 	{
1528e6ea10cSStéphane Rochoy 		.devid   = 0xa025,
1538e6ea10cSStéphane Rochoy 		.descr   = "GPIO on Winbond 83627DHG IC ver. 5",
1548e6ea10cSStéphane Rochoy 		.ngroups = 5,
1558e6ea10cSStéphane Rochoy 		.groups  = {
1568e6ea10cSStéphane Rochoy 			{
1578e6ea10cSStéphane Rochoy 				.grpnum      = 2,
1588e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
1598e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
1608e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
1618e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
1628e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
1638e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe0, /* FIXME Need to check for this group. */
1648e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
1658e6ea10cSStéphane Rochoy 				.npins       = 8,
1668e6ea10cSStéphane Rochoy 				.iobase      = 0xe3,
1678e6ea10cSStéphane Rochoy 			},
1688e6ea10cSStéphane Rochoy 			{
1698e6ea10cSStéphane Rochoy 				.grpnum      = 3,
1708e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
1718e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
1728e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
1738e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
1748e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
1758e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
1768e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
1778e6ea10cSStéphane Rochoy 				.npins       = 8,
1788e6ea10cSStéphane Rochoy 				.iobase      = 0xf0,
1798e6ea10cSStéphane Rochoy 			},
1808e6ea10cSStéphane Rochoy 			{
1818e6ea10cSStéphane Rochoy 				.grpnum      = 4,
1828e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
1838e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
1848e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
1858e6ea10cSStéphane Rochoy 				.enable_mask = 0x04,
1868e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
1878e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
1888e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
1898e6ea10cSStéphane Rochoy 				.npins       = 8,
1908e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
1918e6ea10cSStéphane Rochoy 			},
1928e6ea10cSStéphane Rochoy 			{
1938e6ea10cSStéphane Rochoy 				.grpnum      = 5,
1948e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
1958e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
1968e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
1978e6ea10cSStéphane Rochoy 				.enable_mask = 0x08,
1988e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
1998e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
2008e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
2018e6ea10cSStéphane Rochoy 				.npins       = 8,
2028e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
2038e6ea10cSStéphane Rochoy 			},
2048e6ea10cSStéphane Rochoy 			{
2058e6ea10cSStéphane Rochoy 				.grpnum      = 6,
2068e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
2078e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
2088e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
2098e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
2108e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
2118e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
2128e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
2138e6ea10cSStéphane Rochoy 				.npins       = 8,
2148e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
2158e6ea10cSStéphane Rochoy 			},
2168e6ea10cSStéphane Rochoy 		},
2178e6ea10cSStéphane Rochoy 	},
2188e6ea10cSStéphane Rochoy 	{
2197f8d2ed0SStéphane Rochoy 		.devid   = 0x1061,
2207f8d2ed0SStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT5104D",
2217f8d2ed0SStéphane Rochoy 		.ngroups = 2,
2227f8d2ed0SStéphane Rochoy 		.groups  = {
2237f8d2ed0SStéphane Rochoy 			{
2247f8d2ed0SStéphane Rochoy 				.grpnum      = 0,
2257f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
2267f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
2277f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
2287f8d2ed0SStéphane Rochoy 				.enable_mask = 0x01,
2297f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
2307f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe0,
2317f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
2327f8d2ed0SStéphane Rochoy 				.npins       = 8,
2337f8d2ed0SStéphane Rochoy 				.iobase      = 0xe0,
2346b7b2d80SAdrian Chadd 			},
2356b7b2d80SAdrian Chadd 			{
2367f8d2ed0SStéphane Rochoy 				.grpnum      = 1,
2377f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
2387f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
2397f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
2407f8d2ed0SStéphane Rochoy 				.enable_mask = 0x02,
2417f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
2427f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe1,
2437f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
2447f8d2ed0SStéphane Rochoy 				.npins       = 8,
2457f8d2ed0SStéphane Rochoy 				.iobase      = 0xe4,
2467f8d2ed0SStéphane Rochoy 			},
2477f8d2ed0SStéphane Rochoy 		},
2486b7b2d80SAdrian Chadd 	},
2499d1208bbSOleksandr Tymoshenko 	{
2508e6ea10cSStéphane Rochoy 		.devid   = 0xc452, /* FIXME Conflict with Nuvoton NCT6106D. See NetBSD's nct_match. */
2517f8d2ed0SStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT5104D (PC-Engines APU)",
2527f8d2ed0SStéphane Rochoy 		.ngroups = 2,
2537f8d2ed0SStéphane Rochoy 		.groups  = {
2547f8d2ed0SStéphane Rochoy 			{
2557f8d2ed0SStéphane Rochoy 				.grpnum      = 0,
2567f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
2577f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
2587f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
2597f8d2ed0SStéphane Rochoy 				.enable_mask = 0x01,
2607f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
2617f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe0,
2627f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
2637f8d2ed0SStéphane Rochoy 				.npins       = 8,
2647f8d2ed0SStéphane Rochoy 				.iobase      = 0xe0,
2657f8d2ed0SStéphane Rochoy 			},
2667f8d2ed0SStéphane Rochoy 			{
2677f8d2ed0SStéphane Rochoy 				.grpnum      = 1,
2687f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
2697f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
2707f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
2717f8d2ed0SStéphane Rochoy 				.enable_mask = 0x02,
2727f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
2737f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe1,
2747f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
2757f8d2ed0SStéphane Rochoy 				.npins       = 8,
2767f8d2ed0SStéphane Rochoy 				.iobase      = 0xe4,
2777f8d2ed0SStéphane Rochoy 			},
2787f8d2ed0SStéphane Rochoy 		},
2797f8d2ed0SStéphane Rochoy 	},
2807f8d2ed0SStéphane Rochoy 	{
2817f8d2ed0SStéphane Rochoy 		.devid   = 0xc453,
2827f8d2ed0SStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT5104D (PC-Engines APU3)",
2837f8d2ed0SStéphane Rochoy 		.ngroups = 2,
2847f8d2ed0SStéphane Rochoy 		.groups  = {
2857f8d2ed0SStéphane Rochoy 			{
2867f8d2ed0SStéphane Rochoy 				.grpnum      = 0,
2877f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
2887f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
2897f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
2907f8d2ed0SStéphane Rochoy 				.enable_mask = 0x01,
2917f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
2927f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe0,
2937f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
2947f8d2ed0SStéphane Rochoy 				.npins       = 8,
2957f8d2ed0SStéphane Rochoy 				.iobase      = 0xe0,
2967f8d2ed0SStéphane Rochoy 			},
2977f8d2ed0SStéphane Rochoy 			{
2987f8d2ed0SStéphane Rochoy 				.grpnum      = 1,
2997f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
3007f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
3017f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
3027f8d2ed0SStéphane Rochoy 				.enable_mask = 0x02,
3037f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
3047f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe1,
3057f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
3067f8d2ed0SStéphane Rochoy 				.npins       = 8,
3077f8d2ed0SStéphane Rochoy 				.iobase      = 0xe4,
3087f8d2ed0SStéphane Rochoy 			},
3097f8d2ed0SStéphane Rochoy 		},
3109d1208bbSOleksandr Tymoshenko 	},
3118e6ea10cSStéphane Rochoy 	{
3128e6ea10cSStéphane Rochoy 		.devid  = 0xd42a,
3138e6ea10cSStéphane Rochoy 		.extid  = 1,
3148e6ea10cSStéphane Rochoy 		.descr  = "GPIO on Nuvoton NCT6796D-E",
3158e6ea10cSStéphane Rochoy 		.ngroups = 10,
3168e6ea10cSStéphane Rochoy 		.groups  = {
3178e6ea10cSStéphane Rochoy 			{
3188e6ea10cSStéphane Rochoy 				.grpnum      = 0,
3198e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
3208e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x08,
3218e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
3228e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
3238e6ea10cSStéphane Rochoy 				.data_ldn    = 0x08,
3248e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe0, /* FIXME Need to check for this group. */
3258e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
3268e6ea10cSStéphane Rochoy 				.npins       = 8,
3278e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
3288e6ea10cSStéphane Rochoy 			},
3298e6ea10cSStéphane Rochoy 			{
3308e6ea10cSStéphane Rochoy 				.grpnum      = 1,
3318e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
3328e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x08,
3338e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
3348e6ea10cSStéphane Rochoy 				.enable_mask = 0x80,
3358e6ea10cSStéphane Rochoy 				.data_ldn    = 0x08,
3368e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
3378e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
3388e6ea10cSStéphane Rochoy 				.npins       = 8,
3398e6ea10cSStéphane Rochoy 				.iobase      = 0xf0,
3408e6ea10cSStéphane Rochoy 			},
3418e6ea10cSStéphane Rochoy 			{
3428e6ea10cSStéphane Rochoy 				.grpnum      = 2,
3438e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
3448e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
3458e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
3468e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
3478e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
3488e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
3498e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
3508e6ea10cSStéphane Rochoy 				.npins       = 8,
3518e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
3528e6ea10cSStéphane Rochoy 			},
3538e6ea10cSStéphane Rochoy 			{
3548e6ea10cSStéphane Rochoy 				.grpnum      = 3,
3558e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6 },
3568e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
3578e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
3588e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
3598e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
3608e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
3618e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
3628e6ea10cSStéphane Rochoy 				.npins       = 7,
3638e6ea10cSStéphane Rochoy 				.iobase      = 0xe4,
3648e6ea10cSStéphane Rochoy 			},
3658e6ea10cSStéphane Rochoy 			{
3668e6ea10cSStéphane Rochoy 				.grpnum      = 4,
3678e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
3688e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
3698e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
3708e6ea10cSStéphane Rochoy 				.enable_mask = 0x04,
3718e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
3728e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
3738e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
3748e6ea10cSStéphane Rochoy 				.npins       = 8,
3758e6ea10cSStéphane Rochoy 				.iobase      = 0xf0, /* FIXME Page 344 say "F0~F2, E8",
3768e6ea10cSStéphane Rochoy 										not "F0~F3". */
3778e6ea10cSStéphane Rochoy 			},
3788e6ea10cSStéphane Rochoy 			{
3798e6ea10cSStéphane Rochoy 				.grpnum      = 5,
3808e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
3818e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
3828e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
3838e6ea10cSStéphane Rochoy 				.enable_mask = 0x08,
3848e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
3858e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
3868e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
3878e6ea10cSStéphane Rochoy 				.npins       = 8,
3888e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
3898e6ea10cSStéphane Rochoy 			},
3908e6ea10cSStéphane Rochoy 			{
3918e6ea10cSStéphane Rochoy 				.grpnum      = 6,
3928e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
3938e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
3948e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
3958e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
3968e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
3978e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
3988e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
3998e6ea10cSStéphane Rochoy 				.npins       = 8,
4008e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
4018e6ea10cSStéphane Rochoy 			},
4028e6ea10cSStéphane Rochoy 			{
4038e6ea10cSStéphane Rochoy 				.grpnum      = 7,
4048e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
4058e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
4068e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
4078e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
4088e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
4098e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
4108e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
4118e6ea10cSStéphane Rochoy 				.npins       = 8,
4128e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
4138e6ea10cSStéphane Rochoy 			},
4148e6ea10cSStéphane Rochoy 			{
4158e6ea10cSStéphane Rochoy 				.grpnum      = 8,
4168e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
4178e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
4188e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
4198e6ea10cSStéphane Rochoy 				.enable_mask = 0x04,
4208e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
4218e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
4228e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
4238e6ea10cSStéphane Rochoy 				.npins       = 8,
4248e6ea10cSStéphane Rochoy 				.iobase      = 0xe4,
4258e6ea10cSStéphane Rochoy 			},
4268e6ea10cSStéphane Rochoy 			{
4278e6ea10cSStéphane Rochoy 				.grpnum      = 9,
4288e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3 },
4298e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
4308e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
4318e6ea10cSStéphane Rochoy 				.enable_mask = 0x08,
4328e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
4338e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
4348e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
4358e6ea10cSStéphane Rochoy 				.npins       = 4,
4368e6ea10cSStéphane Rochoy 				.iobase      = 0xe8,
4378e6ea10cSStéphane Rochoy 			},
4388e6ea10cSStéphane Rochoy 		},
4398e6ea10cSStéphane Rochoy 	},
4408e6ea10cSStéphane Rochoy 	{
4418e6ea10cSStéphane Rochoy 		.devid   = 0xd42a,
4428e6ea10cSStéphane Rochoy 		.extid   = 2,
4438e6ea10cSStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT5585D",
4448e6ea10cSStéphane Rochoy 		.ngroups = 6,
4458e6ea10cSStéphane Rochoy 		.groups  = {
4468e6ea10cSStéphane Rochoy 			{
4478e6ea10cSStéphane Rochoy 				.grpnum      = 2,
4488e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6 },
4498e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
4508e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
4518e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
4528e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
4538e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1,
4548e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
4558e6ea10cSStéphane Rochoy 				.npins       = 7,
4568e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
4578e6ea10cSStéphane Rochoy 			},
4588e6ea10cSStéphane Rochoy 			{
4598e6ea10cSStéphane Rochoy 				.grpnum      = 3,
4608e6ea10cSStéphane Rochoy 				.pinbits     = { 1, 2, 3 },
4618e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
4628e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
4638e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
4648e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
4658e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe2,
4668e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
4678e6ea10cSStéphane Rochoy 				.npins       = 3,
4688e6ea10cSStéphane Rochoy 				.iobase      = 0xe4,
4698e6ea10cSStéphane Rochoy 			},
4708e6ea10cSStéphane Rochoy 			{
4718e6ea10cSStéphane Rochoy 				.grpnum      = 5,
4728e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 2, 6, 7 },
4738e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
4748e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
4758e6ea10cSStéphane Rochoy 				.enable_mask = 0x08,
4768e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
4778e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe4,
4788e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
4798e6ea10cSStéphane Rochoy 				.npins       = 4,
4808e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
4818e6ea10cSStéphane Rochoy 			},
4828e6ea10cSStéphane Rochoy 			{
4838e6ea10cSStéphane Rochoy 				.grpnum      = 7,
4848e6ea10cSStéphane Rochoy 				.pinbits     = { 4 },
4858e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
4868e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
4878e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
4888e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
4898e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe6,
4908e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
4918e6ea10cSStéphane Rochoy 				.npins       = 1,
4928e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
4938e6ea10cSStéphane Rochoy 			},
4948e6ea10cSStéphane Rochoy 			{
4958e6ea10cSStéphane Rochoy 				.grpnum      = 8,
4968e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
4978e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
4988e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
4998e6ea10cSStéphane Rochoy 				.enable_mask = 0x04,
5008e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
5018e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe7,
5028e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
5038e6ea10cSStéphane Rochoy 				.npins       = 8,
5048e6ea10cSStéphane Rochoy 				.iobase      = 0xe4,
5058e6ea10cSStéphane Rochoy 			},
5068e6ea10cSStéphane Rochoy 			{
5078e6ea10cSStéphane Rochoy 				.grpnum      = 9,
5088e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 2 },
5098e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
5108e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
5118e6ea10cSStéphane Rochoy 				.enable_mask = 0x08,
5128e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
5138e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xea,
5148e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
5158e6ea10cSStéphane Rochoy 				.npins       = 2,
5168e6ea10cSStéphane Rochoy 				.iobase      = 0xe8,
5178e6ea10cSStéphane Rochoy 			},
5188e6ea10cSStéphane Rochoy 		},
5198e6ea10cSStéphane Rochoy 	},
5208e6ea10cSStéphane Rochoy 	{
5218e6ea10cSStéphane Rochoy 		.devid   = 0xc562,
5228e6ea10cSStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT6779D",
5238e6ea10cSStéphane Rochoy 		.ngroups = 9,
5248e6ea10cSStéphane Rochoy 		.groups  = {
5258e6ea10cSStéphane Rochoy 			{
5268e6ea10cSStéphane Rochoy 				.grpnum      = 0,
5278e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
5288e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x08,
5298e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
5308e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
5318e6ea10cSStéphane Rochoy 				.data_ldn    = 0x08,
5328e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe0, /* FIXME Need to check for this group. */
5338e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
5348e6ea10cSStéphane Rochoy 				.npins       = 8,
5358e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
5368e6ea10cSStéphane Rochoy 			},
5378e6ea10cSStéphane Rochoy 			{
5388e6ea10cSStéphane Rochoy 				.grpnum      = 1,
5398e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
5408e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
5418e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
5428e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
5438e6ea10cSStéphane Rochoy 				.data_ldn    = 0x08,
5448e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
5458e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
5468e6ea10cSStéphane Rochoy 				.npins       = 8,
5478e6ea10cSStéphane Rochoy 				.iobase      = 0xf0,
5488e6ea10cSStéphane Rochoy 			},
5498e6ea10cSStéphane Rochoy 			{
5508e6ea10cSStéphane Rochoy 				.grpnum      = 2,
5518e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
5528e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
5538e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
5548e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
5558e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
5568e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
5578e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
5588e6ea10cSStéphane Rochoy 				.npins       = 8,
5598e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
5608e6ea10cSStéphane Rochoy 			},
5618e6ea10cSStéphane Rochoy 			{
5628e6ea10cSStéphane Rochoy 				.grpnum      = 3,
5638e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6 },
5648e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
5658e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
5668e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
5678e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
5688e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
5698e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
5708e6ea10cSStéphane Rochoy 				.npins       = 7,
5718e6ea10cSStéphane Rochoy 				.iobase      = 0xe4,
5728e6ea10cSStéphane Rochoy 			},
5738e6ea10cSStéphane Rochoy 			{
5748e6ea10cSStéphane Rochoy 				.grpnum      = 4,
5758e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
5768e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
5778e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
5788e6ea10cSStéphane Rochoy 				.enable_mask = 0x04,
5798e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
5808e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
5818e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
5828e6ea10cSStéphane Rochoy 				.npins       = 8,
5838e6ea10cSStéphane Rochoy 				.iobase      = 0xf0,
5848e6ea10cSStéphane Rochoy 			},
5858e6ea10cSStéphane Rochoy 			{
5868e6ea10cSStéphane Rochoy 				.grpnum      = 5,
5878e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
5888e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
5898e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
5908e6ea10cSStéphane Rochoy 				.enable_mask = 0x08,
5918e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
5928e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
5938e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
5948e6ea10cSStéphane Rochoy 				.npins       = 8,
5958e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
5968e6ea10cSStéphane Rochoy 			},
5978e6ea10cSStéphane Rochoy 			{
5988e6ea10cSStéphane Rochoy 				.grpnum      = 6,
5998e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
6008e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
6018e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
6028e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
6038e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
6048e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
6058e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
6068e6ea10cSStéphane Rochoy 				.npins       = 8,
6078e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
6088e6ea10cSStéphane Rochoy 			},
6098e6ea10cSStéphane Rochoy 			{
6108e6ea10cSStéphane Rochoy 				.grpnum      = 7,
6118e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6 },
6128e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
6138e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
6148e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
6158e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
6168e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
6178e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
6188e6ea10cSStéphane Rochoy 				.npins       = 7,
6198e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
6208e6ea10cSStéphane Rochoy 			},
6218e6ea10cSStéphane Rochoy 			{
6228e6ea10cSStéphane Rochoy 				.grpnum      = 8,
6238e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
6248e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
6258e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
6268e6ea10cSStéphane Rochoy 				.enable_mask = 0x04,
6278e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
6288e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
6298e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
6308e6ea10cSStéphane Rochoy 				.npins       = 8,
6318e6ea10cSStéphane Rochoy 				.iobase      = 0xe4,
6328e6ea10cSStéphane Rochoy 			},
6338e6ea10cSStéphane Rochoy 		},
6348e6ea10cSStéphane Rochoy 	},
6358e6ea10cSStéphane Rochoy 	{
6368e6ea10cSStéphane Rochoy 		.devid   = 0xd282,
6378e6ea10cSStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT6112D/NCT6114D/NCT6116D",
638*8e4aa631SStéphane Rochoy 		.ngroups = 9,
6398e6ea10cSStéphane Rochoy 		.groups  = {
6408e6ea10cSStéphane Rochoy 			{
6418e6ea10cSStéphane Rochoy 				.grpnum      = 0,
6428e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
6438e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
6448e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
6458e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
6468e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
647*8e4aa631SStéphane Rochoy 				.ppod_reg    = 0xe0,
6488e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
6498e6ea10cSStéphane Rochoy 				.npins       = 8,
6508e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
6518e6ea10cSStéphane Rochoy 			},
6528e6ea10cSStéphane Rochoy 			{
6538e6ea10cSStéphane Rochoy 				.grpnum      = 1,
6548e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
6558e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
6568e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
6578e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
6588e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
659*8e4aa631SStéphane Rochoy 				.ppod_reg    = 0xe1,
6608e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
6618e6ea10cSStéphane Rochoy 				.npins       = 8,
6628e6ea10cSStéphane Rochoy 				.iobase      = 0xe4,
6638e6ea10cSStéphane Rochoy 			},
6648e6ea10cSStéphane Rochoy 			{
6658e6ea10cSStéphane Rochoy 				.grpnum      = 2,
6668e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
6678e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
6688e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
6698e6ea10cSStéphane Rochoy 				.enable_mask = 0x04,
6708e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
671*8e4aa631SStéphane Rochoy 				.ppod_reg    = 0xe1,
6728e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
6738e6ea10cSStéphane Rochoy 				.npins       = 8,
6748e6ea10cSStéphane Rochoy 				.iobase      = 0xe8,
6758e6ea10cSStéphane Rochoy 			},
6768e6ea10cSStéphane Rochoy 			{
6778e6ea10cSStéphane Rochoy 				.grpnum      = 3,
6788e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
6798e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
6808e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
6818e6ea10cSStéphane Rochoy 				.enable_mask = 0x08,
6828e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
683*8e4aa631SStéphane Rochoy 				.ppod_reg    = 0xe1,
6848e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
6858e6ea10cSStéphane Rochoy 				.npins       = 8,
6868e6ea10cSStéphane Rochoy 				.iobase      = 0xec,
6878e6ea10cSStéphane Rochoy 			},
6888e6ea10cSStéphane Rochoy 			{
6898e6ea10cSStéphane Rochoy 				.grpnum      = 4,
6908e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
6918e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
6928e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
6938e6ea10cSStéphane Rochoy 				.enable_mask = 0x10,
6948e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
695*8e4aa631SStéphane Rochoy 				.ppod_reg    = 0xe1,
6968e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
6978e6ea10cSStéphane Rochoy 				.npins       = 8,
6988e6ea10cSStéphane Rochoy 				.iobase      = 0xf0,
6998e6ea10cSStéphane Rochoy 			},
7008e6ea10cSStéphane Rochoy 			{
7018e6ea10cSStéphane Rochoy 				.grpnum      = 5,
7028e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
7038e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
7048e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
7058e6ea10cSStéphane Rochoy 				.enable_mask = 0x20,
7068e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
707*8e4aa631SStéphane Rochoy 				.ppod_reg    = 0xe1,
7088e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
7098e6ea10cSStéphane Rochoy 				.npins       = 8,
7108e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
7118e6ea10cSStéphane Rochoy 			},
7128e6ea10cSStéphane Rochoy 			{
7138e6ea10cSStéphane Rochoy 				.grpnum      = 6,
7148e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
7158e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
7168e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
7178e6ea10cSStéphane Rochoy 				.enable_mask = 0x40,
7188e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
719*8e4aa631SStéphane Rochoy 				.ppod_reg    = 0xe1,
7208e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
7218e6ea10cSStéphane Rochoy 				.npins       = 8,
7228e6ea10cSStéphane Rochoy 				.iobase      = 0xf8,
7238e6ea10cSStéphane Rochoy 			},
7248e6ea10cSStéphane Rochoy 			{
7258e6ea10cSStéphane Rochoy 				.grpnum      = 7,
7268e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
7278e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
7288e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
7298e6ea10cSStéphane Rochoy 				.enable_mask = 0x80,
7308e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
731*8e4aa631SStéphane Rochoy 				.ppod_reg    = 0xe1,
7328e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
7338e6ea10cSStéphane Rochoy 				.npins       = 8,
7348e6ea10cSStéphane Rochoy 				.iobase      = 0xfc,
7358e6ea10cSStéphane Rochoy 			},
7368e6ea10cSStéphane Rochoy 			{
7378e6ea10cSStéphane Rochoy 				.grpnum      = 8,
7388e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
7398e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
7408e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
7418e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
7428e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
743*8e4aa631SStéphane Rochoy 				.ppod_reg    = 0xe1,
7448e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
7458e6ea10cSStéphane Rochoy 				.npins       = 8,
7468e6ea10cSStéphane Rochoy 				.iobase      = 0xf0,
7478e6ea10cSStéphane Rochoy 			},
7488e6ea10cSStéphane Rochoy 		},
7498e6ea10cSStéphane Rochoy 	},
7506b7b2d80SAdrian Chadd };
7516b7b2d80SAdrian Chadd 
7527f8d2ed0SStéphane Rochoy static const char *
io2str(uint8_t ioport)7537f8d2ed0SStéphane Rochoy io2str(uint8_t ioport)
7546b7b2d80SAdrian Chadd {
7557f8d2ed0SStéphane Rochoy 	switch (ioport) {
7567f8d2ed0SStéphane Rochoy 	case NCT_IO_GSR: return ("grpsel");
7577f8d2ed0SStéphane Rochoy 	case NCT_IO_IOR: return ("io");
7587f8d2ed0SStéphane Rochoy 	case NCT_IO_DAT: return ("data");
7597f8d2ed0SStéphane Rochoy 	case NCT_IO_INV: return ("inv");
7607f8d2ed0SStéphane Rochoy 	case NCT_IO_DST: return ("status");
7617f8d2ed0SStéphane Rochoy 	default:         return ("?");
7627f8d2ed0SStéphane Rochoy 	}
7637f8d2ed0SStéphane Rochoy }
7646b7b2d80SAdrian Chadd 
7657f8d2ed0SStéphane Rochoy static void
nct_io_set_group(struct nct_softc * sc,uint8_t grpnum)7667f8d2ed0SStéphane Rochoy nct_io_set_group(struct nct_softc *sc, uint8_t grpnum)
7677f8d2ed0SStéphane Rochoy {
768155514eaSAndriy Gapon 	GPIO_ASSERT_LOCKED(sc);
7697f8d2ed0SStéphane Rochoy 
7707f8d2ed0SStéphane Rochoy 	if (grpnum == sc->curgrp)
7717f8d2ed0SStéphane Rochoy 		return;
7727f8d2ed0SStéphane Rochoy 
7737f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x ioport %d\n",
7747f8d2ed0SStéphane Rochoy 		io2str(NCT_IO_GSR), grpnum, NCT_IO_GSR);
7757f8d2ed0SStéphane Rochoy 	bus_write_1(sc->iores, NCT_IO_GSR, grpnum);
7767f8d2ed0SStéphane Rochoy 	sc->curgrp = grpnum;
777155514eaSAndriy Gapon }
7786b7b2d80SAdrian Chadd 
779155514eaSAndriy Gapon static uint8_t
nct_io_read(struct nct_softc * sc,uint8_t grpnum,uint8_t reg)7807f8d2ed0SStéphane Rochoy nct_io_read(struct nct_softc *sc, uint8_t grpnum, uint8_t reg)
781155514eaSAndriy Gapon {
782155514eaSAndriy Gapon 	uint8_t val;
783155514eaSAndriy Gapon 
7847f8d2ed0SStéphane Rochoy 	nct_io_set_group(sc, grpnum);
785155514eaSAndriy Gapon 
7867f8d2ed0SStéphane Rochoy 	val = bus_read_1(sc->iores, reg);
7877f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "read %s 0x%x ioport %d\n",
7887f8d2ed0SStéphane Rochoy 		io2str(reg), val, reg);
789155514eaSAndriy Gapon 	return (val);
790155514eaSAndriy Gapon }
791155514eaSAndriy Gapon 
7927f8d2ed0SStéphane Rochoy static void
nct_io_write(struct nct_softc * sc,uint8_t grpnum,uint8_t reg,uint8_t val)7937f8d2ed0SStéphane Rochoy nct_io_write(struct nct_softc *sc, uint8_t grpnum, uint8_t reg, uint8_t val)
794155514eaSAndriy Gapon {
7957f8d2ed0SStéphane Rochoy 	nct_io_set_group(sc, grpnum);
7967f8d2ed0SStéphane Rochoy 
7977f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x ioport %d\n",
7987f8d2ed0SStéphane Rochoy 		io2str(reg), val, reg);
7997f8d2ed0SStéphane Rochoy 	bus_write_1(sc->iores, reg, val);
8007f8d2ed0SStéphane Rochoy }
8017f8d2ed0SStéphane Rochoy 
8027f8d2ed0SStéphane Rochoy static uint8_t
nct_get_ioreg(struct nct_softc * sc,reg_t reg,uint8_t grpnum)8037f8d2ed0SStéphane Rochoy nct_get_ioreg(struct nct_softc *sc, reg_t reg, uint8_t grpnum)
8047f8d2ed0SStéphane Rochoy {
8057f8d2ed0SStéphane Rochoy 	uint8_t iobase;
8067f8d2ed0SStéphane Rochoy 
8077f8d2ed0SStéphane Rochoy 	if (sc->iores != NULL)
8087f8d2ed0SStéphane Rochoy 		iobase = NCT_IO_IOR;
8097f8d2ed0SStéphane Rochoy 	else
8107f8d2ed0SStéphane Rochoy 		iobase = sc->grpmap[grpnum]->iobase;
8117f8d2ed0SStéphane Rochoy 	return (iobase + reg);
8127f8d2ed0SStéphane Rochoy }
8137f8d2ed0SStéphane Rochoy 
8147f8d2ed0SStéphane Rochoy static const char *
reg2str(reg_t reg)8157f8d2ed0SStéphane Rochoy reg2str(reg_t reg)
8167f8d2ed0SStéphane Rochoy {
8177f8d2ed0SStéphane Rochoy 	switch (reg) {
8187f8d2ed0SStéphane Rochoy 	case REG_IOR: return ("io");
8197f8d2ed0SStéphane Rochoy 	case REG_DAT: return ("data");
8207f8d2ed0SStéphane Rochoy 	case REG_INV: return ("inv");
8217f8d2ed0SStéphane Rochoy 	default:      return ("?");
8227f8d2ed0SStéphane Rochoy 	}
8237f8d2ed0SStéphane Rochoy }
8247f8d2ed0SStéphane Rochoy 
8257f8d2ed0SStéphane Rochoy static uint8_t
nct_read_reg(struct nct_softc * sc,reg_t reg,uint8_t grpnum)8267f8d2ed0SStéphane Rochoy nct_read_reg(struct nct_softc *sc, reg_t reg, uint8_t grpnum)
8277f8d2ed0SStéphane Rochoy {
8287f8d2ed0SStéphane Rochoy 	struct nct_gpio_group *gp;
8297f8d2ed0SStéphane Rochoy 	uint8_t                ioreg;
830155514eaSAndriy Gapon 	uint8_t                val;
831155514eaSAndriy Gapon 
8327f8d2ed0SStéphane Rochoy 	ioreg = nct_get_ioreg(sc, reg, grpnum);
833155514eaSAndriy Gapon 
8347f8d2ed0SStéphane Rochoy 	if (sc->iores != NULL)
8357f8d2ed0SStéphane Rochoy 		return (nct_io_read(sc, grpnum, ioreg));
8367f8d2ed0SStéphane Rochoy 
8377f8d2ed0SStéphane Rochoy 	gp  = sc->grpmap[grpnum];
8387f8d2ed0SStéphane Rochoy 	val = superio_ldn_read(sc->dev, gp->data_ldn, ioreg);
8397f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "read %s 0x%x from group GPIO%u ioreg 0x%x\n",
8407f8d2ed0SStéphane Rochoy 		reg2str(reg), val, grpnum, ioreg);
8417f8d2ed0SStéphane Rochoy 	return (val);
842155514eaSAndriy Gapon }
843155514eaSAndriy Gapon 
844155514eaSAndriy Gapon static int
nct_get_pin_cache(struct nct_softc * sc,uint32_t pin_num,uint8_t * cache)845155514eaSAndriy Gapon nct_get_pin_cache(struct nct_softc *sc, uint32_t pin_num, uint8_t *cache)
846155514eaSAndriy Gapon {
847155514eaSAndriy Gapon 	uint8_t bit;
848155514eaSAndriy Gapon 	uint8_t group;
849155514eaSAndriy Gapon 	uint8_t val;
850155514eaSAndriy Gapon 
8517f8d2ed0SStéphane Rochoy 	KASSERT(NCT_PIN_IS_VALID(sc, pin_num), ("%s: invalid pin number %d",
852155514eaSAndriy Gapon 	    __func__, pin_num));
853155514eaSAndriy Gapon 
8547f8d2ed0SStéphane Rochoy 	group = NCT_PIN_GRPNUM(sc, pin_num);
8557f8d2ed0SStéphane Rochoy 	bit   = NCT_PIN_BIT(sc, pin_num);
856155514eaSAndriy Gapon 	val   = cache[group];
857155514eaSAndriy Gapon 	return (GET_BIT(val, bit));
858155514eaSAndriy Gapon }
859155514eaSAndriy Gapon 
860155514eaSAndriy Gapon static void
nct_write_reg(struct nct_softc * sc,reg_t reg,uint8_t grpnum,uint8_t val)8617f8d2ed0SStéphane Rochoy nct_write_reg(struct nct_softc *sc, reg_t reg, uint8_t grpnum, uint8_t val)
862155514eaSAndriy Gapon {
8637f8d2ed0SStéphane Rochoy 	struct nct_gpio_group *gp;
864155514eaSAndriy Gapon 	uint8_t                ioreg;
865155514eaSAndriy Gapon 
8667f8d2ed0SStéphane Rochoy 	ioreg = nct_get_ioreg(sc, reg, grpnum);
8677f8d2ed0SStéphane Rochoy 
8687f8d2ed0SStéphane Rochoy 	if (sc->iores != NULL) {
8697f8d2ed0SStéphane Rochoy 		nct_io_write(sc, grpnum, ioreg, val);
8707f8d2ed0SStéphane Rochoy 		return;
8717f8d2ed0SStéphane Rochoy 	}
8727f8d2ed0SStéphane Rochoy 
8737f8d2ed0SStéphane Rochoy 	gp = sc->grpmap[grpnum];
8747f8d2ed0SStéphane Rochoy 	superio_ldn_write(sc->dev, gp->data_ldn, ioreg, val);
8757f8d2ed0SStéphane Rochoy 
8767f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x to group GPIO%u ioreg 0x%x\n",
8777f8d2ed0SStéphane Rochoy 		reg2str(reg), val, grpnum, ioreg);
878155514eaSAndriy Gapon }
879155514eaSAndriy Gapon 
880155514eaSAndriy Gapon static void
nct_set_pin_reg(struct nct_softc * sc,reg_t reg,uint32_t pin_num,bool val)881155514eaSAndriy Gapon nct_set_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num, bool val)
882155514eaSAndriy Gapon {
883155514eaSAndriy Gapon 	uint8_t *cache;
884155514eaSAndriy Gapon 	uint8_t bit;
885155514eaSAndriy Gapon 	uint8_t bitval;
886155514eaSAndriy Gapon 	uint8_t group;
887155514eaSAndriy Gapon 	uint8_t mask;
888155514eaSAndriy Gapon 
8897f8d2ed0SStéphane Rochoy 	KASSERT(NCT_PIN_IS_VALID(sc, pin_num),
890155514eaSAndriy Gapon 	    ("%s: invalid pin number %d", __func__, pin_num));
891155514eaSAndriy Gapon 	KASSERT(reg == REG_IOR || reg == REG_INV,
892155514eaSAndriy Gapon 	    ("%s: unsupported register %d", __func__, reg));
893155514eaSAndriy Gapon 
8947f8d2ed0SStéphane Rochoy 	group  = NCT_PIN_GRPNUM(sc, pin_num);
8957f8d2ed0SStéphane Rochoy 	bit    = NCT_PIN_BIT(sc, pin_num);
896155514eaSAndriy Gapon 	mask   = (uint8_t)1 << bit;
897155514eaSAndriy Gapon 	bitval = (uint8_t)val << bit;
898155514eaSAndriy Gapon 
899155514eaSAndriy Gapon 	if (reg == REG_IOR)
900155514eaSAndriy Gapon 		cache = &sc->cache.ior[group];
901155514eaSAndriy Gapon 	else
902155514eaSAndriy Gapon 		cache = &sc->cache.inv[group];
903155514eaSAndriy Gapon 	if ((*cache & mask) == bitval)
904155514eaSAndriy Gapon 		return;
905155514eaSAndriy Gapon 	*cache &= ~mask;
906155514eaSAndriy Gapon 	*cache |= bitval;
907155514eaSAndriy Gapon 	nct_write_reg(sc, reg, group, *cache);
9086b7b2d80SAdrian Chadd }
9096b7b2d80SAdrian Chadd 
9106b7b2d80SAdrian Chadd /*
911155514eaSAndriy Gapon  * Set a pin to input (val is true) or output (val is false) mode.
9126b7b2d80SAdrian Chadd  */
9136b7b2d80SAdrian Chadd static void
nct_set_pin_input(struct nct_softc * sc,uint32_t pin_num,bool val)914155514eaSAndriy Gapon nct_set_pin_input(struct nct_softc *sc, uint32_t pin_num, bool val)
9156b7b2d80SAdrian Chadd {
916155514eaSAndriy Gapon 	nct_set_pin_reg(sc, REG_IOR, pin_num, val);
9176b7b2d80SAdrian Chadd }
9186b7b2d80SAdrian Chadd 
9196b7b2d80SAdrian Chadd /*
9206b7b2d80SAdrian Chadd  * Check whether a pin is configured as an input.
9216b7b2d80SAdrian Chadd  */
9226b7b2d80SAdrian Chadd static bool
nct_pin_is_input(struct nct_softc * sc,uint32_t pin_num)9236b7b2d80SAdrian Chadd nct_pin_is_input(struct nct_softc *sc, uint32_t pin_num)
9246b7b2d80SAdrian Chadd {
925155514eaSAndriy Gapon 	return (nct_get_pin_cache(sc, pin_num, sc->cache.ior));
9266b7b2d80SAdrian Chadd }
9276b7b2d80SAdrian Chadd 
9286b7b2d80SAdrian Chadd /*
929155514eaSAndriy Gapon  * Set a pin to inverted (val is true) or normal (val is false) mode.
9306b7b2d80SAdrian Chadd  */
9316b7b2d80SAdrian Chadd static void
nct_set_pin_inverted(struct nct_softc * sc,uint32_t pin_num,bool val)932155514eaSAndriy Gapon nct_set_pin_inverted(struct nct_softc *sc, uint32_t pin_num, bool val)
9336b7b2d80SAdrian Chadd {
934155514eaSAndriy Gapon 	nct_set_pin_reg(sc, REG_INV, pin_num, val);
9356b7b2d80SAdrian Chadd }
9366b7b2d80SAdrian Chadd 
9376b7b2d80SAdrian Chadd static bool
nct_pin_is_inverted(struct nct_softc * sc,uint32_t pin_num)9386b7b2d80SAdrian Chadd nct_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num)
9396b7b2d80SAdrian Chadd {
940155514eaSAndriy Gapon 	return (nct_get_pin_cache(sc, pin_num, sc->cache.inv));
9416b7b2d80SAdrian Chadd }
9426b7b2d80SAdrian Chadd 
943155514eaSAndriy Gapon /*
944155514eaSAndriy Gapon  * Write a value to an output pin.
945155514eaSAndriy Gapon  * NB: the hardware remembers last output value across switching from
946155514eaSAndriy Gapon  * output mode to input mode and back.
947155514eaSAndriy Gapon  * Writes to a pin in input mode are not allowed here as they cannot
948155514eaSAndriy Gapon  * have any effect and would corrupt the output value cache.
949155514eaSAndriy Gapon  */
950155514eaSAndriy Gapon static void
nct_write_pin(struct nct_softc * sc,uint32_t pin_num,bool val)951155514eaSAndriy Gapon nct_write_pin(struct nct_softc *sc, uint32_t pin_num, bool val)
952155514eaSAndriy Gapon {
953155514eaSAndriy Gapon 	uint8_t bit;
954155514eaSAndriy Gapon 	uint8_t group;
955155514eaSAndriy Gapon 
956155514eaSAndriy Gapon 	KASSERT(!nct_pin_is_input(sc, pin_num), ("attempt to write input pin"));
9577f8d2ed0SStéphane Rochoy 	group = NCT_PIN_GRPNUM(sc, pin_num);
9587f8d2ed0SStéphane Rochoy 	bit   = NCT_PIN_BIT(sc, pin_num);
9597f8d2ed0SStéphane Rochoy 
960155514eaSAndriy Gapon 	if (GET_BIT(sc->cache.out_known[group], bit) &&
961155514eaSAndriy Gapon 	    GET_BIT(sc->cache.out[group], bit) == val) {
962155514eaSAndriy Gapon 		/* The pin is already in requested state. */
963155514eaSAndriy Gapon 		return;
964155514eaSAndriy Gapon 	}
965155514eaSAndriy Gapon 	sc->cache.out_known[group] |= 1 << bit;
966155514eaSAndriy Gapon 	if (val)
967155514eaSAndriy Gapon 		sc->cache.out[group] |= 1 << bit;
968155514eaSAndriy Gapon 	else
969155514eaSAndriy Gapon 		sc->cache.out[group] &= ~(1 << bit);
970155514eaSAndriy Gapon 	nct_write_reg(sc, REG_DAT, group, sc->cache.out[group]);
971155514eaSAndriy Gapon }
972155514eaSAndriy Gapon 
9737f8d2ed0SStéphane Rochoy static bool
nct_get_pin_reg(struct nct_softc * sc,reg_t reg,uint32_t pin_num)9747f8d2ed0SStéphane Rochoy nct_get_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num)
9757f8d2ed0SStéphane Rochoy {
9767f8d2ed0SStéphane Rochoy 	uint8_t            bit;
9777f8d2ed0SStéphane Rochoy 	uint8_t            group;
9787f8d2ed0SStéphane Rochoy 	uint8_t            val;
9797f8d2ed0SStéphane Rochoy 	bool               b;
9807f8d2ed0SStéphane Rochoy 
9817f8d2ed0SStéphane Rochoy 	KASSERT(NCT_PIN_IS_VALID(sc, pin_num), ("%s: invalid pin number %d",
9827f8d2ed0SStéphane Rochoy 			__func__, pin_num));
9837f8d2ed0SStéphane Rochoy 
9847f8d2ed0SStéphane Rochoy 	group = NCT_PIN_GRPNUM(sc, pin_num);
9857f8d2ed0SStéphane Rochoy 	bit   = NCT_PIN_BIT(sc, pin_num);
9867f8d2ed0SStéphane Rochoy 	val   = nct_read_reg(sc, reg, group);
9877f8d2ed0SStéphane Rochoy 	b     = GET_BIT(val, bit);
9887f8d2ed0SStéphane Rochoy 
9897f8d2ed0SStéphane Rochoy 	if (__predict_false(bootverbose)) {
9907f8d2ed0SStéphane Rochoy 		if (nct_pin_is_input(sc, pin_num))
9917f8d2ed0SStéphane Rochoy 			NCT_VERBOSE_PRINTF(sc->dev, "read %d from input pin %u<GPIO%u%u>\n",
9927f8d2ed0SStéphane Rochoy 				b, pin_num, group, bit);
9937f8d2ed0SStéphane Rochoy 		else
9947f8d2ed0SStéphane Rochoy 			NCT_VERBOSE_PRINTF(sc->dev,
9957f8d2ed0SStéphane Rochoy 				"read %d from output pin %u<GPIO%u%u>, cache miss\n",
9967f8d2ed0SStéphane Rochoy 				b, pin_num, group, bit);
9977f8d2ed0SStéphane Rochoy 	}
9987f8d2ed0SStéphane Rochoy 
9997f8d2ed0SStéphane Rochoy 	return (b);
10007f8d2ed0SStéphane Rochoy }
10017f8d2ed0SStéphane Rochoy 
1002155514eaSAndriy Gapon /*
1003155514eaSAndriy Gapon  * NB: state of an input pin cannot be cached, of course.
1004155514eaSAndriy Gapon  * For an output we can either take the value from the cache if it's valid
1005155514eaSAndriy Gapon  * or read the state from the hadrware and cache it.
1006155514eaSAndriy Gapon  */
1007155514eaSAndriy Gapon static bool
nct_read_pin(struct nct_softc * sc,uint32_t pin_num)1008155514eaSAndriy Gapon nct_read_pin(struct nct_softc *sc, uint32_t pin_num)
1009155514eaSAndriy Gapon {
1010155514eaSAndriy Gapon 	uint8_t bit;
1011155514eaSAndriy Gapon 	uint8_t group;
1012155514eaSAndriy Gapon 	bool    val;
1013155514eaSAndriy Gapon 
10147f8d2ed0SStéphane Rochoy 	if (nct_pin_is_input(sc, pin_num)) {
1015155514eaSAndriy Gapon 		return (nct_get_pin_reg(sc, REG_DAT, pin_num));
10167f8d2ed0SStéphane Rochoy 	}
1017155514eaSAndriy Gapon 
10187f8d2ed0SStéphane Rochoy 	group = NCT_PIN_GRPNUM(sc, pin_num);
10197f8d2ed0SStéphane Rochoy 	bit   = NCT_PIN_BIT(sc, pin_num);
10207f8d2ed0SStéphane Rochoy 
10217f8d2ed0SStéphane Rochoy 	if (GET_BIT(sc->cache.out_known[group], bit)) {
10227f8d2ed0SStéphane Rochoy 		val = GET_BIT(sc->cache.out[group], bit);
10237f8d2ed0SStéphane Rochoy 
10247f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(sc->dev,
10257f8d2ed0SStéphane Rochoy 			"read %d from output pin %u<GPIO%u%u>, cache hit\n",
10267f8d2ed0SStéphane Rochoy 			val, pin_num, group, bit);
10277f8d2ed0SStéphane Rochoy 
10287f8d2ed0SStéphane Rochoy 		return (val);
10297f8d2ed0SStéphane Rochoy 	}
1030155514eaSAndriy Gapon 
1031155514eaSAndriy Gapon 	val = nct_get_pin_reg(sc, REG_DAT, pin_num);
1032155514eaSAndriy Gapon 	sc->cache.out_known[group] |= 1 << bit;
1033155514eaSAndriy Gapon 	if (val)
1034155514eaSAndriy Gapon 		sc->cache.out[group] |= 1 << bit;
1035155514eaSAndriy Gapon 	else
1036155514eaSAndriy Gapon 		sc->cache.out[group] &= ~(1 << bit);
1037155514eaSAndriy Gapon 	return (val);
1038155514eaSAndriy Gapon }
1039155514eaSAndriy Gapon 
10408e6ea10cSStéphane Rochoy /* FIXME Incorret for NCT5585D and probably other chips. */
1041155514eaSAndriy Gapon static uint8_t
nct_ppod_reg(struct nct_softc * sc,uint32_t pin_num)10427f8d2ed0SStéphane Rochoy nct_ppod_reg(struct nct_softc *sc, uint32_t pin_num)
1043155514eaSAndriy Gapon {
10447f8d2ed0SStéphane Rochoy 	uint8_t group = NCT_PIN_GRPNUM(sc, pin_num);
10457f8d2ed0SStéphane Rochoy 
10467f8d2ed0SStéphane Rochoy 	return (sc->grpmap[group]->ppod_reg);
1047155514eaSAndriy Gapon }
1048155514eaSAndriy Gapon 
1049155514eaSAndriy Gapon /*
1050155514eaSAndriy Gapon  * NB: PP/OD can be configured only via configuration registers.
1051155514eaSAndriy Gapon  * Also, the registers are in a different logical device.
1052155514eaSAndriy Gapon  * So, this is a special case.  No caching too.
1053155514eaSAndriy Gapon  */
10546b7b2d80SAdrian Chadd static void
nct_set_pin_opendrain(struct nct_softc * sc,uint32_t pin_num)10556b7b2d80SAdrian Chadd nct_set_pin_opendrain(struct nct_softc *sc, uint32_t pin_num)
10566b7b2d80SAdrian Chadd {
10576b7b2d80SAdrian Chadd 	uint8_t reg;
10586b7b2d80SAdrian Chadd 	uint8_t outcfg;
10596b7b2d80SAdrian Chadd 
10607f8d2ed0SStéphane Rochoy 	reg = nct_ppod_reg(sc, pin_num);
10617f8d2ed0SStéphane Rochoy 	outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg);
10627f8d2ed0SStéphane Rochoy 	outcfg |= NCT_PIN_BITMASK(pin_num);
10637f8d2ed0SStéphane Rochoy 	superio_ldn_write(sc->dev, 0xf, reg, outcfg);
10646b7b2d80SAdrian Chadd }
10656b7b2d80SAdrian Chadd 
10666b7b2d80SAdrian Chadd static void
nct_set_pin_pushpull(struct nct_softc * sc,uint32_t pin_num)10676b7b2d80SAdrian Chadd nct_set_pin_pushpull(struct nct_softc *sc, uint32_t pin_num)
10686b7b2d80SAdrian Chadd {
10696b7b2d80SAdrian Chadd 	uint8_t reg;
10706b7b2d80SAdrian Chadd 	uint8_t outcfg;
10716b7b2d80SAdrian Chadd 
10727f8d2ed0SStéphane Rochoy 	reg = nct_ppod_reg(sc, pin_num);
10737f8d2ed0SStéphane Rochoy 	outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg);
10747f8d2ed0SStéphane Rochoy 	outcfg &= ~NCT_PIN_BITMASK(pin_num);
10757f8d2ed0SStéphane Rochoy 	superio_ldn_write(sc->dev, 0xf, reg, outcfg);
10766b7b2d80SAdrian Chadd }
10776b7b2d80SAdrian Chadd 
10786b7b2d80SAdrian Chadd static bool
nct_pin_is_opendrain(struct nct_softc * sc,uint32_t pin_num)10796b7b2d80SAdrian Chadd nct_pin_is_opendrain(struct nct_softc *sc, uint32_t pin_num)
10806b7b2d80SAdrian Chadd {
10816b7b2d80SAdrian Chadd 	uint8_t reg;
10826b7b2d80SAdrian Chadd 	uint8_t outcfg;
10836b7b2d80SAdrian Chadd 
10847f8d2ed0SStéphane Rochoy 	reg = nct_ppod_reg(sc, pin_num);
10857f8d2ed0SStéphane Rochoy 	outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg);
10867f8d2ed0SStéphane Rochoy 	return (outcfg & NCT_PIN_BITMASK(pin_num));
10877f8d2ed0SStéphane Rochoy }
10887f8d2ed0SStéphane Rochoy 
10897f8d2ed0SStéphane Rochoy static struct nct_device *
nct_lookup_device(device_t dev)10907f8d2ed0SStéphane Rochoy nct_lookup_device(device_t dev)
10917f8d2ed0SStéphane Rochoy {
10927f8d2ed0SStéphane Rochoy 	struct nct_device *nctdevp;
10937f8d2ed0SStéphane Rochoy 	uint16_t           devid;
10947f8d2ed0SStéphane Rochoy 	int                i, extid;
10957f8d2ed0SStéphane Rochoy 
10967f8d2ed0SStéphane Rochoy 	devid = superio_devid(dev);
10977f8d2ed0SStéphane Rochoy 	extid = superio_extid(dev);
10987f8d2ed0SStéphane Rochoy 	for (i = 0, nctdevp = nct_devices; i < nitems(nct_devices); i++, nctdevp++) {
10997f8d2ed0SStéphane Rochoy 		if (devid == nctdevp->devid && nctdevp->extid == extid)
11007f8d2ed0SStéphane Rochoy 			return (nctdevp);
11017f8d2ed0SStéphane Rochoy 	}
11027f8d2ed0SStéphane Rochoy 	return (NULL);
11036b7b2d80SAdrian Chadd }
11046b7b2d80SAdrian Chadd 
11056b7b2d80SAdrian Chadd static int
nct_probe(device_t dev)11066b7b2d80SAdrian Chadd nct_probe(device_t dev)
11076b7b2d80SAdrian Chadd {
11087f8d2ed0SStéphane Rochoy 	struct nct_device *nctdevp;
11097f8d2ed0SStéphane Rochoy 	uint8_t            ldn;
11106b7b2d80SAdrian Chadd 
11117f8d2ed0SStéphane Rochoy 	ldn = superio_get_ldn(dev);
11126b7b2d80SAdrian Chadd 
11137f8d2ed0SStéphane Rochoy 	if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON) {
11147f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not a Nuvoton device\n", ldn);
1115e3df342aSAndriy Gapon 		return (ENXIO);
11167f8d2ed0SStéphane Rochoy 	}
11177f8d2ed0SStéphane Rochoy 	if (superio_get_type(dev) != SUPERIO_DEV_GPIO) {
11187f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not a GPIO device\n", ldn);
11197f8d2ed0SStéphane Rochoy 		return (ENXIO);
11207f8d2ed0SStéphane Rochoy 	}
11216b7b2d80SAdrian Chadd 
11227f8d2ed0SStéphane Rochoy 	nctdevp = nct_lookup_device(dev);
11237f8d2ed0SStéphane Rochoy 	if (nctdevp == NULL) {
11247f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not supported\n", ldn);
11257f8d2ed0SStéphane Rochoy 		return (ENXIO);
11267f8d2ed0SStéphane Rochoy 	}
11277f8d2ed0SStéphane Rochoy 	device_set_desc(dev, nctdevp->descr);
11286b7b2d80SAdrian Chadd 	return (BUS_PROBE_DEFAULT);
11296b7b2d80SAdrian Chadd }
11306b7b2d80SAdrian Chadd 
11316b7b2d80SAdrian Chadd static int
nct_attach(device_t dev)11326b7b2d80SAdrian Chadd nct_attach(device_t dev)
11336b7b2d80SAdrian Chadd {
11346b7b2d80SAdrian Chadd 	struct nct_softc *sc;
11357f8d2ed0SStéphane Rochoy 	struct nct_gpio_group *gp;
11367f8d2ed0SStéphane Rochoy 	uint32_t pin_num;
11377f8d2ed0SStéphane Rochoy 	uint8_t v;
11387f8d2ed0SStéphane Rochoy 	int flags, i, g;
11396b7b2d80SAdrian Chadd 
11406b7b2d80SAdrian Chadd 	sc          = device_get_softc(dev);
1141e3df342aSAndriy Gapon 	sc->dev     = dev;
11427f8d2ed0SStéphane Rochoy 	sc->nctdevp = nct_lookup_device(dev);
11437f8d2ed0SStéphane Rochoy 
11447f8d2ed0SStéphane Rochoy 	flags = 0;
11457f8d2ed0SStéphane Rochoy 	(void)resource_int_value(device_get_name(dev), device_get_unit(dev), "flags", &flags);
11467f8d2ed0SStéphane Rochoy 
11477f8d2ed0SStéphane Rochoy 	if ((flags & NCT_PREFER_INDIRECT_CHANNEL) == 0) {
11487f8d2ed0SStéphane Rochoy 		uint16_t iobase;
11497f8d2ed0SStéphane Rochoy 		device_t dev_8;
11506b7b2d80SAdrian Chadd 
1151155514eaSAndriy Gapon 		/*
1152155514eaSAndriy Gapon 		 * As strange as it may seem, I/O port base is configured in the
1153155514eaSAndriy Gapon 		 * Logical Device 8 which is primarily used for WDT, but also plays
1154155514eaSAndriy Gapon 		 * a role in GPIO configuration.
1155155514eaSAndriy Gapon 		 */
1156155514eaSAndriy Gapon 		iobase = 0;
1157155514eaSAndriy Gapon 		dev_8 = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_WDT, 8);
1158155514eaSAndriy Gapon 		if (dev_8 != NULL)
1159155514eaSAndriy Gapon 			iobase = superio_get_iobase(dev_8);
1160155514eaSAndriy Gapon 		if (iobase != 0 && iobase != 0xffff) {
11617f8d2ed0SStéphane Rochoy 			int err;
11627f8d2ed0SStéphane Rochoy 
11637f8d2ed0SStéphane Rochoy 			NCT_VERBOSE_PRINTF(dev, "iobase %#x\n", iobase);
1164155514eaSAndriy Gapon 			sc->curgrp = -1;
1165155514eaSAndriy Gapon 			sc->iorid = 0;
1166155514eaSAndriy Gapon 			err = bus_set_resource(dev, SYS_RES_IOPORT, sc->iorid,
11678e6ea10cSStéphane Rochoy 				iobase, 7); /* FIXME NCT6796D-E have 8 registers according to table 18.3. */
1168155514eaSAndriy Gapon 			if (err == 0) {
1169155514eaSAndriy Gapon 				sc->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
1170155514eaSAndriy Gapon 					&sc->iorid, RF_ACTIVE);
1171155514eaSAndriy Gapon 				if (sc->iores == NULL) {
1172155514eaSAndriy Gapon 					device_printf(dev, "can't map i/o space, "
11737f8d2ed0SStéphane Rochoy 						"iobase=%#x\n", iobase);
1174155514eaSAndriy Gapon 				}
1175155514eaSAndriy Gapon 			} else {
1176155514eaSAndriy Gapon 				device_printf(dev,
11777f8d2ed0SStéphane Rochoy 					"failed to set io port resource at %#x\n", iobase);
1178155514eaSAndriy Gapon 			}
1179155514eaSAndriy Gapon 		}
11807f8d2ed0SStéphane Rochoy 	}
11817f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(dev, "iores %p %s channel\n",
11827f8d2ed0SStéphane Rochoy 		sc->iores, (sc->iores ? "direct" : "indirect"));
1183155514eaSAndriy Gapon 
11847f8d2ed0SStéphane Rochoy 	/* Enable GPIO groups */
11857f8d2ed0SStéphane Rochoy 	for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) {
11867f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(dev,
11877f8d2ed0SStéphane Rochoy 			"GPIO%d: %d pins, enable with mask 0x%x via ldn 0x%x reg 0x%x\n",
11887f8d2ed0SStéphane Rochoy 			gp->grpnum, gp->npins, gp->enable_mask, gp->enable_ldn,
11897f8d2ed0SStéphane Rochoy 			gp->enable_reg);
11907f8d2ed0SStéphane Rochoy 		v = superio_ldn_read(dev, gp->enable_ldn, gp->enable_reg);
11917f8d2ed0SStéphane Rochoy 		v |= gp->enable_mask;
11927f8d2ed0SStéphane Rochoy 		superio_ldn_write(dev, gp->enable_ldn, gp->enable_reg, v);
11937f8d2ed0SStéphane Rochoy 	}
1194e3df342aSAndriy Gapon 
1195e3df342aSAndriy Gapon 	GPIO_LOCK_INIT(sc);
1196e3df342aSAndriy Gapon 	GPIO_LOCK(sc);
11976b7b2d80SAdrian Chadd 
11987f8d2ed0SStéphane Rochoy 	pin_num   = 0;
11997f8d2ed0SStéphane Rochoy 	sc->npins = 0;
12007f8d2ed0SStéphane Rochoy 	for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) {
1201f03a7e52SStéphane Rochoy 
1202f03a7e52SStéphane Rochoy 		sc->grpmap[gp->grpnum] = gp;
1203f03a7e52SStéphane Rochoy 
1204f03a7e52SStéphane Rochoy 		/*
1205f03a7e52SStéphane Rochoy 		 * Caching input values is meaningless as an input can be changed at any
1206f03a7e52SStéphane Rochoy 		 * time by an external agent.  But outputs are controlled by this
1207f03a7e52SStéphane Rochoy 		 * driver, so it can cache their state.  Also, the hardware remembers
1208f03a7e52SStéphane Rochoy 		 * the output state of a pin when the pin is switched to input mode and
1209f03a7e52SStéphane Rochoy 		 * then back to output mode.  So, the cache stays valid.
1210f03a7e52SStéphane Rochoy 		 * The only problem is with pins that are in input mode at the attach
1211f03a7e52SStéphane Rochoy 		 * time.  For them the output state is not known until it is set by the
1212f03a7e52SStéphane Rochoy 		 * driver for the first time.
1213f03a7e52SStéphane Rochoy 		 * 'out' and 'out_known' bits form a tri-state output cache:
1214f03a7e52SStéphane Rochoy 		 * |-----+-----------+---------|
1215f03a7e52SStéphane Rochoy 		 * | out | out_known | cache   |
1216f03a7e52SStéphane Rochoy 		 * |-----+-----------+---------|
1217f03a7e52SStéphane Rochoy 		 * |   X |         0 | invalid |
1218f03a7e52SStéphane Rochoy 		 * |   0 |         1 |       0 |
1219f03a7e52SStéphane Rochoy 		 * |   1 |         1 |       1 |
1220f03a7e52SStéphane Rochoy 		 * |-----+-----------+---------|
1221f03a7e52SStéphane Rochoy 		 */
1222f03a7e52SStéphane Rochoy 		sc->cache.inv[gp->grpnum]       = nct_read_reg(sc, REG_INV, gp->grpnum);
1223f03a7e52SStéphane Rochoy 		sc->cache.ior[gp->grpnum]       = nct_read_reg(sc, REG_IOR, gp->grpnum);
1224f03a7e52SStéphane Rochoy 		sc->cache.out[gp->grpnum]       = nct_read_reg(sc, REG_DAT, gp->grpnum);
1225f03a7e52SStéphane Rochoy 		sc->cache.out_known[gp->grpnum] = ~sc->cache.ior[gp->grpnum];
1226f03a7e52SStéphane Rochoy 
12277f8d2ed0SStéphane Rochoy 		sc->npins += gp->npins;
12287f8d2ed0SStéphane Rochoy 		for (i = 0; i < gp->npins; i++, pin_num++) {
12297f8d2ed0SStéphane Rochoy 			struct gpio_pin *pin;
12307f8d2ed0SStéphane Rochoy 
12317f8d2ed0SStéphane Rochoy 			sc->pinmap[pin_num].group  = gp;
12327f8d2ed0SStéphane Rochoy 			sc->pinmap[pin_num].grpnum = gp->grpnum;
12337f8d2ed0SStéphane Rochoy 			sc->pinmap[pin_num].bit    = gp->pinbits[i];
12347f8d2ed0SStéphane Rochoy 
12357f8d2ed0SStéphane Rochoy 			pin           = &sc->pins[pin_num];
12367f8d2ed0SStéphane Rochoy 			pin->gp_pin   = pin_num;
12377f8d2ed0SStéphane Rochoy 			pin->gp_caps  = gp->caps;
12387f8d2ed0SStéphane Rochoy 			pin->gp_flags = 0;
12397f8d2ed0SStéphane Rochoy 
12407f8d2ed0SStéphane Rochoy 			snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%u%u",
12417f8d2ed0SStéphane Rochoy 				gp->grpnum, gp->pinbits[i]);
12427f8d2ed0SStéphane Rochoy 
12437f8d2ed0SStéphane Rochoy 			if (nct_pin_is_input(sc, pin_num))
12447f8d2ed0SStéphane Rochoy 				pin->gp_flags |= GPIO_PIN_INPUT;
12457f8d2ed0SStéphane Rochoy 			else
12467f8d2ed0SStéphane Rochoy 				pin->gp_flags |= GPIO_PIN_OUTPUT;
12477f8d2ed0SStéphane Rochoy 
12487f8d2ed0SStéphane Rochoy 			if (nct_pin_is_opendrain(sc, pin_num))
12497f8d2ed0SStéphane Rochoy 				pin->gp_flags |= GPIO_PIN_OPENDRAIN;
12507f8d2ed0SStéphane Rochoy 			else
12517f8d2ed0SStéphane Rochoy 				pin->gp_flags |= GPIO_PIN_PUSHPULL;
12527f8d2ed0SStéphane Rochoy 
12537f8d2ed0SStéphane Rochoy 			if (nct_pin_is_inverted(sc, pin_num))
12547f8d2ed0SStéphane Rochoy 				pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
12557f8d2ed0SStéphane Rochoy 		}
12567f8d2ed0SStéphane Rochoy 	}
12577f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(dev, "%d pins available\n", sc->npins);
1258155514eaSAndriy Gapon 
12596b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
12606b7b2d80SAdrian Chadd 
12616b7b2d80SAdrian Chadd 	sc->busdev = gpiobus_attach_bus(dev);
12626b7b2d80SAdrian Chadd 	if (sc->busdev == NULL) {
12637f8d2ed0SStéphane Rochoy 		device_printf(dev, "failed to attach to gpiobus\n");
12646b7b2d80SAdrian Chadd 		GPIO_LOCK_DESTROY(sc);
12656b7b2d80SAdrian Chadd 		return (ENXIO);
12666b7b2d80SAdrian Chadd 	}
12676b7b2d80SAdrian Chadd 
12686b7b2d80SAdrian Chadd 	return (0);
12696b7b2d80SAdrian Chadd }
12706b7b2d80SAdrian Chadd 
12716b7b2d80SAdrian Chadd static int
nct_detach(device_t dev)12726b7b2d80SAdrian Chadd nct_detach(device_t dev)
12736b7b2d80SAdrian Chadd {
12746b7b2d80SAdrian Chadd 	struct nct_softc *sc;
12756b7b2d80SAdrian Chadd 
12766b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
12776b7b2d80SAdrian Chadd 	gpiobus_detach_bus(dev);
12786b7b2d80SAdrian Chadd 
1279155514eaSAndriy Gapon 	if (sc->iores != NULL)
1280155514eaSAndriy Gapon 		bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->iores);
12816b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
12826b7b2d80SAdrian Chadd 	GPIO_LOCK_DESTROY(sc);
12836b7b2d80SAdrian Chadd 
12846b7b2d80SAdrian Chadd 	return (0);
12856b7b2d80SAdrian Chadd }
12866b7b2d80SAdrian Chadd 
12876b7b2d80SAdrian Chadd static device_t
nct_gpio_get_bus(device_t dev)12886b7b2d80SAdrian Chadd nct_gpio_get_bus(device_t dev)
12896b7b2d80SAdrian Chadd {
12906b7b2d80SAdrian Chadd 	struct nct_softc *sc;
12916b7b2d80SAdrian Chadd 
12926b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
12936b7b2d80SAdrian Chadd 
12946b7b2d80SAdrian Chadd 	return (sc->busdev);
12956b7b2d80SAdrian Chadd }
12966b7b2d80SAdrian Chadd 
12976b7b2d80SAdrian Chadd static int
nct_gpio_pin_max(device_t dev,int * maxpin)12987f8d2ed0SStéphane Rochoy nct_gpio_pin_max(device_t dev, int *maxpin)
12996b7b2d80SAdrian Chadd {
13007f8d2ed0SStéphane Rochoy 	struct nct_softc *sc;
13016b7b2d80SAdrian Chadd 
13027f8d2ed0SStéphane Rochoy 	sc      = device_get_softc(dev);
13037f8d2ed0SStéphane Rochoy 	*maxpin = sc->npins - 1;
13046b7b2d80SAdrian Chadd 	return (0);
13056b7b2d80SAdrian Chadd }
13066b7b2d80SAdrian Chadd 
13076b7b2d80SAdrian Chadd static int
nct_gpio_pin_set(device_t dev,uint32_t pin_num,uint32_t pin_value)13086b7b2d80SAdrian Chadd nct_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value)
13096b7b2d80SAdrian Chadd {
13106b7b2d80SAdrian Chadd 	struct nct_softc *sc;
13116b7b2d80SAdrian Chadd 
13127f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
13137f8d2ed0SStéphane Rochoy 
13147f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
13156b7b2d80SAdrian Chadd 		return (EINVAL);
13166b7b2d80SAdrian Chadd 
13176b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
1318155514eaSAndriy Gapon 	if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) {
1319155514eaSAndriy Gapon 		GPIO_UNLOCK(sc);
1320155514eaSAndriy Gapon 		return (EINVAL);
1321155514eaSAndriy Gapon 	}
13226b7b2d80SAdrian Chadd 	nct_write_pin(sc, pin_num, pin_value);
13236b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
13246b7b2d80SAdrian Chadd 
13256b7b2d80SAdrian Chadd 	return (0);
13266b7b2d80SAdrian Chadd }
13276b7b2d80SAdrian Chadd 
13286b7b2d80SAdrian Chadd static int
nct_gpio_pin_get(device_t dev,uint32_t pin_num,uint32_t * pin_value)13296b7b2d80SAdrian Chadd nct_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value)
13306b7b2d80SAdrian Chadd {
13316b7b2d80SAdrian Chadd 	struct nct_softc *sc;
13326b7b2d80SAdrian Chadd 
13337f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
13347f8d2ed0SStéphane Rochoy 
13357f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
13366b7b2d80SAdrian Chadd 		return (EINVAL);
13376b7b2d80SAdrian Chadd 
13386b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
13396b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
13406b7b2d80SAdrian Chadd 	*pin_value = nct_read_pin(sc, pin_num);
13416b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
13426b7b2d80SAdrian Chadd 
13436b7b2d80SAdrian Chadd 	return (0);
13446b7b2d80SAdrian Chadd }
13456b7b2d80SAdrian Chadd 
13466b7b2d80SAdrian Chadd static int
nct_gpio_pin_toggle(device_t dev,uint32_t pin_num)13476b7b2d80SAdrian Chadd nct_gpio_pin_toggle(device_t dev, uint32_t pin_num)
13486b7b2d80SAdrian Chadd {
13496b7b2d80SAdrian Chadd 	struct nct_softc *sc;
13506b7b2d80SAdrian Chadd 
13517f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
13527f8d2ed0SStéphane Rochoy 
13537f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
13546b7b2d80SAdrian Chadd 		return (EINVAL);
13556b7b2d80SAdrian Chadd 
13566b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
13576b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
1358155514eaSAndriy Gapon 	if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) {
1359155514eaSAndriy Gapon 		GPIO_UNLOCK(sc);
1360155514eaSAndriy Gapon 		return (EINVAL);
1361155514eaSAndriy Gapon 	}
13626b7b2d80SAdrian Chadd 	if (nct_read_pin(sc, pin_num))
13636b7b2d80SAdrian Chadd 		nct_write_pin(sc, pin_num, 0);
13646b7b2d80SAdrian Chadd 	else
13656b7b2d80SAdrian Chadd 		nct_write_pin(sc, pin_num, 1);
13666b7b2d80SAdrian Chadd 
13676b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
13686b7b2d80SAdrian Chadd 
13696b7b2d80SAdrian Chadd 	return (0);
13706b7b2d80SAdrian Chadd }
13716b7b2d80SAdrian Chadd 
13726b7b2d80SAdrian Chadd static int
nct_gpio_pin_getcaps(device_t dev,uint32_t pin_num,uint32_t * caps)13736b7b2d80SAdrian Chadd nct_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps)
13746b7b2d80SAdrian Chadd {
13756b7b2d80SAdrian Chadd 	struct nct_softc *sc;
13766b7b2d80SAdrian Chadd 
13777f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
13787f8d2ed0SStéphane Rochoy 
13797f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
13806b7b2d80SAdrian Chadd 		return (EINVAL);
13816b7b2d80SAdrian Chadd 
13826b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
13836b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
13846b7b2d80SAdrian Chadd 	*caps = sc->pins[pin_num].gp_caps;
13856b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
13866b7b2d80SAdrian Chadd 
13876b7b2d80SAdrian Chadd 	return (0);
13886b7b2d80SAdrian Chadd }
13896b7b2d80SAdrian Chadd 
13906b7b2d80SAdrian Chadd static int
nct_gpio_pin_getflags(device_t dev,uint32_t pin_num,uint32_t * flags)13916b7b2d80SAdrian Chadd nct_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags)
13926b7b2d80SAdrian Chadd {
13936b7b2d80SAdrian Chadd 	struct nct_softc *sc;
13946b7b2d80SAdrian Chadd 
13957f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
13967f8d2ed0SStéphane Rochoy 
13977f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
13986b7b2d80SAdrian Chadd 		return (EINVAL);
13996b7b2d80SAdrian Chadd 
14006b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
14016b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
14026b7b2d80SAdrian Chadd 	*flags = sc->pins[pin_num].gp_flags;
14036b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
14046b7b2d80SAdrian Chadd 
14056b7b2d80SAdrian Chadd 	return (0);
14066b7b2d80SAdrian Chadd }
14076b7b2d80SAdrian Chadd 
14086b7b2d80SAdrian Chadd static int
nct_gpio_pin_getname(device_t dev,uint32_t pin_num,char * name)14096b7b2d80SAdrian Chadd nct_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name)
14106b7b2d80SAdrian Chadd {
14116b7b2d80SAdrian Chadd 	struct nct_softc *sc;
14126b7b2d80SAdrian Chadd 
14137f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
14147f8d2ed0SStéphane Rochoy 
14157f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
14166b7b2d80SAdrian Chadd 		return (EINVAL);
14176b7b2d80SAdrian Chadd 
14186b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
14196b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
14206b7b2d80SAdrian Chadd 	memcpy(name, sc->pins[pin_num].gp_name, GPIOMAXNAME);
14216b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
14226b7b2d80SAdrian Chadd 
14236b7b2d80SAdrian Chadd 	return (0);
14246b7b2d80SAdrian Chadd }
14256b7b2d80SAdrian Chadd 
14266b7b2d80SAdrian Chadd static int
nct_gpio_pin_setflags(device_t dev,uint32_t pin_num,uint32_t flags)14276b7b2d80SAdrian Chadd nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags)
14286b7b2d80SAdrian Chadd {
14296b7b2d80SAdrian Chadd 	struct nct_softc *sc;
14306b7b2d80SAdrian Chadd 	struct gpio_pin *pin;
14316b7b2d80SAdrian Chadd 
14327f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
14337f8d2ed0SStéphane Rochoy 
14347f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
14356b7b2d80SAdrian Chadd 		return (EINVAL);
14366b7b2d80SAdrian Chadd 
14376b7b2d80SAdrian Chadd 	pin = &sc->pins[pin_num];
14386b7b2d80SAdrian Chadd 	if ((flags & pin->gp_caps) != flags)
14396b7b2d80SAdrian Chadd 		return (EINVAL);
14406b7b2d80SAdrian Chadd 
14416b7b2d80SAdrian Chadd 	if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
14426b7b2d80SAdrian Chadd 		(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
14436b7b2d80SAdrian Chadd 			return (EINVAL);
14446b7b2d80SAdrian Chadd 	}
14456b7b2d80SAdrian Chadd 	if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) ==
14466b7b2d80SAdrian Chadd 		(GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) {
1447155514eaSAndriy Gapon 			return (EINVAL);
1448155514eaSAndriy Gapon 	}
1449155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) ==
1450155514eaSAndriy Gapon 		(GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) {
14516b7b2d80SAdrian Chadd 			return (EINVAL);
14526b7b2d80SAdrian Chadd 	}
14536b7b2d80SAdrian Chadd 
1454155514eaSAndriy Gapon 	GPIO_ASSERT_UNLOCKED(sc);
1455155514eaSAndriy Gapon 	GPIO_LOCK(sc);
14567f8d2ed0SStéphane Rochoy 
14577f8d2ed0SStéphane Rochoy 	/* input or output */
1458155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) != 0) {
1459155514eaSAndriy Gapon 		nct_set_pin_input(sc, pin_num, (flags & GPIO_PIN_INPUT) != 0);
1460155514eaSAndriy Gapon 		pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
1461155514eaSAndriy Gapon 		pin->gp_flags |= flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
1462155514eaSAndriy Gapon 	}
14637f8d2ed0SStéphane Rochoy 
14647f8d2ed0SStéphane Rochoy 	/* invert */
1465155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) != 0) {
14667f8d2ed0SStéphane Rochoy 		nct_set_pin_inverted(sc, pin_num, 1);
14677f8d2ed0SStéphane Rochoy 		pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
14687f8d2ed0SStéphane Rochoy 	} else {
14697f8d2ed0SStéphane Rochoy 		nct_set_pin_inverted(sc, pin_num, 0);
1470155514eaSAndriy Gapon 		pin->gp_flags &= ~(GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
1471155514eaSAndriy Gapon 	}
14727f8d2ed0SStéphane Rochoy 
14737f8d2ed0SStéphane Rochoy 	/* Open drain or push pull */
1474155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) != 0) {
14756b7b2d80SAdrian Chadd 		if (flags & GPIO_PIN_OPENDRAIN)
14766b7b2d80SAdrian Chadd 			nct_set_pin_opendrain(sc, pin_num);
14776b7b2d80SAdrian Chadd 		else
14786b7b2d80SAdrian Chadd 			nct_set_pin_pushpull(sc, pin_num);
1479155514eaSAndriy Gapon 		pin->gp_flags &= ~(GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL);
1480155514eaSAndriy Gapon 		pin->gp_flags |=
1481155514eaSAndriy Gapon 		    flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL);
14826b7b2d80SAdrian Chadd 	}
14836b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
14846b7b2d80SAdrian Chadd 
14856b7b2d80SAdrian Chadd 	return (0);
14866b7b2d80SAdrian Chadd }
14876b7b2d80SAdrian Chadd 
14886b7b2d80SAdrian Chadd static device_method_t nct_methods[] = {
14896b7b2d80SAdrian Chadd 	/* Device interface */
14906b7b2d80SAdrian Chadd 	DEVMETHOD(device_probe,		nct_probe),
14916b7b2d80SAdrian Chadd 	DEVMETHOD(device_attach,	nct_attach),
14926b7b2d80SAdrian Chadd 	DEVMETHOD(device_detach,	nct_detach),
14936b7b2d80SAdrian Chadd 
14946b7b2d80SAdrian Chadd 	/* GPIO */
14956b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_get_bus,		nct_gpio_get_bus),
14966b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_max,		nct_gpio_pin_max),
14976b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_get,		nct_gpio_pin_get),
14986b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_set,		nct_gpio_pin_set),
14996b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_toggle,	nct_gpio_pin_toggle),
15006b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getname,	nct_gpio_pin_getname),
15016b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getcaps,	nct_gpio_pin_getcaps),
15026b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getflags,	nct_gpio_pin_getflags),
15036b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_setflags,	nct_gpio_pin_setflags),
15046b7b2d80SAdrian Chadd 
15056b7b2d80SAdrian Chadd 	DEVMETHOD_END
15066b7b2d80SAdrian Chadd };
15076b7b2d80SAdrian Chadd 
1508e3df342aSAndriy Gapon static driver_t nct_driver = {
15096b7b2d80SAdrian Chadd 	"gpio",
15106b7b2d80SAdrian Chadd 	nct_methods,
15116b7b2d80SAdrian Chadd 	sizeof(struct nct_softc)
15126b7b2d80SAdrian Chadd };
15136b7b2d80SAdrian Chadd 
151484c5f982SJohn Baldwin DRIVER_MODULE(nctgpio, superio, nct_driver, NULL, NULL);
15156b7b2d80SAdrian Chadd MODULE_DEPEND(nctgpio, gpiobus, 1, 1, 1);
1516e3df342aSAndriy Gapon MODULE_DEPEND(nctgpio, superio, 1, 1, 1);
1517e3df342aSAndriy Gapon MODULE_VERSION(nctgpio, 1);
1518