xref: /freebsd/sys/dev/nctgpio/nctgpio.c (revision 8e6ea10c31220b5f56dbd966a0ee7163d46d30d1)
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 #include <sys/cdefs.h>
356b7b2d80SAdrian Chadd 
366b7b2d80SAdrian Chadd #include <sys/param.h>
376b7b2d80SAdrian Chadd #include <sys/kernel.h>
386b7b2d80SAdrian Chadd #include <sys/systm.h>
396b7b2d80SAdrian Chadd #include <sys/bus.h>
406b7b2d80SAdrian Chadd #include <sys/eventhandler.h>
416b7b2d80SAdrian Chadd #include <sys/lock.h>
426b7b2d80SAdrian Chadd 
436b7b2d80SAdrian Chadd #include <sys/module.h>
446b7b2d80SAdrian Chadd #include <sys/gpio.h>
456b7b2d80SAdrian Chadd 
466b7b2d80SAdrian Chadd #include <machine/bus.h>
476b7b2d80SAdrian Chadd 
486b7b2d80SAdrian Chadd #include <dev/gpio/gpiobusvar.h>
49e3df342aSAndriy Gapon #include <dev/superio/superio.h>
506b7b2d80SAdrian Chadd 
516b7b2d80SAdrian Chadd #include "gpio_if.h"
526b7b2d80SAdrian Chadd 
537f8d2ed0SStéphane Rochoy #define NCT_PPOD_LDN 0xf /* LDN used to select Push-Pull/Open-Drain */
546b7b2d80SAdrian Chadd 
557f8d2ed0SStéphane Rochoy /* Direct access through GPIO register table */
567f8d2ed0SStéphane Rochoy #define	NCT_IO_GSR			0 /* Group Select */
577f8d2ed0SStéphane Rochoy #define	NCT_IO_IOR			1 /* I/O */
587f8d2ed0SStéphane Rochoy #define	NCT_IO_DAT			2 /* Data */
597f8d2ed0SStéphane Rochoy #define	NCT_IO_INV			3 /* Inversion */
607f8d2ed0SStéphane Rochoy #define	NCT_IO_DST          4 /* Status */
616b7b2d80SAdrian Chadd 
627f8d2ed0SStéphane Rochoy #define NCT_MAX_GROUP   9
637f8d2ed0SStéphane Rochoy #define NCT_MAX_PIN     75
646b7b2d80SAdrian Chadd 
657f8d2ed0SStéphane Rochoy #define NCT_PIN_IS_VALID(_sc, _p)   ((_p) < (_sc)->npins)
667f8d2ed0SStéphane Rochoy #define NCT_PIN_GROUP(_sc, _p)      ((_sc)->pinmap[(_p)].group)
677f8d2ed0SStéphane Rochoy #define NCT_PIN_GRPNUM(_sc, _p)     ((_sc)->pinmap[(_p)].grpnum)
687f8d2ed0SStéphane Rochoy #define NCT_PIN_BIT(_sc, _p)        ((_sc)->pinmap[(_p)].bit)
697f8d2ed0SStéphane Rochoy #define NCT_PIN_BITMASK(_p)         (1 << ((_p) & 7))
706b7b2d80SAdrian Chadd 
716b7b2d80SAdrian Chadd #define NCT_GPIO_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
726b7b2d80SAdrian Chadd 	GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \
736b7b2d80SAdrian Chadd 	GPIO_PIN_INVIN | GPIO_PIN_INVOUT)
746b7b2d80SAdrian Chadd 
757f8d2ed0SStéphane Rochoy #define NCT_PREFER_INDIRECT_CHANNEL 2
767f8d2ed0SStéphane Rochoy 
777f8d2ed0SStéphane Rochoy #define NCT_VERBOSE_PRINTF(dev, ...)            \
787f8d2ed0SStéphane Rochoy 	do {                                        \
797f8d2ed0SStéphane Rochoy 		if (__predict_false(bootverbose))       \
807f8d2ed0SStéphane Rochoy 			device_printf(dev, __VA_ARGS__);    \
817f8d2ed0SStéphane Rochoy 	} while (0)
827f8d2ed0SStéphane Rochoy 
83155514eaSAndriy Gapon /*
84155514eaSAndriy Gapon  * Note that the values are important.
85155514eaSAndriy Gapon  * They match actual register offsets.
86155514eaSAndriy Gapon  */
87155514eaSAndriy Gapon typedef enum {
88155514eaSAndriy Gapon 	REG_IOR = 0,
89155514eaSAndriy Gapon 	REG_DAT = 1,
90155514eaSAndriy Gapon 	REG_INV = 2,
91155514eaSAndriy Gapon } reg_t;
92155514eaSAndriy Gapon 
937f8d2ed0SStéphane Rochoy struct nct_gpio_group {
947f8d2ed0SStéphane Rochoy  	uint32_t    caps;
957f8d2ed0SStéphane Rochoy 	uint8_t     enable_ldn;
967f8d2ed0SStéphane Rochoy 	uint8_t     enable_reg;
977f8d2ed0SStéphane Rochoy 	uint8_t     enable_mask;
987f8d2ed0SStéphane Rochoy 	uint8_t     data_ldn;
997f8d2ed0SStéphane Rochoy 	uint8_t     iobase;
1007f8d2ed0SStéphane Rochoy 	uint8_t     ppod_reg; /* Push-Pull/Open-Drain */
1017f8d2ed0SStéphane Rochoy 	uint8_t     grpnum;
1027f8d2ed0SStéphane Rochoy 	uint8_t     pinbits[8];
1037f8d2ed0SStéphane Rochoy 	uint8_t     npins;
1047f8d2ed0SStéphane Rochoy };
1057f8d2ed0SStéphane Rochoy 
1066b7b2d80SAdrian Chadd struct nct_softc {
1076b7b2d80SAdrian Chadd 	device_t			dev;
1086b7b2d80SAdrian Chadd 	device_t			busdev;
1096b7b2d80SAdrian Chadd 	struct mtx			mtx;
110155514eaSAndriy Gapon 	struct resource			*iores;
111155514eaSAndriy Gapon 	int				iorid;
112155514eaSAndriy Gapon 	int				curgrp;
113155514eaSAndriy Gapon 	struct {
1147f8d2ed0SStéphane Rochoy 		uint8_t ior[NCT_MAX_GROUP + 1];       /* direction, 1: input 0: output */
1157f8d2ed0SStéphane Rochoy 		uint8_t out[NCT_MAX_GROUP + 1];       /* output value */
1167f8d2ed0SStéphane Rochoy 		uint8_t out_known[NCT_MAX_GROUP + 1]; /* whether out is valid */
1177f8d2ed0SStéphane Rochoy 		uint8_t inv[NCT_MAX_GROUP + 1];       /* inversion, 1: inverted */
118155514eaSAndriy Gapon 	} cache;
119523af57eSConrad Meyer 	struct gpio_pin				pins[NCT_MAX_PIN + 1];
1207f8d2ed0SStéphane Rochoy 	struct nct_device			*nctdevp;
1217f8d2ed0SStéphane Rochoy 	int							npins; /* Total number of pins */
1227f8d2ed0SStéphane Rochoy 
1237f8d2ed0SStéphane Rochoy 	/* Lookup tables */
1247f8d2ed0SStéphane Rochoy 	struct {
1257f8d2ed0SStéphane Rochoy 		struct nct_gpio_group *group;
1267f8d2ed0SStéphane Rochoy 		uint8_t                grpnum;
1277f8d2ed0SStéphane Rochoy 		uint8_t                bit;
1287f8d2ed0SStéphane Rochoy 	} pinmap[NCT_MAX_PIN+1];
1297f8d2ed0SStéphane Rochoy 	struct nct_gpio_group *grpmap[NCT_MAX_GROUP+1];
1306b7b2d80SAdrian Chadd };
1316b7b2d80SAdrian Chadd 
1326b7b2d80SAdrian Chadd #define GPIO_LOCK_INIT(_sc)	mtx_init(&(_sc)->mtx,		\
1336b7b2d80SAdrian Chadd 		device_get_nameunit(dev), NULL, MTX_DEF)
1346b7b2d80SAdrian Chadd #define GPIO_LOCK_DESTROY(_sc)		mtx_destroy(&(_sc)->mtx)
1356b7b2d80SAdrian Chadd #define GPIO_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
1366b7b2d80SAdrian Chadd #define GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->mtx)
1376b7b2d80SAdrian Chadd #define GPIO_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_OWNED)
1386b7b2d80SAdrian Chadd #define GPIO_ASSERT_UNLOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_NOTOWNED)
1396b7b2d80SAdrian Chadd 
1407f8d2ed0SStéphane Rochoy #define GET_BIT(v, b)	(((v) >> (b)) & 1)
1417f8d2ed0SStéphane Rochoy 
1427f8d2ed0SStéphane Rochoy /*
1437f8d2ed0SStéphane Rochoy  * For most devices there are several GPIO devices, we attach only to one of
1447f8d2ed0SStéphane Rochoy  * them and use the rest without attaching.
1457f8d2ed0SStéphane Rochoy  */
1467f8d2ed0SStéphane Rochoy struct nct_device {
1477f8d2ed0SStéphane Rochoy 	uint16_t                  devid;
1487f8d2ed0SStéphane Rochoy 	int                       extid;
1496b7b2d80SAdrian Chadd 	const char               *descr;
1507f8d2ed0SStéphane Rochoy 	int                       ngroups;
1517f8d2ed0SStéphane Rochoy 	struct nct_gpio_group     groups[NCT_MAX_GROUP + 1];
1527f8d2ed0SStéphane Rochoy } nct_devices[] = {
1536b7b2d80SAdrian Chadd 	{
154*8e6ea10cSStéphane Rochoy 		.devid   = 0xa025,
155*8e6ea10cSStéphane Rochoy 		.descr   = "GPIO on Winbond 83627DHG IC ver. 5",
156*8e6ea10cSStéphane Rochoy 		.ngroups = 5,
157*8e6ea10cSStéphane Rochoy 		.groups  = {
158*8e6ea10cSStéphane Rochoy 			{
159*8e6ea10cSStéphane Rochoy 				.grpnum      = 2,
160*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
161*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
162*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
163*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
164*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
165*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe0, /* FIXME Need to check for this group. */
166*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
167*8e6ea10cSStéphane Rochoy 				.npins       = 8,
168*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe3,
169*8e6ea10cSStéphane Rochoy 			},
170*8e6ea10cSStéphane Rochoy 			{
171*8e6ea10cSStéphane Rochoy 				.grpnum      = 3,
172*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
173*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
174*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
175*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
176*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
177*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
178*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
179*8e6ea10cSStéphane Rochoy 				.npins       = 8,
180*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf0,
181*8e6ea10cSStéphane Rochoy 			},
182*8e6ea10cSStéphane Rochoy 			{
183*8e6ea10cSStéphane Rochoy 				.grpnum      = 4,
184*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
185*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
186*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
187*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x04,
188*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
189*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
190*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
191*8e6ea10cSStéphane Rochoy 				.npins       = 8,
192*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
193*8e6ea10cSStéphane Rochoy 			},
194*8e6ea10cSStéphane Rochoy 			{
195*8e6ea10cSStéphane Rochoy 				.grpnum      = 5,
196*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
197*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
198*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
199*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x08,
200*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
201*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
202*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
203*8e6ea10cSStéphane Rochoy 				.npins       = 8,
204*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
205*8e6ea10cSStéphane Rochoy 			},
206*8e6ea10cSStéphane Rochoy 			{
207*8e6ea10cSStéphane Rochoy 				.grpnum      = 6,
208*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
209*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
210*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
211*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
212*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
213*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
214*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
215*8e6ea10cSStéphane Rochoy 				.npins       = 8,
216*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
217*8e6ea10cSStéphane Rochoy 			},
218*8e6ea10cSStéphane Rochoy 		},
219*8e6ea10cSStéphane Rochoy 	},
220*8e6ea10cSStéphane Rochoy 	{
2217f8d2ed0SStéphane Rochoy 		.devid   = 0x1061,
2227f8d2ed0SStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT5104D",
2237f8d2ed0SStéphane Rochoy 		.ngroups = 2,
2247f8d2ed0SStéphane Rochoy 		.groups  = {
2257f8d2ed0SStéphane Rochoy 			{
2267f8d2ed0SStéphane Rochoy 				.grpnum      = 0,
2277f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
2287f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
2297f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
2307f8d2ed0SStéphane Rochoy 				.enable_mask = 0x01,
2317f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
2327f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe0,
2337f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
2347f8d2ed0SStéphane Rochoy 				.npins       = 8,
2357f8d2ed0SStéphane Rochoy 				.iobase      = 0xe0,
2366b7b2d80SAdrian Chadd 			},
2376b7b2d80SAdrian Chadd 			{
2387f8d2ed0SStéphane Rochoy 				.grpnum      = 1,
2397f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
2407f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
2417f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
2427f8d2ed0SStéphane Rochoy 				.enable_mask = 0x02,
2437f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
2447f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe1,
2457f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
2467f8d2ed0SStéphane Rochoy 				.npins       = 8,
2477f8d2ed0SStéphane Rochoy 				.iobase      = 0xe4,
2487f8d2ed0SStéphane Rochoy 			},
2497f8d2ed0SStéphane Rochoy 		},
2506b7b2d80SAdrian Chadd 	},
2519d1208bbSOleksandr Tymoshenko 	{
252*8e6ea10cSStéphane Rochoy 		.devid   = 0xc452, /* FIXME Conflict with Nuvoton NCT6106D. See NetBSD's nct_match. */
2537f8d2ed0SStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT5104D (PC-Engines APU)",
2547f8d2ed0SStéphane Rochoy 		.ngroups = 2,
2557f8d2ed0SStéphane Rochoy 		.groups  = {
2567f8d2ed0SStéphane Rochoy 			{
2577f8d2ed0SStéphane Rochoy 				.grpnum      = 0,
2587f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
2597f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
2607f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
2617f8d2ed0SStéphane Rochoy 				.enable_mask = 0x01,
2627f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
2637f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe0,
2647f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
2657f8d2ed0SStéphane Rochoy 				.npins       = 8,
2667f8d2ed0SStéphane Rochoy 				.iobase      = 0xe0,
2677f8d2ed0SStéphane Rochoy 			},
2687f8d2ed0SStéphane Rochoy 			{
2697f8d2ed0SStéphane Rochoy 				.grpnum      = 1,
2707f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
2717f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
2727f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
2737f8d2ed0SStéphane Rochoy 				.enable_mask = 0x02,
2747f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
2757f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe1,
2767f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
2777f8d2ed0SStéphane Rochoy 				.npins       = 8,
2787f8d2ed0SStéphane Rochoy 				.iobase      = 0xe4,
2797f8d2ed0SStéphane Rochoy 			},
2807f8d2ed0SStéphane Rochoy 		},
2817f8d2ed0SStéphane Rochoy 	},
2827f8d2ed0SStéphane Rochoy 	{
2837f8d2ed0SStéphane Rochoy 		.devid   = 0xc453,
2847f8d2ed0SStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT5104D (PC-Engines APU3)",
2857f8d2ed0SStéphane Rochoy 		.ngroups = 2,
2867f8d2ed0SStéphane Rochoy 		.groups  = {
2877f8d2ed0SStéphane Rochoy 			{
2887f8d2ed0SStéphane Rochoy 				.grpnum      = 0,
2897f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
2907f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
2917f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
2927f8d2ed0SStéphane Rochoy 				.enable_mask = 0x01,
2937f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
2947f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe0,
2957f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
2967f8d2ed0SStéphane Rochoy 				.npins       = 8,
2977f8d2ed0SStéphane Rochoy 				.iobase      = 0xe0,
2987f8d2ed0SStéphane Rochoy 			},
2997f8d2ed0SStéphane Rochoy 			{
3007f8d2ed0SStéphane Rochoy 				.grpnum      = 1,
3017f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
3027f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
3037f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
3047f8d2ed0SStéphane Rochoy 				.enable_mask = 0x02,
3057f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
3067f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe1,
3077f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
3087f8d2ed0SStéphane Rochoy 				.npins       = 8,
3097f8d2ed0SStéphane Rochoy 				.iobase      = 0xe4,
3107f8d2ed0SStéphane Rochoy 			},
3117f8d2ed0SStéphane Rochoy 		},
3129d1208bbSOleksandr Tymoshenko 	},
313*8e6ea10cSStéphane Rochoy 	{
314*8e6ea10cSStéphane Rochoy 		.devid  = 0xd42a,
315*8e6ea10cSStéphane Rochoy 		.extid  = 1,
316*8e6ea10cSStéphane Rochoy 		.descr  = "GPIO on Nuvoton NCT6796D-E",
317*8e6ea10cSStéphane Rochoy 		.ngroups = 10,
318*8e6ea10cSStéphane Rochoy 		.groups  = {
319*8e6ea10cSStéphane Rochoy 			{
320*8e6ea10cSStéphane Rochoy 				.grpnum      = 0,
321*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
322*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x08,
323*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
324*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
325*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x08,
326*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe0, /* FIXME Need to check for this group. */
327*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
328*8e6ea10cSStéphane Rochoy 				.npins       = 8,
329*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
330*8e6ea10cSStéphane Rochoy 			},
331*8e6ea10cSStéphane Rochoy 			{
332*8e6ea10cSStéphane Rochoy 				.grpnum      = 1,
333*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
334*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x08,
335*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
336*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x80,
337*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x08,
338*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
339*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
340*8e6ea10cSStéphane Rochoy 				.npins       = 8,
341*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf0,
342*8e6ea10cSStéphane Rochoy 			},
343*8e6ea10cSStéphane Rochoy 			{
344*8e6ea10cSStéphane Rochoy 				.grpnum      = 2,
345*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
346*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
347*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
348*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
349*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
350*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
351*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
352*8e6ea10cSStéphane Rochoy 				.npins       = 8,
353*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
354*8e6ea10cSStéphane Rochoy 			},
355*8e6ea10cSStéphane Rochoy 			{
356*8e6ea10cSStéphane Rochoy 				.grpnum      = 3,
357*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6 },
358*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
359*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
360*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
361*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
362*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
363*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
364*8e6ea10cSStéphane Rochoy 				.npins       = 7,
365*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe4,
366*8e6ea10cSStéphane Rochoy 			},
367*8e6ea10cSStéphane Rochoy 			{
368*8e6ea10cSStéphane Rochoy 				.grpnum      = 4,
369*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
370*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
371*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
372*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x04,
373*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
374*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
375*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
376*8e6ea10cSStéphane Rochoy 				.npins       = 8,
377*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf0, /* FIXME Page 344 say "F0~F2, E8",
378*8e6ea10cSStéphane Rochoy 										not "F0~F3". */
379*8e6ea10cSStéphane Rochoy 			},
380*8e6ea10cSStéphane Rochoy 			{
381*8e6ea10cSStéphane Rochoy 				.grpnum      = 5,
382*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
383*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
384*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
385*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x08,
386*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
387*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
388*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
389*8e6ea10cSStéphane Rochoy 				.npins       = 8,
390*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
391*8e6ea10cSStéphane Rochoy 			},
392*8e6ea10cSStéphane Rochoy 			{
393*8e6ea10cSStéphane Rochoy 				.grpnum      = 6,
394*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
395*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
396*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
397*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
398*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
399*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
400*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
401*8e6ea10cSStéphane Rochoy 				.npins       = 8,
402*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
403*8e6ea10cSStéphane Rochoy 			},
404*8e6ea10cSStéphane Rochoy 			{
405*8e6ea10cSStéphane Rochoy 				.grpnum      = 7,
406*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
407*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
408*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
409*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
410*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
411*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
412*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
413*8e6ea10cSStéphane Rochoy 				.npins       = 8,
414*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
415*8e6ea10cSStéphane Rochoy 			},
416*8e6ea10cSStéphane Rochoy 			{
417*8e6ea10cSStéphane Rochoy 				.grpnum      = 8,
418*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
419*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
420*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
421*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x04,
422*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
423*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
424*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
425*8e6ea10cSStéphane Rochoy 				.npins       = 8,
426*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe4,
427*8e6ea10cSStéphane Rochoy 			},
428*8e6ea10cSStéphane Rochoy 			{
429*8e6ea10cSStéphane Rochoy 				.grpnum      = 9,
430*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3 },
431*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
432*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
433*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x08,
434*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
435*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
436*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
437*8e6ea10cSStéphane Rochoy 				.npins       = 4,
438*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe8,
439*8e6ea10cSStéphane Rochoy 			},
440*8e6ea10cSStéphane Rochoy 		},
441*8e6ea10cSStéphane Rochoy 	},
442*8e6ea10cSStéphane Rochoy 	{
443*8e6ea10cSStéphane Rochoy 		.devid   = 0xd42a,
444*8e6ea10cSStéphane Rochoy 		.extid   = 2,
445*8e6ea10cSStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT5585D",
446*8e6ea10cSStéphane Rochoy 		.ngroups = 6,
447*8e6ea10cSStéphane Rochoy 		.groups  = {
448*8e6ea10cSStéphane Rochoy 			{
449*8e6ea10cSStéphane Rochoy 				.grpnum      = 2,
450*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6 },
451*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
452*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
453*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
454*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
455*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1,
456*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
457*8e6ea10cSStéphane Rochoy 				.npins       = 7,
458*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
459*8e6ea10cSStéphane Rochoy 			},
460*8e6ea10cSStéphane Rochoy 			{
461*8e6ea10cSStéphane Rochoy 				.grpnum      = 3,
462*8e6ea10cSStéphane Rochoy 				.pinbits     = { 1, 2, 3 },
463*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
464*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
465*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
466*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
467*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe2,
468*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
469*8e6ea10cSStéphane Rochoy 				.npins       = 3,
470*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe4,
471*8e6ea10cSStéphane Rochoy 			},
472*8e6ea10cSStéphane Rochoy 			{
473*8e6ea10cSStéphane Rochoy 				.grpnum      = 5,
474*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 2, 6, 7 },
475*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
476*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
477*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x08,
478*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
479*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe4,
480*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
481*8e6ea10cSStéphane Rochoy 				.npins       = 4,
482*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
483*8e6ea10cSStéphane Rochoy 			},
484*8e6ea10cSStéphane Rochoy 			{
485*8e6ea10cSStéphane Rochoy 				.grpnum      = 7,
486*8e6ea10cSStéphane Rochoy 				.pinbits     = { 4 },
487*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
488*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
489*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
490*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
491*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe6,
492*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
493*8e6ea10cSStéphane Rochoy 				.npins       = 1,
494*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
495*8e6ea10cSStéphane Rochoy 			},
496*8e6ea10cSStéphane Rochoy 			{
497*8e6ea10cSStéphane Rochoy 				.grpnum      = 8,
498*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
499*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
500*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
501*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x04,
502*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
503*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe7,
504*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
505*8e6ea10cSStéphane Rochoy 				.npins       = 8,
506*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe4,
507*8e6ea10cSStéphane Rochoy 			},
508*8e6ea10cSStéphane Rochoy 			{
509*8e6ea10cSStéphane Rochoy 				.grpnum      = 9,
510*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 2 },
511*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
512*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
513*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x08,
514*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
515*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xea,
516*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
517*8e6ea10cSStéphane Rochoy 				.npins       = 2,
518*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe8,
519*8e6ea10cSStéphane Rochoy 			},
520*8e6ea10cSStéphane Rochoy 		},
521*8e6ea10cSStéphane Rochoy 	},
522*8e6ea10cSStéphane Rochoy 	{
523*8e6ea10cSStéphane Rochoy 		.devid   = 0xc562,
524*8e6ea10cSStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT6779D",
525*8e6ea10cSStéphane Rochoy 		.ngroups = 9,
526*8e6ea10cSStéphane Rochoy 		.groups  = {
527*8e6ea10cSStéphane Rochoy 			{
528*8e6ea10cSStéphane Rochoy 				.grpnum      = 0,
529*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
530*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x08,
531*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
532*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
533*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x08,
534*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe0, /* FIXME Need to check for this group. */
535*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
536*8e6ea10cSStéphane Rochoy 				.npins       = 8,
537*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
538*8e6ea10cSStéphane Rochoy 			},
539*8e6ea10cSStéphane Rochoy 			{
540*8e6ea10cSStéphane Rochoy 				.grpnum      = 1,
541*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
542*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
543*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
544*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
545*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x08,
546*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
547*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
548*8e6ea10cSStéphane Rochoy 				.npins       = 8,
549*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf0,
550*8e6ea10cSStéphane Rochoy 			},
551*8e6ea10cSStéphane Rochoy 			{
552*8e6ea10cSStéphane Rochoy 				.grpnum      = 2,
553*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
554*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
555*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
556*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
557*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
558*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
559*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
560*8e6ea10cSStéphane Rochoy 				.npins       = 8,
561*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
562*8e6ea10cSStéphane Rochoy 			},
563*8e6ea10cSStéphane Rochoy 			{
564*8e6ea10cSStéphane Rochoy 				.grpnum      = 3,
565*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6 },
566*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
567*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
568*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
569*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
570*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
571*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
572*8e6ea10cSStéphane Rochoy 				.npins       = 7,
573*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe4,
574*8e6ea10cSStéphane Rochoy 			},
575*8e6ea10cSStéphane Rochoy 			{
576*8e6ea10cSStéphane Rochoy 				.grpnum      = 4,
577*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
578*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
579*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
580*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x04,
581*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
582*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
583*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
584*8e6ea10cSStéphane Rochoy 				.npins       = 8,
585*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf0,
586*8e6ea10cSStéphane Rochoy 			},
587*8e6ea10cSStéphane Rochoy 			{
588*8e6ea10cSStéphane Rochoy 				.grpnum      = 5,
589*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
590*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
591*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
592*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x08,
593*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
594*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
595*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
596*8e6ea10cSStéphane Rochoy 				.npins       = 8,
597*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
598*8e6ea10cSStéphane Rochoy 			},
599*8e6ea10cSStéphane Rochoy 			{
600*8e6ea10cSStéphane Rochoy 				.grpnum      = 6,
601*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
602*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
603*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
604*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
605*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
606*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
607*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
608*8e6ea10cSStéphane Rochoy 				.npins       = 8,
609*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
610*8e6ea10cSStéphane Rochoy 			},
611*8e6ea10cSStéphane Rochoy 			{
612*8e6ea10cSStéphane Rochoy 				.grpnum      = 7,
613*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6 },
614*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
615*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
616*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
617*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
618*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
619*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
620*8e6ea10cSStéphane Rochoy 				.npins       = 7,
621*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
622*8e6ea10cSStéphane Rochoy 			},
623*8e6ea10cSStéphane Rochoy 			{
624*8e6ea10cSStéphane Rochoy 				.grpnum      = 8,
625*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
626*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
627*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
628*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x04,
629*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
630*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
631*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
632*8e6ea10cSStéphane Rochoy 				.npins       = 8,
633*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe4,
634*8e6ea10cSStéphane Rochoy 			},
635*8e6ea10cSStéphane Rochoy 		},
636*8e6ea10cSStéphane Rochoy 	},
637*8e6ea10cSStéphane Rochoy 	{
638*8e6ea10cSStéphane Rochoy 		.devid   = 0xd282,
639*8e6ea10cSStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT6112D/NCT6114D/NCT6116D",
640*8e6ea10cSStéphane Rochoy 		.ngroups = 2,
641*8e6ea10cSStéphane Rochoy 		.groups  = {
642*8e6ea10cSStéphane Rochoy 			{
643*8e6ea10cSStéphane Rochoy 				.grpnum      = 0,
644*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
645*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
646*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
647*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
648*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
649*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe0, /* FIXME Need to check for this group. */
650*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
651*8e6ea10cSStéphane Rochoy 				.npins       = 8,
652*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe0,
653*8e6ea10cSStéphane Rochoy 			},
654*8e6ea10cSStéphane Rochoy 			{
655*8e6ea10cSStéphane Rochoy 				.grpnum      = 1,
656*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
657*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
658*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
659*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x02,
660*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
661*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
662*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
663*8e6ea10cSStéphane Rochoy 				.npins       = 8,
664*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe4,
665*8e6ea10cSStéphane Rochoy 			},
666*8e6ea10cSStéphane Rochoy 			{
667*8e6ea10cSStéphane Rochoy 				.grpnum      = 2,
668*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
669*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
670*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
671*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x04,
672*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
673*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
674*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
675*8e6ea10cSStéphane Rochoy 				.npins       = 8,
676*8e6ea10cSStéphane Rochoy 				.iobase      = 0xe8,
677*8e6ea10cSStéphane Rochoy 			},
678*8e6ea10cSStéphane Rochoy 			{
679*8e6ea10cSStéphane Rochoy 				.grpnum      = 3,
680*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
681*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
682*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
683*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x08,
684*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
685*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
686*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
687*8e6ea10cSStéphane Rochoy 				.npins       = 8,
688*8e6ea10cSStéphane Rochoy 				.iobase      = 0xec,
689*8e6ea10cSStéphane Rochoy 			},
690*8e6ea10cSStéphane Rochoy 			{
691*8e6ea10cSStéphane Rochoy 				.grpnum      = 4,
692*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
693*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
694*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
695*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x10,
696*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
697*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
698*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
699*8e6ea10cSStéphane Rochoy 				.npins       = 8,
700*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf0,
701*8e6ea10cSStéphane Rochoy 			},
702*8e6ea10cSStéphane Rochoy 			{
703*8e6ea10cSStéphane Rochoy 				.grpnum      = 5,
704*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
705*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
706*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
707*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x20,
708*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
709*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
710*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
711*8e6ea10cSStéphane Rochoy 				.npins       = 8,
712*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf4,
713*8e6ea10cSStéphane Rochoy 			},
714*8e6ea10cSStéphane Rochoy 			{
715*8e6ea10cSStéphane Rochoy 				.grpnum      = 6,
716*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
717*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
718*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
719*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x40,
720*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
721*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
722*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
723*8e6ea10cSStéphane Rochoy 				.npins       = 8,
724*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf8,
725*8e6ea10cSStéphane Rochoy 			},
726*8e6ea10cSStéphane Rochoy 			{
727*8e6ea10cSStéphane Rochoy 				.grpnum      = 7,
728*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
729*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x07,
730*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
731*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x80,
732*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x07,
733*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
734*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
735*8e6ea10cSStéphane Rochoy 				.npins       = 8,
736*8e6ea10cSStéphane Rochoy 				.iobase      = 0xfc,
737*8e6ea10cSStéphane Rochoy 			},
738*8e6ea10cSStéphane Rochoy 			{
739*8e6ea10cSStéphane Rochoy 				.grpnum      = 8,
740*8e6ea10cSStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
741*8e6ea10cSStéphane Rochoy 				.enable_ldn  = 0x09,
742*8e6ea10cSStéphane Rochoy 				.enable_reg  = 0x30,
743*8e6ea10cSStéphane Rochoy 				.enable_mask = 0x01,
744*8e6ea10cSStéphane Rochoy 				.data_ldn    = 0x09,
745*8e6ea10cSStéphane Rochoy 				.ppod_reg    = 0xe1, /* FIXME Need to check for this group. */
746*8e6ea10cSStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
747*8e6ea10cSStéphane Rochoy 				.npins       = 8,
748*8e6ea10cSStéphane Rochoy 				.iobase      = 0xf0,
749*8e6ea10cSStéphane Rochoy 			},
750*8e6ea10cSStéphane Rochoy 		},
751*8e6ea10cSStéphane Rochoy 	},
7526b7b2d80SAdrian Chadd };
7536b7b2d80SAdrian Chadd 
7547f8d2ed0SStéphane Rochoy static const char *
7557f8d2ed0SStéphane Rochoy io2str(uint8_t ioport)
7566b7b2d80SAdrian Chadd {
7577f8d2ed0SStéphane Rochoy 	switch (ioport) {
7587f8d2ed0SStéphane Rochoy 	case NCT_IO_GSR: return ("grpsel");
7597f8d2ed0SStéphane Rochoy 	case NCT_IO_IOR: return ("io");
7607f8d2ed0SStéphane Rochoy 	case NCT_IO_DAT: return ("data");
7617f8d2ed0SStéphane Rochoy 	case NCT_IO_INV: return ("inv");
7627f8d2ed0SStéphane Rochoy 	case NCT_IO_DST: return ("status");
7637f8d2ed0SStéphane Rochoy 	default:         return ("?");
7647f8d2ed0SStéphane Rochoy 	}
7657f8d2ed0SStéphane Rochoy }
7666b7b2d80SAdrian Chadd 
7677f8d2ed0SStéphane Rochoy static void
7687f8d2ed0SStéphane Rochoy nct_io_set_group(struct nct_softc *sc, uint8_t grpnum)
7697f8d2ed0SStéphane Rochoy {
770155514eaSAndriy Gapon 	GPIO_ASSERT_LOCKED(sc);
7717f8d2ed0SStéphane Rochoy 
7727f8d2ed0SStéphane Rochoy 	if (grpnum == sc->curgrp)
7737f8d2ed0SStéphane Rochoy 		return;
7747f8d2ed0SStéphane Rochoy 
7757f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x ioport %d\n",
7767f8d2ed0SStéphane Rochoy 		io2str(NCT_IO_GSR), grpnum, NCT_IO_GSR);
7777f8d2ed0SStéphane Rochoy 	bus_write_1(sc->iores, NCT_IO_GSR, grpnum);
7787f8d2ed0SStéphane Rochoy 	sc->curgrp = grpnum;
779155514eaSAndriy Gapon }
7806b7b2d80SAdrian Chadd 
781155514eaSAndriy Gapon static uint8_t
7827f8d2ed0SStéphane Rochoy nct_io_read(struct nct_softc *sc, uint8_t grpnum, uint8_t reg)
783155514eaSAndriy Gapon {
784155514eaSAndriy Gapon 	uint8_t val;
785155514eaSAndriy Gapon 
7867f8d2ed0SStéphane Rochoy 	nct_io_set_group(sc, grpnum);
787155514eaSAndriy Gapon 
7887f8d2ed0SStéphane Rochoy 	val = bus_read_1(sc->iores, reg);
7897f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "read %s 0x%x ioport %d\n",
7907f8d2ed0SStéphane Rochoy 		io2str(reg), val, reg);
791155514eaSAndriy Gapon 	return (val);
792155514eaSAndriy Gapon }
793155514eaSAndriy Gapon 
7947f8d2ed0SStéphane Rochoy static void
7957f8d2ed0SStéphane Rochoy nct_io_write(struct nct_softc *sc, uint8_t grpnum, uint8_t reg, uint8_t val)
796155514eaSAndriy Gapon {
7977f8d2ed0SStéphane Rochoy 	nct_io_set_group(sc, grpnum);
7987f8d2ed0SStéphane Rochoy 
7997f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x ioport %d\n",
8007f8d2ed0SStéphane Rochoy 		io2str(reg), val, reg);
8017f8d2ed0SStéphane Rochoy 	bus_write_1(sc->iores, reg, val);
8027f8d2ed0SStéphane Rochoy }
8037f8d2ed0SStéphane Rochoy 
8047f8d2ed0SStéphane Rochoy static uint8_t
8057f8d2ed0SStéphane Rochoy nct_get_ioreg(struct nct_softc *sc, reg_t reg, uint8_t grpnum)
8067f8d2ed0SStéphane Rochoy {
8077f8d2ed0SStéphane Rochoy 	uint8_t iobase;
8087f8d2ed0SStéphane Rochoy 
8097f8d2ed0SStéphane Rochoy 	if (sc->iores != NULL)
8107f8d2ed0SStéphane Rochoy 		iobase = NCT_IO_IOR;
8117f8d2ed0SStéphane Rochoy 	else
8127f8d2ed0SStéphane Rochoy 		iobase = sc->grpmap[grpnum]->iobase;
8137f8d2ed0SStéphane Rochoy 	return (iobase + reg);
8147f8d2ed0SStéphane Rochoy }
8157f8d2ed0SStéphane Rochoy 
8167f8d2ed0SStéphane Rochoy static const char *
8177f8d2ed0SStéphane Rochoy reg2str(reg_t reg)
8187f8d2ed0SStéphane Rochoy {
8197f8d2ed0SStéphane Rochoy 	switch (reg) {
8207f8d2ed0SStéphane Rochoy 	case REG_IOR: return ("io");
8217f8d2ed0SStéphane Rochoy 	case REG_DAT: return ("data");
8227f8d2ed0SStéphane Rochoy 	case REG_INV: return ("inv");
8237f8d2ed0SStéphane Rochoy 	default:      return ("?");
8247f8d2ed0SStéphane Rochoy 	}
8257f8d2ed0SStéphane Rochoy }
8267f8d2ed0SStéphane Rochoy 
8277f8d2ed0SStéphane Rochoy static uint8_t
8287f8d2ed0SStéphane Rochoy nct_read_reg(struct nct_softc *sc, reg_t reg, uint8_t grpnum)
8297f8d2ed0SStéphane Rochoy {
8307f8d2ed0SStéphane Rochoy 	struct nct_gpio_group *gp;
8317f8d2ed0SStéphane Rochoy 	uint8_t                ioreg;
832155514eaSAndriy Gapon 	uint8_t                val;
833155514eaSAndriy Gapon 
8347f8d2ed0SStéphane Rochoy 	ioreg = nct_get_ioreg(sc, reg, grpnum);
835155514eaSAndriy Gapon 
8367f8d2ed0SStéphane Rochoy 	if (sc->iores != NULL)
8377f8d2ed0SStéphane Rochoy 		return (nct_io_read(sc, grpnum, ioreg));
8387f8d2ed0SStéphane Rochoy 
8397f8d2ed0SStéphane Rochoy 	gp  = sc->grpmap[grpnum];
8407f8d2ed0SStéphane Rochoy 	val = superio_ldn_read(sc->dev, gp->data_ldn, ioreg);
8417f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "read %s 0x%x from group GPIO%u ioreg 0x%x\n",
8427f8d2ed0SStéphane Rochoy 		reg2str(reg), val, grpnum, ioreg);
8437f8d2ed0SStéphane Rochoy 	return (val);
844155514eaSAndriy Gapon }
845155514eaSAndriy Gapon 
846155514eaSAndriy Gapon static int
847155514eaSAndriy Gapon nct_get_pin_cache(struct nct_softc *sc, uint32_t pin_num, uint8_t *cache)
848155514eaSAndriy Gapon {
849155514eaSAndriy Gapon 	uint8_t bit;
850155514eaSAndriy Gapon 	uint8_t group;
851155514eaSAndriy Gapon 	uint8_t val;
852155514eaSAndriy Gapon 
8537f8d2ed0SStéphane Rochoy 	KASSERT(NCT_PIN_IS_VALID(sc, pin_num), ("%s: invalid pin number %d",
854155514eaSAndriy Gapon 	    __func__, pin_num));
855155514eaSAndriy Gapon 
8567f8d2ed0SStéphane Rochoy 	group = NCT_PIN_GRPNUM(sc, pin_num);
8577f8d2ed0SStéphane Rochoy 	bit   = NCT_PIN_BIT(sc, pin_num);
858155514eaSAndriy Gapon 	val   = cache[group];
859155514eaSAndriy Gapon 	return (GET_BIT(val, bit));
860155514eaSAndriy Gapon }
861155514eaSAndriy Gapon 
862155514eaSAndriy Gapon static void
8637f8d2ed0SStéphane Rochoy nct_write_reg(struct nct_softc *sc, reg_t reg, uint8_t grpnum, uint8_t val)
864155514eaSAndriy Gapon {
8657f8d2ed0SStéphane Rochoy 	struct nct_gpio_group *gp;
866155514eaSAndriy Gapon 	uint8_t                ioreg;
867155514eaSAndriy Gapon 
8687f8d2ed0SStéphane Rochoy 	ioreg = nct_get_ioreg(sc, reg, grpnum);
8697f8d2ed0SStéphane Rochoy 
8707f8d2ed0SStéphane Rochoy 	if (sc->iores != NULL) {
8717f8d2ed0SStéphane Rochoy 		nct_io_write(sc, grpnum, ioreg, val);
8727f8d2ed0SStéphane Rochoy 		return;
8737f8d2ed0SStéphane Rochoy 	}
8747f8d2ed0SStéphane Rochoy 
8757f8d2ed0SStéphane Rochoy 	gp = sc->grpmap[grpnum];
8767f8d2ed0SStéphane Rochoy 	superio_ldn_write(sc->dev, gp->data_ldn, ioreg, val);
8777f8d2ed0SStéphane Rochoy 
8787f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x to group GPIO%u ioreg 0x%x\n",
8797f8d2ed0SStéphane Rochoy 		reg2str(reg), val, grpnum, ioreg);
880155514eaSAndriy Gapon }
881155514eaSAndriy Gapon 
882155514eaSAndriy Gapon static void
883155514eaSAndriy Gapon nct_set_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num, bool val)
884155514eaSAndriy Gapon {
885155514eaSAndriy Gapon 	uint8_t *cache;
886155514eaSAndriy Gapon 	uint8_t bit;
887155514eaSAndriy Gapon 	uint8_t bitval;
888155514eaSAndriy Gapon 	uint8_t group;
889155514eaSAndriy Gapon 	uint8_t mask;
890155514eaSAndriy Gapon 
8917f8d2ed0SStéphane Rochoy 	KASSERT(NCT_PIN_IS_VALID(sc, pin_num),
892155514eaSAndriy Gapon 	    ("%s: invalid pin number %d", __func__, pin_num));
893155514eaSAndriy Gapon 	KASSERT(reg == REG_IOR || reg == REG_INV,
894155514eaSAndriy Gapon 	    ("%s: unsupported register %d", __func__, reg));
895155514eaSAndriy Gapon 
8967f8d2ed0SStéphane Rochoy 	group  = NCT_PIN_GRPNUM(sc, pin_num);
8977f8d2ed0SStéphane Rochoy 	bit    = NCT_PIN_BIT(sc, pin_num);
898155514eaSAndriy Gapon 	mask   = (uint8_t)1 << bit;
899155514eaSAndriy Gapon 	bitval = (uint8_t)val << bit;
900155514eaSAndriy Gapon 
901155514eaSAndriy Gapon 	if (reg == REG_IOR)
902155514eaSAndriy Gapon 		cache = &sc->cache.ior[group];
903155514eaSAndriy Gapon 	else
904155514eaSAndriy Gapon 		cache = &sc->cache.inv[group];
905155514eaSAndriy Gapon 	if ((*cache & mask) == bitval)
906155514eaSAndriy Gapon 		return;
907155514eaSAndriy Gapon 	*cache &= ~mask;
908155514eaSAndriy Gapon 	*cache |= bitval;
909155514eaSAndriy Gapon 	nct_write_reg(sc, reg, group, *cache);
9106b7b2d80SAdrian Chadd }
9116b7b2d80SAdrian Chadd 
9126b7b2d80SAdrian Chadd /*
913155514eaSAndriy Gapon  * Set a pin to input (val is true) or output (val is false) mode.
9146b7b2d80SAdrian Chadd  */
9156b7b2d80SAdrian Chadd static void
916155514eaSAndriy Gapon nct_set_pin_input(struct nct_softc *sc, uint32_t pin_num, bool val)
9176b7b2d80SAdrian Chadd {
918155514eaSAndriy Gapon 	nct_set_pin_reg(sc, REG_IOR, pin_num, val);
9196b7b2d80SAdrian Chadd }
9206b7b2d80SAdrian Chadd 
9216b7b2d80SAdrian Chadd /*
9226b7b2d80SAdrian Chadd  * Check whether a pin is configured as an input.
9236b7b2d80SAdrian Chadd  */
9246b7b2d80SAdrian Chadd static bool
9256b7b2d80SAdrian Chadd nct_pin_is_input(struct nct_softc *sc, uint32_t pin_num)
9266b7b2d80SAdrian Chadd {
927155514eaSAndriy Gapon 	return (nct_get_pin_cache(sc, pin_num, sc->cache.ior));
9286b7b2d80SAdrian Chadd }
9296b7b2d80SAdrian Chadd 
9306b7b2d80SAdrian Chadd /*
931155514eaSAndriy Gapon  * Set a pin to inverted (val is true) or normal (val is false) mode.
9326b7b2d80SAdrian Chadd  */
9336b7b2d80SAdrian Chadd static void
934155514eaSAndriy Gapon nct_set_pin_inverted(struct nct_softc *sc, uint32_t pin_num, bool val)
9356b7b2d80SAdrian Chadd {
936155514eaSAndriy Gapon 	nct_set_pin_reg(sc, REG_INV, pin_num, val);
9376b7b2d80SAdrian Chadd }
9386b7b2d80SAdrian Chadd 
9396b7b2d80SAdrian Chadd static bool
9406b7b2d80SAdrian Chadd nct_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num)
9416b7b2d80SAdrian Chadd {
942155514eaSAndriy Gapon 	return (nct_get_pin_cache(sc, pin_num, sc->cache.inv));
9436b7b2d80SAdrian Chadd }
9446b7b2d80SAdrian Chadd 
945155514eaSAndriy Gapon /*
946155514eaSAndriy Gapon  * Write a value to an output pin.
947155514eaSAndriy Gapon  * NB: the hardware remembers last output value across switching from
948155514eaSAndriy Gapon  * output mode to input mode and back.
949155514eaSAndriy Gapon  * Writes to a pin in input mode are not allowed here as they cannot
950155514eaSAndriy Gapon  * have any effect and would corrupt the output value cache.
951155514eaSAndriy Gapon  */
952155514eaSAndriy Gapon static void
953155514eaSAndriy Gapon nct_write_pin(struct nct_softc *sc, uint32_t pin_num, bool val)
954155514eaSAndriy Gapon {
955155514eaSAndriy Gapon 	uint8_t bit;
956155514eaSAndriy Gapon 	uint8_t group;
957155514eaSAndriy Gapon 
958155514eaSAndriy Gapon 	KASSERT(!nct_pin_is_input(sc, pin_num), ("attempt to write input pin"));
9597f8d2ed0SStéphane Rochoy 	group = NCT_PIN_GRPNUM(sc, pin_num);
9607f8d2ed0SStéphane Rochoy 	bit   = NCT_PIN_BIT(sc, pin_num);
9617f8d2ed0SStéphane Rochoy 
962155514eaSAndriy Gapon 	if (GET_BIT(sc->cache.out_known[group], bit) &&
963155514eaSAndriy Gapon 	    GET_BIT(sc->cache.out[group], bit) == val) {
964155514eaSAndriy Gapon 		/* The pin is already in requested state. */
965155514eaSAndriy Gapon 		return;
966155514eaSAndriy Gapon 	}
967155514eaSAndriy Gapon 	sc->cache.out_known[group] |= 1 << bit;
968155514eaSAndriy Gapon 	if (val)
969155514eaSAndriy Gapon 		sc->cache.out[group] |= 1 << bit;
970155514eaSAndriy Gapon 	else
971155514eaSAndriy Gapon 		sc->cache.out[group] &= ~(1 << bit);
972155514eaSAndriy Gapon 	nct_write_reg(sc, REG_DAT, group, sc->cache.out[group]);
973155514eaSAndriy Gapon }
974155514eaSAndriy Gapon 
9757f8d2ed0SStéphane Rochoy static bool
9767f8d2ed0SStéphane Rochoy nct_get_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num)
9777f8d2ed0SStéphane Rochoy {
9787f8d2ed0SStéphane Rochoy 	uint8_t            bit;
9797f8d2ed0SStéphane Rochoy 	uint8_t            group;
9807f8d2ed0SStéphane Rochoy 	uint8_t            val;
9817f8d2ed0SStéphane Rochoy 	bool               b;
9827f8d2ed0SStéphane Rochoy 
9837f8d2ed0SStéphane Rochoy 	KASSERT(NCT_PIN_IS_VALID(sc, pin_num), ("%s: invalid pin number %d",
9847f8d2ed0SStéphane Rochoy 			__func__, pin_num));
9857f8d2ed0SStéphane Rochoy 
9867f8d2ed0SStéphane Rochoy 	group = NCT_PIN_GRPNUM(sc, pin_num);
9877f8d2ed0SStéphane Rochoy 	bit   = NCT_PIN_BIT(sc, pin_num);
9887f8d2ed0SStéphane Rochoy 	val   = nct_read_reg(sc, reg, group);
9897f8d2ed0SStéphane Rochoy 	b     = GET_BIT(val, bit);
9907f8d2ed0SStéphane Rochoy 
9917f8d2ed0SStéphane Rochoy 	if (__predict_false(bootverbose)) {
9927f8d2ed0SStéphane Rochoy 		if (nct_pin_is_input(sc, pin_num))
9937f8d2ed0SStéphane Rochoy 			NCT_VERBOSE_PRINTF(sc->dev, "read %d from input pin %u<GPIO%u%u>\n",
9947f8d2ed0SStéphane Rochoy 				b, pin_num, group, bit);
9957f8d2ed0SStéphane Rochoy 		else
9967f8d2ed0SStéphane Rochoy 			NCT_VERBOSE_PRINTF(sc->dev,
9977f8d2ed0SStéphane Rochoy 				"read %d from output pin %u<GPIO%u%u>, cache miss\n",
9987f8d2ed0SStéphane Rochoy 				b, pin_num, group, bit);
9997f8d2ed0SStéphane Rochoy 	}
10007f8d2ed0SStéphane Rochoy 
10017f8d2ed0SStéphane Rochoy 	return (b);
10027f8d2ed0SStéphane Rochoy }
10037f8d2ed0SStéphane Rochoy 
1004155514eaSAndriy Gapon /*
1005155514eaSAndriy Gapon  * NB: state of an input pin cannot be cached, of course.
1006155514eaSAndriy Gapon  * For an output we can either take the value from the cache if it's valid
1007155514eaSAndriy Gapon  * or read the state from the hadrware and cache it.
1008155514eaSAndriy Gapon  */
1009155514eaSAndriy Gapon static bool
1010155514eaSAndriy Gapon nct_read_pin(struct nct_softc *sc, uint32_t pin_num)
1011155514eaSAndriy Gapon {
1012155514eaSAndriy Gapon 	uint8_t bit;
1013155514eaSAndriy Gapon 	uint8_t group;
1014155514eaSAndriy Gapon 	bool    val;
1015155514eaSAndriy Gapon 
10167f8d2ed0SStéphane Rochoy 	if (nct_pin_is_input(sc, pin_num)) {
1017155514eaSAndriy Gapon 		return (nct_get_pin_reg(sc, REG_DAT, pin_num));
10187f8d2ed0SStéphane Rochoy 	}
1019155514eaSAndriy Gapon 
10207f8d2ed0SStéphane Rochoy 	group = NCT_PIN_GRPNUM(sc, pin_num);
10217f8d2ed0SStéphane Rochoy 	bit   = NCT_PIN_BIT(sc, pin_num);
10227f8d2ed0SStéphane Rochoy 
10237f8d2ed0SStéphane Rochoy 	if (GET_BIT(sc->cache.out_known[group], bit)) {
10247f8d2ed0SStéphane Rochoy 		val = GET_BIT(sc->cache.out[group], bit);
10257f8d2ed0SStéphane Rochoy 
10267f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(sc->dev,
10277f8d2ed0SStéphane Rochoy 			"read %d from output pin %u<GPIO%u%u>, cache hit\n",
10287f8d2ed0SStéphane Rochoy 			val, pin_num, group, bit);
10297f8d2ed0SStéphane Rochoy 
10307f8d2ed0SStéphane Rochoy 		return (val);
10317f8d2ed0SStéphane Rochoy 	}
1032155514eaSAndriy Gapon 
1033155514eaSAndriy Gapon 	val = nct_get_pin_reg(sc, REG_DAT, pin_num);
1034155514eaSAndriy Gapon 	sc->cache.out_known[group] |= 1 << bit;
1035155514eaSAndriy Gapon 	if (val)
1036155514eaSAndriy Gapon 		sc->cache.out[group] |= 1 << bit;
1037155514eaSAndriy Gapon 	else
1038155514eaSAndriy Gapon 		sc->cache.out[group] &= ~(1 << bit);
1039155514eaSAndriy Gapon 	return (val);
1040155514eaSAndriy Gapon }
1041155514eaSAndriy Gapon 
1042*8e6ea10cSStéphane Rochoy /* FIXME Incorret for NCT5585D and probably other chips. */
1043155514eaSAndriy Gapon static uint8_t
10447f8d2ed0SStéphane Rochoy nct_ppod_reg(struct nct_softc *sc, uint32_t pin_num)
1045155514eaSAndriy Gapon {
10467f8d2ed0SStéphane Rochoy 	uint8_t group = NCT_PIN_GRPNUM(sc, pin_num);
10477f8d2ed0SStéphane Rochoy 
10487f8d2ed0SStéphane Rochoy 	return (sc->grpmap[group]->ppod_reg);
1049155514eaSAndriy Gapon }
1050155514eaSAndriy Gapon 
1051155514eaSAndriy Gapon /*
1052155514eaSAndriy Gapon  * NB: PP/OD can be configured only via configuration registers.
1053155514eaSAndriy Gapon  * Also, the registers are in a different logical device.
1054155514eaSAndriy Gapon  * So, this is a special case.  No caching too.
1055155514eaSAndriy Gapon  */
10566b7b2d80SAdrian Chadd static void
10576b7b2d80SAdrian Chadd nct_set_pin_opendrain(struct nct_softc *sc, uint32_t pin_num)
10586b7b2d80SAdrian Chadd {
10596b7b2d80SAdrian Chadd 	uint8_t reg;
10606b7b2d80SAdrian Chadd 	uint8_t outcfg;
10616b7b2d80SAdrian Chadd 
10627f8d2ed0SStéphane Rochoy 	reg = nct_ppod_reg(sc, pin_num);
10637f8d2ed0SStéphane Rochoy 	outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg);
10647f8d2ed0SStéphane Rochoy 	outcfg |= NCT_PIN_BITMASK(pin_num);
10657f8d2ed0SStéphane Rochoy 	superio_ldn_write(sc->dev, 0xf, reg, outcfg);
10666b7b2d80SAdrian Chadd }
10676b7b2d80SAdrian Chadd 
10686b7b2d80SAdrian Chadd static void
10696b7b2d80SAdrian Chadd nct_set_pin_pushpull(struct nct_softc *sc, uint32_t pin_num)
10706b7b2d80SAdrian Chadd {
10716b7b2d80SAdrian Chadd 	uint8_t reg;
10726b7b2d80SAdrian Chadd 	uint8_t outcfg;
10736b7b2d80SAdrian Chadd 
10747f8d2ed0SStéphane Rochoy 	reg = nct_ppod_reg(sc, pin_num);
10757f8d2ed0SStéphane Rochoy 	outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg);
10767f8d2ed0SStéphane Rochoy 	outcfg &= ~NCT_PIN_BITMASK(pin_num);
10777f8d2ed0SStéphane Rochoy 	superio_ldn_write(sc->dev, 0xf, reg, outcfg);
10786b7b2d80SAdrian Chadd }
10796b7b2d80SAdrian Chadd 
10806b7b2d80SAdrian Chadd static bool
10816b7b2d80SAdrian Chadd nct_pin_is_opendrain(struct nct_softc *sc, uint32_t pin_num)
10826b7b2d80SAdrian Chadd {
10836b7b2d80SAdrian Chadd 	uint8_t reg;
10846b7b2d80SAdrian Chadd 	uint8_t outcfg;
10856b7b2d80SAdrian Chadd 
10867f8d2ed0SStéphane Rochoy 	reg = nct_ppod_reg(sc, pin_num);
10877f8d2ed0SStéphane Rochoy 	outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg);
10887f8d2ed0SStéphane Rochoy 	return (outcfg & NCT_PIN_BITMASK(pin_num));
10897f8d2ed0SStéphane Rochoy }
10907f8d2ed0SStéphane Rochoy 
10917f8d2ed0SStéphane Rochoy static struct nct_device *
10927f8d2ed0SStéphane Rochoy nct_lookup_device(device_t dev)
10937f8d2ed0SStéphane Rochoy {
10947f8d2ed0SStéphane Rochoy 	struct nct_device *nctdevp;
10957f8d2ed0SStéphane Rochoy 	uint16_t           devid;
10967f8d2ed0SStéphane Rochoy 	int                i, extid;
10977f8d2ed0SStéphane Rochoy 
10987f8d2ed0SStéphane Rochoy 	devid = superio_devid(dev);
10997f8d2ed0SStéphane Rochoy 	extid = superio_extid(dev);
11007f8d2ed0SStéphane Rochoy 	for (i = 0, nctdevp = nct_devices; i < nitems(nct_devices); i++, nctdevp++) {
11017f8d2ed0SStéphane Rochoy 		if (devid == nctdevp->devid && nctdevp->extid == extid)
11027f8d2ed0SStéphane Rochoy 			return (nctdevp);
11037f8d2ed0SStéphane Rochoy 	}
11047f8d2ed0SStéphane Rochoy 	return (NULL);
11056b7b2d80SAdrian Chadd }
11066b7b2d80SAdrian Chadd 
11076b7b2d80SAdrian Chadd static int
11086b7b2d80SAdrian Chadd nct_probe(device_t dev)
11096b7b2d80SAdrian Chadd {
11107f8d2ed0SStéphane Rochoy 	struct nct_device *nctdevp;
11117f8d2ed0SStéphane Rochoy 	uint8_t            ldn;
11126b7b2d80SAdrian Chadd 
11137f8d2ed0SStéphane Rochoy 	ldn = superio_get_ldn(dev);
11146b7b2d80SAdrian Chadd 
11157f8d2ed0SStéphane Rochoy 	if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON) {
11167f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not a Nuvoton device\n", ldn);
1117e3df342aSAndriy Gapon 		return (ENXIO);
11187f8d2ed0SStéphane Rochoy 	}
11197f8d2ed0SStéphane Rochoy 	if (superio_get_type(dev) != SUPERIO_DEV_GPIO) {
11207f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not a GPIO device\n", ldn);
11217f8d2ed0SStéphane Rochoy 		return (ENXIO);
11227f8d2ed0SStéphane Rochoy 	}
11236b7b2d80SAdrian Chadd 
11247f8d2ed0SStéphane Rochoy 	nctdevp = nct_lookup_device(dev);
11257f8d2ed0SStéphane Rochoy 	if (nctdevp == NULL) {
11267f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not supported\n", ldn);
11277f8d2ed0SStéphane Rochoy 		return (ENXIO);
11287f8d2ed0SStéphane Rochoy 	}
11297f8d2ed0SStéphane Rochoy 	device_set_desc(dev, nctdevp->descr);
11306b7b2d80SAdrian Chadd 	return (BUS_PROBE_DEFAULT);
11316b7b2d80SAdrian Chadd }
11326b7b2d80SAdrian Chadd 
11336b7b2d80SAdrian Chadd static int
11346b7b2d80SAdrian Chadd nct_attach(device_t dev)
11356b7b2d80SAdrian Chadd {
11366b7b2d80SAdrian Chadd 	struct nct_softc *sc;
11377f8d2ed0SStéphane Rochoy 	struct nct_gpio_group *gp;
11387f8d2ed0SStéphane Rochoy 	uint32_t pin_num;
11397f8d2ed0SStéphane Rochoy 	uint8_t v;
11407f8d2ed0SStéphane Rochoy 	int flags, i, g;
11416b7b2d80SAdrian Chadd 
11426b7b2d80SAdrian Chadd 	sc          = device_get_softc(dev);
1143e3df342aSAndriy Gapon 	sc->dev     = dev;
11447f8d2ed0SStéphane Rochoy 	sc->nctdevp = nct_lookup_device(dev);
11457f8d2ed0SStéphane Rochoy 
11467f8d2ed0SStéphane Rochoy 	flags = 0;
11477f8d2ed0SStéphane Rochoy 	(void)resource_int_value(device_get_name(dev), device_get_unit(dev), "flags", &flags);
11487f8d2ed0SStéphane Rochoy 
11497f8d2ed0SStéphane Rochoy 	if ((flags & NCT_PREFER_INDIRECT_CHANNEL) == 0) {
11507f8d2ed0SStéphane Rochoy 		uint16_t iobase;
11517f8d2ed0SStéphane Rochoy 		device_t dev_8;
11526b7b2d80SAdrian Chadd 
1153155514eaSAndriy Gapon 		/*
1154155514eaSAndriy Gapon 		 * As strange as it may seem, I/O port base is configured in the
1155155514eaSAndriy Gapon 		 * Logical Device 8 which is primarily used for WDT, but also plays
1156155514eaSAndriy Gapon 		 * a role in GPIO configuration.
1157155514eaSAndriy Gapon 		 */
1158155514eaSAndriy Gapon 		iobase = 0;
1159155514eaSAndriy Gapon 		dev_8 = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_WDT, 8);
1160155514eaSAndriy Gapon 		if (dev_8 != NULL)
1161155514eaSAndriy Gapon 			iobase = superio_get_iobase(dev_8);
1162155514eaSAndriy Gapon 		if (iobase != 0 && iobase != 0xffff) {
11637f8d2ed0SStéphane Rochoy 			int err;
11647f8d2ed0SStéphane Rochoy 
11657f8d2ed0SStéphane Rochoy 			NCT_VERBOSE_PRINTF(dev, "iobase %#x\n", iobase);
1166155514eaSAndriy Gapon 			sc->curgrp = -1;
1167155514eaSAndriy Gapon 			sc->iorid = 0;
1168155514eaSAndriy Gapon 			err = bus_set_resource(dev, SYS_RES_IOPORT, sc->iorid,
1169*8e6ea10cSStéphane Rochoy 				iobase, 7); /* FIXME NCT6796D-E have 8 registers according to table 18.3. */
1170155514eaSAndriy Gapon 			if (err == 0) {
1171155514eaSAndriy Gapon 				sc->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
1172155514eaSAndriy Gapon 					&sc->iorid, RF_ACTIVE);
1173155514eaSAndriy Gapon 				if (sc->iores == NULL) {
1174155514eaSAndriy Gapon 					device_printf(dev, "can't map i/o space, "
11757f8d2ed0SStéphane Rochoy 						"iobase=%#x\n", iobase);
1176155514eaSAndriy Gapon 				}
1177155514eaSAndriy Gapon 			} else {
1178155514eaSAndriy Gapon 				device_printf(dev,
11797f8d2ed0SStéphane Rochoy 					"failed to set io port resource at %#x\n", iobase);
1180155514eaSAndriy Gapon 			}
1181155514eaSAndriy Gapon 		}
11827f8d2ed0SStéphane Rochoy 	}
11837f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(dev, "iores %p %s channel\n",
11847f8d2ed0SStéphane Rochoy 		sc->iores, (sc->iores ? "direct" : "indirect"));
1185155514eaSAndriy Gapon 
11867f8d2ed0SStéphane Rochoy 	/* Enable GPIO groups */
11877f8d2ed0SStéphane Rochoy 	for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) {
11887f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(dev,
11897f8d2ed0SStéphane Rochoy 			"GPIO%d: %d pins, enable with mask 0x%x via ldn 0x%x reg 0x%x\n",
11907f8d2ed0SStéphane Rochoy 			gp->grpnum, gp->npins, gp->enable_mask, gp->enable_ldn,
11917f8d2ed0SStéphane Rochoy 			gp->enable_reg);
11927f8d2ed0SStéphane Rochoy 		v = superio_ldn_read(dev, gp->enable_ldn, gp->enable_reg);
11937f8d2ed0SStéphane Rochoy 		v |= gp->enable_mask;
11947f8d2ed0SStéphane Rochoy 		superio_ldn_write(dev, gp->enable_ldn, gp->enable_reg, v);
11957f8d2ed0SStéphane Rochoy 	}
1196e3df342aSAndriy Gapon 
1197e3df342aSAndriy Gapon 	GPIO_LOCK_INIT(sc);
1198e3df342aSAndriy Gapon 	GPIO_LOCK(sc);
11996b7b2d80SAdrian Chadd 
12007f8d2ed0SStéphane Rochoy 	pin_num   = 0;
12017f8d2ed0SStéphane Rochoy 	sc->npins = 0;
12027f8d2ed0SStéphane Rochoy 	for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) {
12037f8d2ed0SStéphane Rochoy 		sc->npins += gp->npins;
12047f8d2ed0SStéphane Rochoy 		for (i = 0; i < gp->npins; i++, pin_num++) {
12057f8d2ed0SStéphane Rochoy 			struct gpio_pin *pin;
12067f8d2ed0SStéphane Rochoy 
12077f8d2ed0SStéphane Rochoy 			sc->pinmap[pin_num].group  = gp;
12087f8d2ed0SStéphane Rochoy 			sc->pinmap[pin_num].grpnum = gp->grpnum;
12097f8d2ed0SStéphane Rochoy 			sc->pinmap[pin_num].bit    = gp->pinbits[i];
12107f8d2ed0SStéphane Rochoy 
12117f8d2ed0SStéphane Rochoy 			sc->grpmap[gp->grpnum] = gp;
12127f8d2ed0SStéphane Rochoy 
12137f8d2ed0SStéphane Rochoy 			pin           = &sc->pins[pin_num];
12147f8d2ed0SStéphane Rochoy 			pin->gp_pin   = pin_num;
12157f8d2ed0SStéphane Rochoy 			pin->gp_caps  = gp->caps;
12167f8d2ed0SStéphane Rochoy 			pin->gp_flags = 0;
12177f8d2ed0SStéphane Rochoy 
12187f8d2ed0SStéphane Rochoy 			snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%u%u",
12197f8d2ed0SStéphane Rochoy 				gp->grpnum, gp->pinbits[i]);
12207f8d2ed0SStéphane Rochoy 
12217f8d2ed0SStéphane Rochoy 			if (nct_pin_is_input(sc, pin_num))
12227f8d2ed0SStéphane Rochoy 				pin->gp_flags |= GPIO_PIN_INPUT;
12237f8d2ed0SStéphane Rochoy 			else
12247f8d2ed0SStéphane Rochoy 				pin->gp_flags |= GPIO_PIN_OUTPUT;
12257f8d2ed0SStéphane Rochoy 
12267f8d2ed0SStéphane Rochoy 			if (nct_pin_is_opendrain(sc, pin_num))
12277f8d2ed0SStéphane Rochoy 				pin->gp_flags |= GPIO_PIN_OPENDRAIN;
12287f8d2ed0SStéphane Rochoy 			else
12297f8d2ed0SStéphane Rochoy 				pin->gp_flags |= GPIO_PIN_PUSHPULL;
12307f8d2ed0SStéphane Rochoy 
12317f8d2ed0SStéphane Rochoy 			if (nct_pin_is_inverted(sc, pin_num))
12327f8d2ed0SStéphane Rochoy 				pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
12337f8d2ed0SStéphane Rochoy 		}
12347f8d2ed0SStéphane Rochoy 	}
12357f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(dev, "%d pins available\n", sc->npins);
1236155514eaSAndriy Gapon 
1237155514eaSAndriy Gapon 	/*
1238155514eaSAndriy Gapon 	 * Caching input values is meaningless as an input can be changed at any
1239155514eaSAndriy Gapon 	 * time by an external agent.  But outputs are controlled by this
1240155514eaSAndriy Gapon 	 * driver, so it can cache their state.  Also, the hardware remembers
1241155514eaSAndriy Gapon 	 * the output state of a pin when the pin is switched to input mode and
1242155514eaSAndriy Gapon 	 * then back to output mode.  So, the cache stays valid.
1243155514eaSAndriy Gapon 	 * The only problem is with pins that are in input mode at the attach
1244155514eaSAndriy Gapon 	 * time.  For them the output state is not known until it is set by the
1245155514eaSAndriy Gapon 	 * driver for the first time.
1246155514eaSAndriy Gapon 	 * 'out' and 'out_known' bits form a tri-state output cache:
1247155514eaSAndriy Gapon 	 * |-----+-----------+---------|
1248155514eaSAndriy Gapon 	 * | out | out_known | cache   |
1249155514eaSAndriy Gapon 	 * |-----+-----------+---------|
1250155514eaSAndriy Gapon 	 * |   X |         0 | invalid |
1251155514eaSAndriy Gapon 	 * |   0 |         1 |       0 |
1252155514eaSAndriy Gapon 	 * |   1 |         1 |       1 |
1253155514eaSAndriy Gapon 	 * |-----+-----------+---------|
1254155514eaSAndriy Gapon 	 */
12557f8d2ed0SStéphane Rochoy 	for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) {
12567f8d2ed0SStéphane Rochoy 		sc->cache.inv[gp->grpnum]       = nct_read_reg(sc, REG_INV, gp->grpnum);
12577f8d2ed0SStéphane Rochoy 		sc->cache.ior[gp->grpnum]       = nct_read_reg(sc, REG_IOR, gp->grpnum);
12587f8d2ed0SStéphane Rochoy 		sc->cache.out[gp->grpnum]       = nct_read_reg(sc, REG_DAT, gp->grpnum);
12597f8d2ed0SStéphane Rochoy 		sc->cache.out_known[gp->grpnum] = ~sc->cache.ior[gp->grpnum];
12606b7b2d80SAdrian Chadd 	}
12617f8d2ed0SStéphane Rochoy 
12626b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
12636b7b2d80SAdrian Chadd 
12646b7b2d80SAdrian Chadd 	sc->busdev = gpiobus_attach_bus(dev);
12656b7b2d80SAdrian Chadd 	if (sc->busdev == NULL) {
12667f8d2ed0SStéphane Rochoy 		device_printf(dev, "failed to attach to gpiobus\n");
12676b7b2d80SAdrian Chadd 		GPIO_LOCK_DESTROY(sc);
12686b7b2d80SAdrian Chadd 		return (ENXIO);
12696b7b2d80SAdrian Chadd 	}
12706b7b2d80SAdrian Chadd 
12716b7b2d80SAdrian Chadd 	return (0);
12726b7b2d80SAdrian Chadd }
12736b7b2d80SAdrian Chadd 
12746b7b2d80SAdrian Chadd static int
12756b7b2d80SAdrian Chadd nct_detach(device_t dev)
12766b7b2d80SAdrian Chadd {
12776b7b2d80SAdrian Chadd 	struct nct_softc *sc;
12786b7b2d80SAdrian Chadd 
12796b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
12806b7b2d80SAdrian Chadd 	gpiobus_detach_bus(dev);
12816b7b2d80SAdrian Chadd 
1282155514eaSAndriy Gapon 	if (sc->iores != NULL)
1283155514eaSAndriy Gapon 		bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->iores);
12846b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
12856b7b2d80SAdrian Chadd 	GPIO_LOCK_DESTROY(sc);
12866b7b2d80SAdrian Chadd 
12876b7b2d80SAdrian Chadd 	return (0);
12886b7b2d80SAdrian Chadd }
12896b7b2d80SAdrian Chadd 
12906b7b2d80SAdrian Chadd static device_t
12916b7b2d80SAdrian Chadd nct_gpio_get_bus(device_t dev)
12926b7b2d80SAdrian Chadd {
12936b7b2d80SAdrian Chadd 	struct nct_softc *sc;
12946b7b2d80SAdrian Chadd 
12956b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
12966b7b2d80SAdrian Chadd 
12976b7b2d80SAdrian Chadd 	return (sc->busdev);
12986b7b2d80SAdrian Chadd }
12996b7b2d80SAdrian Chadd 
13006b7b2d80SAdrian Chadd static int
13017f8d2ed0SStéphane Rochoy nct_gpio_pin_max(device_t dev, int *maxpin)
13026b7b2d80SAdrian Chadd {
13037f8d2ed0SStéphane Rochoy 	struct nct_softc *sc;
13046b7b2d80SAdrian Chadd 
13057f8d2ed0SStéphane Rochoy 	sc      = device_get_softc(dev);
13067f8d2ed0SStéphane Rochoy 	*maxpin = sc->npins - 1;
13076b7b2d80SAdrian Chadd 	return (0);
13086b7b2d80SAdrian Chadd }
13096b7b2d80SAdrian Chadd 
13106b7b2d80SAdrian Chadd static int
13116b7b2d80SAdrian Chadd nct_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value)
13126b7b2d80SAdrian Chadd {
13136b7b2d80SAdrian Chadd 	struct nct_softc *sc;
13146b7b2d80SAdrian Chadd 
13157f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
13167f8d2ed0SStéphane Rochoy 
13177f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
13186b7b2d80SAdrian Chadd 		return (EINVAL);
13196b7b2d80SAdrian Chadd 
13206b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
1321155514eaSAndriy Gapon 	if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) {
1322155514eaSAndriy Gapon 		GPIO_UNLOCK(sc);
1323155514eaSAndriy Gapon 		return (EINVAL);
1324155514eaSAndriy Gapon 	}
13256b7b2d80SAdrian Chadd 	nct_write_pin(sc, pin_num, pin_value);
13266b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
13276b7b2d80SAdrian Chadd 
13286b7b2d80SAdrian Chadd 	return (0);
13296b7b2d80SAdrian Chadd }
13306b7b2d80SAdrian Chadd 
13316b7b2d80SAdrian Chadd static int
13326b7b2d80SAdrian Chadd nct_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value)
13336b7b2d80SAdrian Chadd {
13346b7b2d80SAdrian Chadd 	struct nct_softc *sc;
13356b7b2d80SAdrian Chadd 
13367f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
13377f8d2ed0SStéphane Rochoy 
13387f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
13396b7b2d80SAdrian Chadd 		return (EINVAL);
13406b7b2d80SAdrian Chadd 
13416b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
13426b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
13436b7b2d80SAdrian Chadd 	*pin_value = nct_read_pin(sc, pin_num);
13446b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
13456b7b2d80SAdrian Chadd 
13466b7b2d80SAdrian Chadd 	return (0);
13476b7b2d80SAdrian Chadd }
13486b7b2d80SAdrian Chadd 
13496b7b2d80SAdrian Chadd static int
13506b7b2d80SAdrian Chadd nct_gpio_pin_toggle(device_t dev, uint32_t pin_num)
13516b7b2d80SAdrian Chadd {
13526b7b2d80SAdrian Chadd 	struct nct_softc *sc;
13536b7b2d80SAdrian Chadd 
13547f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
13557f8d2ed0SStéphane Rochoy 
13567f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
13576b7b2d80SAdrian Chadd 		return (EINVAL);
13586b7b2d80SAdrian Chadd 
13596b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
13606b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
1361155514eaSAndriy Gapon 	if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) {
1362155514eaSAndriy Gapon 		GPIO_UNLOCK(sc);
1363155514eaSAndriy Gapon 		return (EINVAL);
1364155514eaSAndriy Gapon 	}
13656b7b2d80SAdrian Chadd 	if (nct_read_pin(sc, pin_num))
13666b7b2d80SAdrian Chadd 		nct_write_pin(sc, pin_num, 0);
13676b7b2d80SAdrian Chadd 	else
13686b7b2d80SAdrian Chadd 		nct_write_pin(sc, pin_num, 1);
13696b7b2d80SAdrian Chadd 
13706b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
13716b7b2d80SAdrian Chadd 
13726b7b2d80SAdrian Chadd 	return (0);
13736b7b2d80SAdrian Chadd }
13746b7b2d80SAdrian Chadd 
13756b7b2d80SAdrian Chadd static int
13766b7b2d80SAdrian Chadd nct_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps)
13776b7b2d80SAdrian Chadd {
13786b7b2d80SAdrian Chadd 	struct nct_softc *sc;
13796b7b2d80SAdrian Chadd 
13807f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
13817f8d2ed0SStéphane Rochoy 
13827f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
13836b7b2d80SAdrian Chadd 		return (EINVAL);
13846b7b2d80SAdrian Chadd 
13856b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
13866b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
13876b7b2d80SAdrian Chadd 	*caps = sc->pins[pin_num].gp_caps;
13886b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
13896b7b2d80SAdrian Chadd 
13906b7b2d80SAdrian Chadd 	return (0);
13916b7b2d80SAdrian Chadd }
13926b7b2d80SAdrian Chadd 
13936b7b2d80SAdrian Chadd static int
13946b7b2d80SAdrian Chadd nct_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags)
13956b7b2d80SAdrian Chadd {
13966b7b2d80SAdrian Chadd 	struct nct_softc *sc;
13976b7b2d80SAdrian Chadd 
13987f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
13997f8d2ed0SStéphane Rochoy 
14007f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
14016b7b2d80SAdrian Chadd 		return (EINVAL);
14026b7b2d80SAdrian Chadd 
14036b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
14046b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
14056b7b2d80SAdrian Chadd 	*flags = sc->pins[pin_num].gp_flags;
14066b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
14076b7b2d80SAdrian Chadd 
14086b7b2d80SAdrian Chadd 	return (0);
14096b7b2d80SAdrian Chadd }
14106b7b2d80SAdrian Chadd 
14116b7b2d80SAdrian Chadd static int
14126b7b2d80SAdrian Chadd nct_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name)
14136b7b2d80SAdrian Chadd {
14146b7b2d80SAdrian Chadd 	struct nct_softc *sc;
14156b7b2d80SAdrian Chadd 
14167f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
14177f8d2ed0SStéphane Rochoy 
14187f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
14196b7b2d80SAdrian Chadd 		return (EINVAL);
14206b7b2d80SAdrian Chadd 
14216b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
14226b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
14236b7b2d80SAdrian Chadd 	memcpy(name, sc->pins[pin_num].gp_name, GPIOMAXNAME);
14246b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
14256b7b2d80SAdrian Chadd 
14266b7b2d80SAdrian Chadd 	return (0);
14276b7b2d80SAdrian Chadd }
14286b7b2d80SAdrian Chadd 
14296b7b2d80SAdrian Chadd static int
14306b7b2d80SAdrian Chadd nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags)
14316b7b2d80SAdrian Chadd {
14326b7b2d80SAdrian Chadd 	struct nct_softc *sc;
14336b7b2d80SAdrian Chadd 	struct gpio_pin *pin;
14346b7b2d80SAdrian Chadd 
14357f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
14367f8d2ed0SStéphane Rochoy 
14377f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
14386b7b2d80SAdrian Chadd 		return (EINVAL);
14396b7b2d80SAdrian Chadd 
14406b7b2d80SAdrian Chadd 	pin = &sc->pins[pin_num];
14416b7b2d80SAdrian Chadd 	if ((flags & pin->gp_caps) != flags)
14426b7b2d80SAdrian Chadd 		return (EINVAL);
14436b7b2d80SAdrian Chadd 
14446b7b2d80SAdrian Chadd 	if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
14456b7b2d80SAdrian Chadd 		(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
14466b7b2d80SAdrian Chadd 			return (EINVAL);
14476b7b2d80SAdrian Chadd 	}
14486b7b2d80SAdrian Chadd 	if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) ==
14496b7b2d80SAdrian Chadd 		(GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) {
1450155514eaSAndriy Gapon 			return (EINVAL);
1451155514eaSAndriy Gapon 	}
1452155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) ==
1453155514eaSAndriy Gapon 		(GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) {
14546b7b2d80SAdrian Chadd 			return (EINVAL);
14556b7b2d80SAdrian Chadd 	}
14566b7b2d80SAdrian Chadd 
1457155514eaSAndriy Gapon 	GPIO_ASSERT_UNLOCKED(sc);
1458155514eaSAndriy Gapon 	GPIO_LOCK(sc);
14597f8d2ed0SStéphane Rochoy 
14607f8d2ed0SStéphane Rochoy 	/* input or output */
1461155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) != 0) {
1462155514eaSAndriy Gapon 		nct_set_pin_input(sc, pin_num, (flags & GPIO_PIN_INPUT) != 0);
1463155514eaSAndriy Gapon 		pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
1464155514eaSAndriy Gapon 		pin->gp_flags |= flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
1465155514eaSAndriy Gapon 	}
14667f8d2ed0SStéphane Rochoy 
14677f8d2ed0SStéphane Rochoy 	/* invert */
1468155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) != 0) {
14697f8d2ed0SStéphane Rochoy 		nct_set_pin_inverted(sc, pin_num, 1);
14707f8d2ed0SStéphane Rochoy 		pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
14717f8d2ed0SStéphane Rochoy 	} else {
14727f8d2ed0SStéphane Rochoy 		nct_set_pin_inverted(sc, pin_num, 0);
1473155514eaSAndriy Gapon 		pin->gp_flags &= ~(GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
1474155514eaSAndriy Gapon 	}
14757f8d2ed0SStéphane Rochoy 
14767f8d2ed0SStéphane Rochoy 	/* Open drain or push pull */
1477155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) != 0) {
14786b7b2d80SAdrian Chadd 		if (flags & GPIO_PIN_OPENDRAIN)
14796b7b2d80SAdrian Chadd 			nct_set_pin_opendrain(sc, pin_num);
14806b7b2d80SAdrian Chadd 		else
14816b7b2d80SAdrian Chadd 			nct_set_pin_pushpull(sc, pin_num);
1482155514eaSAndriy Gapon 		pin->gp_flags &= ~(GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL);
1483155514eaSAndriy Gapon 		pin->gp_flags |=
1484155514eaSAndriy Gapon 		    flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL);
14856b7b2d80SAdrian Chadd 	}
14866b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
14876b7b2d80SAdrian Chadd 
14886b7b2d80SAdrian Chadd 	return (0);
14896b7b2d80SAdrian Chadd }
14906b7b2d80SAdrian Chadd 
14916b7b2d80SAdrian Chadd static device_method_t nct_methods[] = {
14926b7b2d80SAdrian Chadd 	/* Device interface */
14936b7b2d80SAdrian Chadd 	DEVMETHOD(device_probe,		nct_probe),
14946b7b2d80SAdrian Chadd 	DEVMETHOD(device_attach,	nct_attach),
14956b7b2d80SAdrian Chadd 	DEVMETHOD(device_detach,	nct_detach),
14966b7b2d80SAdrian Chadd 
14976b7b2d80SAdrian Chadd 	/* GPIO */
14986b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_get_bus,		nct_gpio_get_bus),
14996b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_max,		nct_gpio_pin_max),
15006b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_get,		nct_gpio_pin_get),
15016b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_set,		nct_gpio_pin_set),
15026b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_toggle,	nct_gpio_pin_toggle),
15036b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getname,	nct_gpio_pin_getname),
15046b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getcaps,	nct_gpio_pin_getcaps),
15056b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getflags,	nct_gpio_pin_getflags),
15066b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_setflags,	nct_gpio_pin_setflags),
15076b7b2d80SAdrian Chadd 
15086b7b2d80SAdrian Chadd 	DEVMETHOD_END
15096b7b2d80SAdrian Chadd };
15106b7b2d80SAdrian Chadd 
1511e3df342aSAndriy Gapon static driver_t nct_driver = {
15126b7b2d80SAdrian Chadd 	"gpio",
15136b7b2d80SAdrian Chadd 	nct_methods,
15146b7b2d80SAdrian Chadd 	sizeof(struct nct_softc)
15156b7b2d80SAdrian Chadd };
15166b7b2d80SAdrian Chadd 
151784c5f982SJohn Baldwin DRIVER_MODULE(nctgpio, superio, nct_driver, NULL, NULL);
15186b7b2d80SAdrian Chadd MODULE_DEPEND(nctgpio, gpiobus, 1, 1, 1);
1519e3df342aSAndriy Gapon MODULE_DEPEND(nctgpio, superio, 1, 1, 1);
1520e3df342aSAndriy Gapon MODULE_VERSION(nctgpio, 1);
1521