xref: /freebsd/sys/dev/nctgpio/nctgpio.c (revision 7f8d2ed03bc670393d7a8322b0681f46ead745e7)
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 
53*7f8d2ed0SStéphane Rochoy #define NCT_PPOD_LDN 0xf /* LDN used to select Push-Pull/Open-Drain */
546b7b2d80SAdrian Chadd 
55*7f8d2ed0SStéphane Rochoy /* Direct access through GPIO register table */
56*7f8d2ed0SStéphane Rochoy #define	NCT_IO_GSR			0 /* Group Select */
57*7f8d2ed0SStéphane Rochoy #define	NCT_IO_IOR			1 /* I/O */
58*7f8d2ed0SStéphane Rochoy #define	NCT_IO_DAT			2 /* Data */
59*7f8d2ed0SStéphane Rochoy #define	NCT_IO_INV			3 /* Inversion */
60*7f8d2ed0SStéphane Rochoy #define	NCT_IO_DST          4 /* Status */
616b7b2d80SAdrian Chadd 
62*7f8d2ed0SStéphane Rochoy #define NCT_MAX_GROUP   9
63*7f8d2ed0SStéphane Rochoy #define NCT_MAX_PIN     75
646b7b2d80SAdrian Chadd 
65*7f8d2ed0SStéphane Rochoy #define NCT_PIN_IS_VALID(_sc, _p)   ((_p) < (_sc)->npins)
66*7f8d2ed0SStéphane Rochoy #define NCT_PIN_GROUP(_sc, _p)      ((_sc)->pinmap[(_p)].group)
67*7f8d2ed0SStéphane Rochoy #define NCT_PIN_GRPNUM(_sc, _p)     ((_sc)->pinmap[(_p)].grpnum)
68*7f8d2ed0SStéphane Rochoy #define NCT_PIN_BIT(_sc, _p)        ((_sc)->pinmap[(_p)].bit)
69*7f8d2ed0SSté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 
75*7f8d2ed0SStéphane Rochoy #define NCT_PREFER_INDIRECT_CHANNEL 2
76*7f8d2ed0SStéphane Rochoy 
77*7f8d2ed0SStéphane Rochoy #define NCT_VERBOSE_PRINTF(dev, ...)            \
78*7f8d2ed0SStéphane Rochoy 	do {                                        \
79*7f8d2ed0SStéphane Rochoy 		if (__predict_false(bootverbose))       \
80*7f8d2ed0SStéphane Rochoy 			device_printf(dev, __VA_ARGS__);    \
81*7f8d2ed0SStéphane Rochoy 	} while (0)
82*7f8d2ed0SSté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 
93*7f8d2ed0SStéphane Rochoy struct nct_gpio_group {
94*7f8d2ed0SStéphane Rochoy  	uint32_t    caps;
95*7f8d2ed0SStéphane Rochoy 	uint8_t     enable_ldn;
96*7f8d2ed0SStéphane Rochoy 	uint8_t     enable_reg;
97*7f8d2ed0SStéphane Rochoy 	uint8_t     enable_mask;
98*7f8d2ed0SStéphane Rochoy 	uint8_t     data_ldn;
99*7f8d2ed0SStéphane Rochoy 	uint8_t     iobase;
100*7f8d2ed0SStéphane Rochoy 	uint8_t     ppod_reg; /* Push-Pull/Open-Drain */
101*7f8d2ed0SStéphane Rochoy 	uint8_t     grpnum;
102*7f8d2ed0SStéphane Rochoy 	uint8_t     pinbits[8];
103*7f8d2ed0SStéphane Rochoy 	uint8_t     npins;
104*7f8d2ed0SStéphane Rochoy };
105*7f8d2ed0SSté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 {
114*7f8d2ed0SStéphane Rochoy 		uint8_t ior[NCT_MAX_GROUP + 1];       /* direction, 1: input 0: output */
115*7f8d2ed0SStéphane Rochoy 		uint8_t out[NCT_MAX_GROUP + 1];       /* output value */
116*7f8d2ed0SStéphane Rochoy 		uint8_t out_known[NCT_MAX_GROUP + 1]; /* whether out is valid */
117*7f8d2ed0SSté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];
120*7f8d2ed0SStéphane Rochoy 	struct nct_device			*nctdevp;
121*7f8d2ed0SStéphane Rochoy 	int							npins; /* Total number of pins */
122*7f8d2ed0SStéphane Rochoy 
123*7f8d2ed0SStéphane Rochoy 	/* Lookup tables */
124*7f8d2ed0SStéphane Rochoy 	struct {
125*7f8d2ed0SStéphane Rochoy 		struct nct_gpio_group *group;
126*7f8d2ed0SStéphane Rochoy 		uint8_t                grpnum;
127*7f8d2ed0SStéphane Rochoy 		uint8_t                bit;
128*7f8d2ed0SStéphane Rochoy 	} pinmap[NCT_MAX_PIN+1];
129*7f8d2ed0SSté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 
140*7f8d2ed0SStéphane Rochoy #define GET_BIT(v, b)	(((v) >> (b)) & 1)
141*7f8d2ed0SStéphane Rochoy 
142*7f8d2ed0SStéphane Rochoy /*
143*7f8d2ed0SStéphane Rochoy  * For most devices there are several GPIO devices, we attach only to one of
144*7f8d2ed0SStéphane Rochoy  * them and use the rest without attaching.
145*7f8d2ed0SStéphane Rochoy  */
146*7f8d2ed0SStéphane Rochoy struct nct_device {
147*7f8d2ed0SStéphane Rochoy 	uint16_t                  devid;
148*7f8d2ed0SStéphane Rochoy 	int                       extid;
1496b7b2d80SAdrian Chadd 	const char               *descr;
150*7f8d2ed0SStéphane Rochoy 	int                       ngroups;
151*7f8d2ed0SStéphane Rochoy 	struct nct_gpio_group     groups[NCT_MAX_GROUP + 1];
152*7f8d2ed0SStéphane Rochoy } nct_devices[] = {
1536b7b2d80SAdrian Chadd 	{
154*7f8d2ed0SStéphane Rochoy 		.devid   = 0x1061,
155*7f8d2ed0SStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT5104D",
156*7f8d2ed0SStéphane Rochoy 		.ngroups = 2,
157*7f8d2ed0SStéphane Rochoy 		.groups  = {
158*7f8d2ed0SStéphane Rochoy 			{
159*7f8d2ed0SStéphane Rochoy 				.grpnum      = 0,
160*7f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
161*7f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
162*7f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
163*7f8d2ed0SStéphane Rochoy 				.enable_mask = 0x01,
164*7f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
165*7f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe0,
166*7f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
167*7f8d2ed0SStéphane Rochoy 				.npins       = 8,
168*7f8d2ed0SStéphane Rochoy 				.iobase      = 0xe0,
1696b7b2d80SAdrian Chadd 			},
1706b7b2d80SAdrian Chadd 			{
171*7f8d2ed0SStéphane Rochoy 				.grpnum      = 1,
172*7f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
173*7f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
174*7f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
175*7f8d2ed0SStéphane Rochoy 				.enable_mask = 0x02,
176*7f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
177*7f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe1,
178*7f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
179*7f8d2ed0SStéphane Rochoy 				.npins       = 8,
180*7f8d2ed0SStéphane Rochoy 				.iobase      = 0xe4,
181*7f8d2ed0SStéphane Rochoy 			},
182*7f8d2ed0SStéphane Rochoy 		},
1836b7b2d80SAdrian Chadd 	},
1849d1208bbSOleksandr Tymoshenko 	{
185*7f8d2ed0SStéphane Rochoy 		.devid   = 0xc452,
186*7f8d2ed0SStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT5104D (PC-Engines APU)",
187*7f8d2ed0SStéphane Rochoy 		.ngroups = 2,
188*7f8d2ed0SStéphane Rochoy 		.groups  = {
189*7f8d2ed0SStéphane Rochoy 			{
190*7f8d2ed0SStéphane Rochoy 				.grpnum      = 0,
191*7f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
192*7f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
193*7f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
194*7f8d2ed0SStéphane Rochoy 				.enable_mask = 0x01,
195*7f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
196*7f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe0,
197*7f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
198*7f8d2ed0SStéphane Rochoy 				.npins       = 8,
199*7f8d2ed0SStéphane Rochoy 				.iobase      = 0xe0,
200*7f8d2ed0SStéphane Rochoy 			},
201*7f8d2ed0SStéphane Rochoy 			{
202*7f8d2ed0SStéphane Rochoy 				.grpnum      = 1,
203*7f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
204*7f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
205*7f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
206*7f8d2ed0SStéphane Rochoy 				.enable_mask = 0x02,
207*7f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
208*7f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe1,
209*7f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
210*7f8d2ed0SStéphane Rochoy 				.npins       = 8,
211*7f8d2ed0SStéphane Rochoy 				.iobase      = 0xe4,
212*7f8d2ed0SStéphane Rochoy 			},
213*7f8d2ed0SStéphane Rochoy 		},
214*7f8d2ed0SStéphane Rochoy 	},
215*7f8d2ed0SStéphane Rochoy 	{
216*7f8d2ed0SStéphane Rochoy 		.devid   = 0xc453,
217*7f8d2ed0SStéphane Rochoy 		.descr   = "GPIO on Nuvoton NCT5104D (PC-Engines APU3)",
218*7f8d2ed0SStéphane Rochoy 		.ngroups = 2,
219*7f8d2ed0SStéphane Rochoy 		.groups  = {
220*7f8d2ed0SStéphane Rochoy 			{
221*7f8d2ed0SStéphane Rochoy 				.grpnum      = 0,
222*7f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
223*7f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
224*7f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
225*7f8d2ed0SStéphane Rochoy 				.enable_mask = 0x01,
226*7f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
227*7f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe0,
228*7f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
229*7f8d2ed0SStéphane Rochoy 				.npins       = 8,
230*7f8d2ed0SStéphane Rochoy 				.iobase      = 0xe0,
231*7f8d2ed0SStéphane Rochoy 			},
232*7f8d2ed0SStéphane Rochoy 			{
233*7f8d2ed0SStéphane Rochoy 				.grpnum      = 1,
234*7f8d2ed0SStéphane Rochoy 				.pinbits     = { 0, 1, 2, 3, 4, 5, 6, 7 },
235*7f8d2ed0SStéphane Rochoy 				.enable_ldn  = 0x07,
236*7f8d2ed0SStéphane Rochoy 				.enable_reg  = 0x30,
237*7f8d2ed0SStéphane Rochoy 				.enable_mask = 0x02,
238*7f8d2ed0SStéphane Rochoy 				.data_ldn    = 0x07,
239*7f8d2ed0SStéphane Rochoy 				.ppod_reg    = 0xe1,
240*7f8d2ed0SStéphane Rochoy 				.caps        = NCT_GPIO_CAPS,
241*7f8d2ed0SStéphane Rochoy 				.npins       = 8,
242*7f8d2ed0SStéphane Rochoy 				.iobase      = 0xe4,
243*7f8d2ed0SStéphane Rochoy 			},
244*7f8d2ed0SStéphane Rochoy 		},
2459d1208bbSOleksandr Tymoshenko 	},
2466b7b2d80SAdrian Chadd };
2476b7b2d80SAdrian Chadd 
248*7f8d2ed0SStéphane Rochoy static const char *
249*7f8d2ed0SStéphane Rochoy io2str(uint8_t ioport)
2506b7b2d80SAdrian Chadd {
251*7f8d2ed0SStéphane Rochoy 	switch (ioport) {
252*7f8d2ed0SStéphane Rochoy 	case NCT_IO_GSR: return ("grpsel");
253*7f8d2ed0SStéphane Rochoy 	case NCT_IO_IOR: return ("io");
254*7f8d2ed0SStéphane Rochoy 	case NCT_IO_DAT: return ("data");
255*7f8d2ed0SStéphane Rochoy 	case NCT_IO_INV: return ("inv");
256*7f8d2ed0SStéphane Rochoy 	case NCT_IO_DST: return ("status");
257*7f8d2ed0SStéphane Rochoy 	default:         return ("?");
258*7f8d2ed0SStéphane Rochoy 	}
259*7f8d2ed0SStéphane Rochoy }
2606b7b2d80SAdrian Chadd 
261*7f8d2ed0SStéphane Rochoy static void
262*7f8d2ed0SStéphane Rochoy nct_io_set_group(struct nct_softc *sc, uint8_t grpnum)
263*7f8d2ed0SStéphane Rochoy {
264155514eaSAndriy Gapon 	GPIO_ASSERT_LOCKED(sc);
265*7f8d2ed0SStéphane Rochoy 
266*7f8d2ed0SStéphane Rochoy 	if (grpnum == sc->curgrp)
267*7f8d2ed0SStéphane Rochoy 		return;
268*7f8d2ed0SStéphane Rochoy 
269*7f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x ioport %d\n",
270*7f8d2ed0SStéphane Rochoy 		io2str(NCT_IO_GSR), grpnum, NCT_IO_GSR);
271*7f8d2ed0SStéphane Rochoy 	bus_write_1(sc->iores, NCT_IO_GSR, grpnum);
272*7f8d2ed0SStéphane Rochoy 	sc->curgrp = grpnum;
273155514eaSAndriy Gapon }
2746b7b2d80SAdrian Chadd 
275155514eaSAndriy Gapon static uint8_t
276*7f8d2ed0SStéphane Rochoy nct_io_read(struct nct_softc *sc, uint8_t grpnum, uint8_t reg)
277155514eaSAndriy Gapon {
278155514eaSAndriy Gapon 	uint8_t val;
279155514eaSAndriy Gapon 
280*7f8d2ed0SStéphane Rochoy 	nct_io_set_group(sc, grpnum);
281155514eaSAndriy Gapon 
282*7f8d2ed0SStéphane Rochoy 	val = bus_read_1(sc->iores, reg);
283*7f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "read %s 0x%x ioport %d\n",
284*7f8d2ed0SStéphane Rochoy 		io2str(reg), val, reg);
285155514eaSAndriy Gapon 	return (val);
286155514eaSAndriy Gapon }
287155514eaSAndriy Gapon 
288*7f8d2ed0SStéphane Rochoy static void
289*7f8d2ed0SStéphane Rochoy nct_io_write(struct nct_softc *sc, uint8_t grpnum, uint8_t reg, uint8_t val)
290155514eaSAndriy Gapon {
291*7f8d2ed0SStéphane Rochoy 	nct_io_set_group(sc, grpnum);
292*7f8d2ed0SStéphane Rochoy 
293*7f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x ioport %d\n",
294*7f8d2ed0SStéphane Rochoy 		io2str(reg), val, reg);
295*7f8d2ed0SStéphane Rochoy 	bus_write_1(sc->iores, reg, val);
296*7f8d2ed0SStéphane Rochoy }
297*7f8d2ed0SStéphane Rochoy 
298*7f8d2ed0SStéphane Rochoy static uint8_t
299*7f8d2ed0SStéphane Rochoy nct_get_ioreg(struct nct_softc *sc, reg_t reg, uint8_t grpnum)
300*7f8d2ed0SStéphane Rochoy {
301*7f8d2ed0SStéphane Rochoy 	uint8_t iobase;
302*7f8d2ed0SStéphane Rochoy 
303*7f8d2ed0SStéphane Rochoy 	if (sc->iores != NULL)
304*7f8d2ed0SStéphane Rochoy 		iobase = NCT_IO_IOR;
305*7f8d2ed0SStéphane Rochoy 	else
306*7f8d2ed0SStéphane Rochoy 		iobase = sc->grpmap[grpnum]->iobase;
307*7f8d2ed0SStéphane Rochoy 	return (iobase + reg);
308*7f8d2ed0SStéphane Rochoy }
309*7f8d2ed0SStéphane Rochoy 
310*7f8d2ed0SStéphane Rochoy static const char *
311*7f8d2ed0SStéphane Rochoy reg2str(reg_t reg)
312*7f8d2ed0SStéphane Rochoy {
313*7f8d2ed0SStéphane Rochoy 	switch (reg) {
314*7f8d2ed0SStéphane Rochoy 	case REG_IOR: return ("io");
315*7f8d2ed0SStéphane Rochoy 	case REG_DAT: return ("data");
316*7f8d2ed0SStéphane Rochoy 	case REG_INV: return ("inv");
317*7f8d2ed0SStéphane Rochoy 	default:      return ("?");
318*7f8d2ed0SStéphane Rochoy 	}
319*7f8d2ed0SStéphane Rochoy }
320*7f8d2ed0SStéphane Rochoy 
321*7f8d2ed0SStéphane Rochoy static uint8_t
322*7f8d2ed0SStéphane Rochoy nct_read_reg(struct nct_softc *sc, reg_t reg, uint8_t grpnum)
323*7f8d2ed0SStéphane Rochoy {
324*7f8d2ed0SStéphane Rochoy 	struct nct_gpio_group *gp;
325*7f8d2ed0SStéphane Rochoy 	uint8_t                ioreg;
326155514eaSAndriy Gapon 	uint8_t                val;
327155514eaSAndriy Gapon 
328*7f8d2ed0SStéphane Rochoy 	ioreg = nct_get_ioreg(sc, reg, grpnum);
329155514eaSAndriy Gapon 
330*7f8d2ed0SStéphane Rochoy 	if (sc->iores != NULL)
331*7f8d2ed0SStéphane Rochoy 		return (nct_io_read(sc, grpnum, ioreg));
332*7f8d2ed0SStéphane Rochoy 
333*7f8d2ed0SStéphane Rochoy 	gp  = sc->grpmap[grpnum];
334*7f8d2ed0SStéphane Rochoy 	val = superio_ldn_read(sc->dev, gp->data_ldn, ioreg);
335*7f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "read %s 0x%x from group GPIO%u ioreg 0x%x\n",
336*7f8d2ed0SStéphane Rochoy 		reg2str(reg), val, grpnum, ioreg);
337*7f8d2ed0SStéphane Rochoy 	return (val);
338155514eaSAndriy Gapon }
339155514eaSAndriy Gapon 
340155514eaSAndriy Gapon static int
341155514eaSAndriy Gapon nct_get_pin_cache(struct nct_softc *sc, uint32_t pin_num, uint8_t *cache)
342155514eaSAndriy Gapon {
343155514eaSAndriy Gapon 	uint8_t bit;
344155514eaSAndriy Gapon 	uint8_t group;
345155514eaSAndriy Gapon 	uint8_t val;
346155514eaSAndriy Gapon 
347*7f8d2ed0SStéphane Rochoy 	KASSERT(NCT_PIN_IS_VALID(sc, pin_num), ("%s: invalid pin number %d",
348155514eaSAndriy Gapon 	    __func__, pin_num));
349155514eaSAndriy Gapon 
350*7f8d2ed0SStéphane Rochoy 	group = NCT_PIN_GRPNUM(sc, pin_num);
351*7f8d2ed0SStéphane Rochoy 	bit   = NCT_PIN_BIT(sc, pin_num);
352155514eaSAndriy Gapon 	val   = cache[group];
353155514eaSAndriy Gapon 	return (GET_BIT(val, bit));
354155514eaSAndriy Gapon }
355155514eaSAndriy Gapon 
356155514eaSAndriy Gapon static void
357*7f8d2ed0SStéphane Rochoy nct_write_reg(struct nct_softc *sc, reg_t reg, uint8_t grpnum, uint8_t val)
358155514eaSAndriy Gapon {
359*7f8d2ed0SStéphane Rochoy 	struct nct_gpio_group *gp;
360155514eaSAndriy Gapon 	uint8_t                ioreg;
361155514eaSAndriy Gapon 
362*7f8d2ed0SStéphane Rochoy 	ioreg = nct_get_ioreg(sc, reg, grpnum);
363*7f8d2ed0SStéphane Rochoy 
364*7f8d2ed0SStéphane Rochoy 	if (sc->iores != NULL) {
365*7f8d2ed0SStéphane Rochoy 		nct_io_write(sc, grpnum, ioreg, val);
366*7f8d2ed0SStéphane Rochoy 		return;
367*7f8d2ed0SStéphane Rochoy 	}
368*7f8d2ed0SStéphane Rochoy 
369*7f8d2ed0SStéphane Rochoy 	gp = sc->grpmap[grpnum];
370*7f8d2ed0SStéphane Rochoy 	superio_ldn_write(sc->dev, gp->data_ldn, ioreg, val);
371*7f8d2ed0SStéphane Rochoy 
372*7f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(sc->dev, "write %s 0x%x to group GPIO%u ioreg 0x%x\n",
373*7f8d2ed0SStéphane Rochoy 		reg2str(reg), val, grpnum, ioreg);
374155514eaSAndriy Gapon }
375155514eaSAndriy Gapon 
376155514eaSAndriy Gapon static void
377155514eaSAndriy Gapon nct_set_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num, bool val)
378155514eaSAndriy Gapon {
379155514eaSAndriy Gapon 	uint8_t *cache;
380155514eaSAndriy Gapon 	uint8_t bit;
381155514eaSAndriy Gapon 	uint8_t bitval;
382155514eaSAndriy Gapon 	uint8_t group;
383155514eaSAndriy Gapon 	uint8_t mask;
384155514eaSAndriy Gapon 
385*7f8d2ed0SStéphane Rochoy 	KASSERT(NCT_PIN_IS_VALID(sc, pin_num),
386155514eaSAndriy Gapon 	    ("%s: invalid pin number %d", __func__, pin_num));
387155514eaSAndriy Gapon 	KASSERT(reg == REG_IOR || reg == REG_INV,
388155514eaSAndriy Gapon 	    ("%s: unsupported register %d", __func__, reg));
389155514eaSAndriy Gapon 
390*7f8d2ed0SStéphane Rochoy 	group  = NCT_PIN_GRPNUM(sc, pin_num);
391*7f8d2ed0SStéphane Rochoy 	bit    = NCT_PIN_BIT(sc, pin_num);
392155514eaSAndriy Gapon 	mask   = (uint8_t)1 << bit;
393155514eaSAndriy Gapon 	bitval = (uint8_t)val << bit;
394155514eaSAndriy Gapon 
395155514eaSAndriy Gapon 	if (reg == REG_IOR)
396155514eaSAndriy Gapon 		cache = &sc->cache.ior[group];
397155514eaSAndriy Gapon 	else
398155514eaSAndriy Gapon 		cache = &sc->cache.inv[group];
399155514eaSAndriy Gapon 	if ((*cache & mask) == bitval)
400155514eaSAndriy Gapon 		return;
401155514eaSAndriy Gapon 	*cache &= ~mask;
402155514eaSAndriy Gapon 	*cache |= bitval;
403155514eaSAndriy Gapon 	nct_write_reg(sc, reg, group, *cache);
4046b7b2d80SAdrian Chadd }
4056b7b2d80SAdrian Chadd 
4066b7b2d80SAdrian Chadd /*
407155514eaSAndriy Gapon  * Set a pin to input (val is true) or output (val is false) mode.
4086b7b2d80SAdrian Chadd  */
4096b7b2d80SAdrian Chadd static void
410155514eaSAndriy Gapon nct_set_pin_input(struct nct_softc *sc, uint32_t pin_num, bool val)
4116b7b2d80SAdrian Chadd {
412155514eaSAndriy Gapon 	nct_set_pin_reg(sc, REG_IOR, pin_num, val);
4136b7b2d80SAdrian Chadd }
4146b7b2d80SAdrian Chadd 
4156b7b2d80SAdrian Chadd /*
4166b7b2d80SAdrian Chadd  * Check whether a pin is configured as an input.
4176b7b2d80SAdrian Chadd  */
4186b7b2d80SAdrian Chadd static bool
4196b7b2d80SAdrian Chadd nct_pin_is_input(struct nct_softc *sc, uint32_t pin_num)
4206b7b2d80SAdrian Chadd {
421155514eaSAndriy Gapon 	return (nct_get_pin_cache(sc, pin_num, sc->cache.ior));
4226b7b2d80SAdrian Chadd }
4236b7b2d80SAdrian Chadd 
4246b7b2d80SAdrian Chadd /*
425155514eaSAndriy Gapon  * Set a pin to inverted (val is true) or normal (val is false) mode.
4266b7b2d80SAdrian Chadd  */
4276b7b2d80SAdrian Chadd static void
428155514eaSAndriy Gapon nct_set_pin_inverted(struct nct_softc *sc, uint32_t pin_num, bool val)
4296b7b2d80SAdrian Chadd {
430155514eaSAndriy Gapon 	nct_set_pin_reg(sc, REG_INV, pin_num, val);
4316b7b2d80SAdrian Chadd }
4326b7b2d80SAdrian Chadd 
4336b7b2d80SAdrian Chadd static bool
4346b7b2d80SAdrian Chadd nct_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num)
4356b7b2d80SAdrian Chadd {
436155514eaSAndriy Gapon 	return (nct_get_pin_cache(sc, pin_num, sc->cache.inv));
4376b7b2d80SAdrian Chadd }
4386b7b2d80SAdrian Chadd 
439155514eaSAndriy Gapon /*
440155514eaSAndriy Gapon  * Write a value to an output pin.
441155514eaSAndriy Gapon  * NB: the hardware remembers last output value across switching from
442155514eaSAndriy Gapon  * output mode to input mode and back.
443155514eaSAndriy Gapon  * Writes to a pin in input mode are not allowed here as they cannot
444155514eaSAndriy Gapon  * have any effect and would corrupt the output value cache.
445155514eaSAndriy Gapon  */
446155514eaSAndriy Gapon static void
447155514eaSAndriy Gapon nct_write_pin(struct nct_softc *sc, uint32_t pin_num, bool val)
448155514eaSAndriy Gapon {
449155514eaSAndriy Gapon 	uint8_t bit;
450155514eaSAndriy Gapon 	uint8_t group;
451155514eaSAndriy Gapon 
452155514eaSAndriy Gapon 	KASSERT(!nct_pin_is_input(sc, pin_num), ("attempt to write input pin"));
453*7f8d2ed0SStéphane Rochoy 	group = NCT_PIN_GRPNUM(sc, pin_num);
454*7f8d2ed0SStéphane Rochoy 	bit   = NCT_PIN_BIT(sc, pin_num);
455*7f8d2ed0SStéphane Rochoy 
456155514eaSAndriy Gapon 	if (GET_BIT(sc->cache.out_known[group], bit) &&
457155514eaSAndriy Gapon 	    GET_BIT(sc->cache.out[group], bit) == val) {
458155514eaSAndriy Gapon 		/* The pin is already in requested state. */
459155514eaSAndriy Gapon 		return;
460155514eaSAndriy Gapon 	}
461155514eaSAndriy Gapon 	sc->cache.out_known[group] |= 1 << bit;
462155514eaSAndriy Gapon 	if (val)
463155514eaSAndriy Gapon 		sc->cache.out[group] |= 1 << bit;
464155514eaSAndriy Gapon 	else
465155514eaSAndriy Gapon 		sc->cache.out[group] &= ~(1 << bit);
466155514eaSAndriy Gapon 	nct_write_reg(sc, REG_DAT, group, sc->cache.out[group]);
467155514eaSAndriy Gapon }
468155514eaSAndriy Gapon 
469*7f8d2ed0SStéphane Rochoy static bool
470*7f8d2ed0SStéphane Rochoy nct_get_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num)
471*7f8d2ed0SStéphane Rochoy {
472*7f8d2ed0SStéphane Rochoy 	uint8_t            bit;
473*7f8d2ed0SStéphane Rochoy 	uint8_t            group;
474*7f8d2ed0SStéphane Rochoy 	uint8_t            val;
475*7f8d2ed0SStéphane Rochoy 	bool               b;
476*7f8d2ed0SStéphane Rochoy 
477*7f8d2ed0SStéphane Rochoy 	KASSERT(NCT_PIN_IS_VALID(sc, pin_num), ("%s: invalid pin number %d",
478*7f8d2ed0SStéphane Rochoy 			__func__, pin_num));
479*7f8d2ed0SStéphane Rochoy 
480*7f8d2ed0SStéphane Rochoy 	group = NCT_PIN_GRPNUM(sc, pin_num);
481*7f8d2ed0SStéphane Rochoy 	bit   = NCT_PIN_BIT(sc, pin_num);
482*7f8d2ed0SStéphane Rochoy 	val   = nct_read_reg(sc, reg, group);
483*7f8d2ed0SStéphane Rochoy 	b     = GET_BIT(val, bit);
484*7f8d2ed0SStéphane Rochoy 
485*7f8d2ed0SStéphane Rochoy 	if (__predict_false(bootverbose)) {
486*7f8d2ed0SStéphane Rochoy 		if (nct_pin_is_input(sc, pin_num))
487*7f8d2ed0SStéphane Rochoy 			NCT_VERBOSE_PRINTF(sc->dev, "read %d from input pin %u<GPIO%u%u>\n",
488*7f8d2ed0SStéphane Rochoy 				b, pin_num, group, bit);
489*7f8d2ed0SStéphane Rochoy 		else
490*7f8d2ed0SStéphane Rochoy 			NCT_VERBOSE_PRINTF(sc->dev,
491*7f8d2ed0SStéphane Rochoy 				"read %d from output pin %u<GPIO%u%u>, cache miss\n",
492*7f8d2ed0SStéphane Rochoy 				b, pin_num, group, bit);
493*7f8d2ed0SStéphane Rochoy 	}
494*7f8d2ed0SStéphane Rochoy 
495*7f8d2ed0SStéphane Rochoy 	return (b);
496*7f8d2ed0SStéphane Rochoy }
497*7f8d2ed0SStéphane Rochoy 
498155514eaSAndriy Gapon /*
499155514eaSAndriy Gapon  * NB: state of an input pin cannot be cached, of course.
500155514eaSAndriy Gapon  * For an output we can either take the value from the cache if it's valid
501155514eaSAndriy Gapon  * or read the state from the hadrware and cache it.
502155514eaSAndriy Gapon  */
503155514eaSAndriy Gapon static bool
504155514eaSAndriy Gapon nct_read_pin(struct nct_softc *sc, uint32_t pin_num)
505155514eaSAndriy Gapon {
506155514eaSAndriy Gapon 	uint8_t bit;
507155514eaSAndriy Gapon 	uint8_t group;
508155514eaSAndriy Gapon 	bool    val;
509155514eaSAndriy Gapon 
510*7f8d2ed0SStéphane Rochoy 	if (nct_pin_is_input(sc, pin_num)) {
511155514eaSAndriy Gapon 		return (nct_get_pin_reg(sc, REG_DAT, pin_num));
512*7f8d2ed0SStéphane Rochoy 	}
513155514eaSAndriy Gapon 
514*7f8d2ed0SStéphane Rochoy 	group = NCT_PIN_GRPNUM(sc, pin_num);
515*7f8d2ed0SStéphane Rochoy 	bit   = NCT_PIN_BIT(sc, pin_num);
516*7f8d2ed0SStéphane Rochoy 
517*7f8d2ed0SStéphane Rochoy 	if (GET_BIT(sc->cache.out_known[group], bit)) {
518*7f8d2ed0SStéphane Rochoy 		val = GET_BIT(sc->cache.out[group], bit);
519*7f8d2ed0SStéphane Rochoy 
520*7f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(sc->dev,
521*7f8d2ed0SStéphane Rochoy 			"read %d from output pin %u<GPIO%u%u>, cache hit\n",
522*7f8d2ed0SStéphane Rochoy 			val, pin_num, group, bit);
523*7f8d2ed0SStéphane Rochoy 
524*7f8d2ed0SStéphane Rochoy 		return (val);
525*7f8d2ed0SStéphane Rochoy 	}
526155514eaSAndriy Gapon 
527155514eaSAndriy Gapon 	val = nct_get_pin_reg(sc, REG_DAT, pin_num);
528155514eaSAndriy Gapon 	sc->cache.out_known[group] |= 1 << bit;
529155514eaSAndriy Gapon 	if (val)
530155514eaSAndriy Gapon 		sc->cache.out[group] |= 1 << bit;
531155514eaSAndriy Gapon 	else
532155514eaSAndriy Gapon 		sc->cache.out[group] &= ~(1 << bit);
533155514eaSAndriy Gapon 	return (val);
534155514eaSAndriy Gapon }
535155514eaSAndriy Gapon 
536155514eaSAndriy Gapon static uint8_t
537*7f8d2ed0SStéphane Rochoy nct_ppod_reg(struct nct_softc *sc, uint32_t pin_num)
538155514eaSAndriy Gapon {
539*7f8d2ed0SStéphane Rochoy 	uint8_t group = NCT_PIN_GRPNUM(sc, pin_num);
540*7f8d2ed0SStéphane Rochoy 
541*7f8d2ed0SStéphane Rochoy 	return (sc->grpmap[group]->ppod_reg);
542155514eaSAndriy Gapon }
543155514eaSAndriy Gapon 
544155514eaSAndriy Gapon /*
545155514eaSAndriy Gapon  * NB: PP/OD can be configured only via configuration registers.
546155514eaSAndriy Gapon  * Also, the registers are in a different logical device.
547155514eaSAndriy Gapon  * So, this is a special case.  No caching too.
548155514eaSAndriy Gapon  */
5496b7b2d80SAdrian Chadd static void
5506b7b2d80SAdrian Chadd nct_set_pin_opendrain(struct nct_softc *sc, uint32_t pin_num)
5516b7b2d80SAdrian Chadd {
5526b7b2d80SAdrian Chadd 	uint8_t reg;
5536b7b2d80SAdrian Chadd 	uint8_t outcfg;
5546b7b2d80SAdrian Chadd 
555*7f8d2ed0SStéphane Rochoy 	reg = nct_ppod_reg(sc, pin_num);
556*7f8d2ed0SStéphane Rochoy 	outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg);
557*7f8d2ed0SStéphane Rochoy 	outcfg |= NCT_PIN_BITMASK(pin_num);
558*7f8d2ed0SStéphane Rochoy 	superio_ldn_write(sc->dev, 0xf, reg, outcfg);
5596b7b2d80SAdrian Chadd }
5606b7b2d80SAdrian Chadd 
5616b7b2d80SAdrian Chadd static void
5626b7b2d80SAdrian Chadd nct_set_pin_pushpull(struct nct_softc *sc, uint32_t pin_num)
5636b7b2d80SAdrian Chadd {
5646b7b2d80SAdrian Chadd 	uint8_t reg;
5656b7b2d80SAdrian Chadd 	uint8_t outcfg;
5666b7b2d80SAdrian Chadd 
567*7f8d2ed0SStéphane Rochoy 	reg = nct_ppod_reg(sc, pin_num);
568*7f8d2ed0SStéphane Rochoy 	outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg);
569*7f8d2ed0SStéphane Rochoy 	outcfg &= ~NCT_PIN_BITMASK(pin_num);
570*7f8d2ed0SStéphane Rochoy 	superio_ldn_write(sc->dev, 0xf, reg, outcfg);
5716b7b2d80SAdrian Chadd }
5726b7b2d80SAdrian Chadd 
5736b7b2d80SAdrian Chadd static bool
5746b7b2d80SAdrian Chadd nct_pin_is_opendrain(struct nct_softc *sc, uint32_t pin_num)
5756b7b2d80SAdrian Chadd {
5766b7b2d80SAdrian Chadd 	uint8_t reg;
5776b7b2d80SAdrian Chadd 	uint8_t outcfg;
5786b7b2d80SAdrian Chadd 
579*7f8d2ed0SStéphane Rochoy 	reg = nct_ppod_reg(sc, pin_num);
580*7f8d2ed0SStéphane Rochoy 	outcfg = superio_ldn_read(sc->dev, NCT_PPOD_LDN, reg);
581*7f8d2ed0SStéphane Rochoy 	return (outcfg & NCT_PIN_BITMASK(pin_num));
582*7f8d2ed0SStéphane Rochoy }
583*7f8d2ed0SStéphane Rochoy 
584*7f8d2ed0SStéphane Rochoy static struct nct_device *
585*7f8d2ed0SStéphane Rochoy nct_lookup_device(device_t dev)
586*7f8d2ed0SStéphane Rochoy {
587*7f8d2ed0SStéphane Rochoy 	struct nct_device *nctdevp;
588*7f8d2ed0SStéphane Rochoy 	uint16_t           devid;
589*7f8d2ed0SStéphane Rochoy 	int                i, extid;
590*7f8d2ed0SStéphane Rochoy 
591*7f8d2ed0SStéphane Rochoy 	devid = superio_devid(dev);
592*7f8d2ed0SStéphane Rochoy 	extid = superio_extid(dev);
593*7f8d2ed0SStéphane Rochoy 	for (i = 0, nctdevp = nct_devices; i < nitems(nct_devices); i++, nctdevp++) {
594*7f8d2ed0SStéphane Rochoy 		if (devid == nctdevp->devid && nctdevp->extid == extid)
595*7f8d2ed0SStéphane Rochoy 			return (nctdevp);
596*7f8d2ed0SStéphane Rochoy 	}
597*7f8d2ed0SStéphane Rochoy 	return (NULL);
5986b7b2d80SAdrian Chadd }
5996b7b2d80SAdrian Chadd 
6006b7b2d80SAdrian Chadd static int
6016b7b2d80SAdrian Chadd nct_probe(device_t dev)
6026b7b2d80SAdrian Chadd {
603*7f8d2ed0SStéphane Rochoy 	struct nct_device *nctdevp;
604*7f8d2ed0SStéphane Rochoy 	uint8_t            ldn;
6056b7b2d80SAdrian Chadd 
606*7f8d2ed0SStéphane Rochoy 	ldn = superio_get_ldn(dev);
6076b7b2d80SAdrian Chadd 
608*7f8d2ed0SStéphane Rochoy 	if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON) {
609*7f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not a Nuvoton device\n", ldn);
610e3df342aSAndriy Gapon 		return (ENXIO);
611*7f8d2ed0SStéphane Rochoy 	}
612*7f8d2ed0SStéphane Rochoy 	if (superio_get_type(dev) != SUPERIO_DEV_GPIO) {
613*7f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not a GPIO device\n", ldn);
614*7f8d2ed0SStéphane Rochoy 		return (ENXIO);
615*7f8d2ed0SStéphane Rochoy 	}
6166b7b2d80SAdrian Chadd 
617*7f8d2ed0SStéphane Rochoy 	nctdevp = nct_lookup_device(dev);
618*7f8d2ed0SStéphane Rochoy 	if (nctdevp == NULL) {
619*7f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(dev, "ldn 0x%x not supported\n", ldn);
620*7f8d2ed0SStéphane Rochoy 		return (ENXIO);
621*7f8d2ed0SStéphane Rochoy 	}
622*7f8d2ed0SStéphane Rochoy 	device_set_desc(dev, nctdevp->descr);
6236b7b2d80SAdrian Chadd 	return (BUS_PROBE_DEFAULT);
6246b7b2d80SAdrian Chadd }
6256b7b2d80SAdrian Chadd 
6266b7b2d80SAdrian Chadd static int
6276b7b2d80SAdrian Chadd nct_attach(device_t dev)
6286b7b2d80SAdrian Chadd {
6296b7b2d80SAdrian Chadd 	struct nct_softc *sc;
630*7f8d2ed0SStéphane Rochoy 	struct nct_gpio_group *gp;
631*7f8d2ed0SStéphane Rochoy 	uint32_t pin_num;
632*7f8d2ed0SStéphane Rochoy 	uint8_t v;
633*7f8d2ed0SStéphane Rochoy 	int flags, i, g;
6346b7b2d80SAdrian Chadd 
6356b7b2d80SAdrian Chadd 	sc          = device_get_softc(dev);
636e3df342aSAndriy Gapon 	sc->dev     = dev;
637*7f8d2ed0SStéphane Rochoy 	sc->nctdevp = nct_lookup_device(dev);
638*7f8d2ed0SStéphane Rochoy 
639*7f8d2ed0SStéphane Rochoy 	flags = 0;
640*7f8d2ed0SStéphane Rochoy 	(void)resource_int_value(device_get_name(dev), device_get_unit(dev), "flags", &flags);
641*7f8d2ed0SStéphane Rochoy 
642*7f8d2ed0SStéphane Rochoy 	if ((flags & NCT_PREFER_INDIRECT_CHANNEL) == 0) {
643*7f8d2ed0SStéphane Rochoy 		uint16_t iobase;
644*7f8d2ed0SStéphane Rochoy 		device_t dev_8;
6456b7b2d80SAdrian Chadd 
646155514eaSAndriy Gapon 		/*
647155514eaSAndriy Gapon 		 * As strange as it may seem, I/O port base is configured in the
648155514eaSAndriy Gapon 		 * Logical Device 8 which is primarily used for WDT, but also plays
649155514eaSAndriy Gapon 		 * a role in GPIO configuration.
650155514eaSAndriy Gapon 		 */
651155514eaSAndriy Gapon 		iobase = 0;
652155514eaSAndriy Gapon 		dev_8 = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_WDT, 8);
653155514eaSAndriy Gapon 		if (dev_8 != NULL)
654155514eaSAndriy Gapon 			iobase = superio_get_iobase(dev_8);
655155514eaSAndriy Gapon 		if (iobase != 0 && iobase != 0xffff) {
656*7f8d2ed0SStéphane Rochoy 			int err;
657*7f8d2ed0SStéphane Rochoy 
658*7f8d2ed0SStéphane Rochoy 			NCT_VERBOSE_PRINTF(dev, "iobase %#x\n", iobase);
659155514eaSAndriy Gapon 			sc->curgrp = -1;
660155514eaSAndriy Gapon 			sc->iorid = 0;
661155514eaSAndriy Gapon 			err = bus_set_resource(dev, SYS_RES_IOPORT, sc->iorid,
662155514eaSAndriy Gapon 				iobase, 7);
663155514eaSAndriy Gapon 			if (err == 0) {
664155514eaSAndriy Gapon 				sc->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
665155514eaSAndriy Gapon 					&sc->iorid, RF_ACTIVE);
666155514eaSAndriy Gapon 				if (sc->iores == NULL) {
667155514eaSAndriy Gapon 					device_printf(dev, "can't map i/o space, "
668*7f8d2ed0SStéphane Rochoy 						"iobase=%#x\n", iobase);
669155514eaSAndriy Gapon 				}
670155514eaSAndriy Gapon 			} else {
671155514eaSAndriy Gapon 				device_printf(dev,
672*7f8d2ed0SStéphane Rochoy 					"failed to set io port resource at %#x\n", iobase);
673155514eaSAndriy Gapon 			}
674155514eaSAndriy Gapon 		}
675*7f8d2ed0SStéphane Rochoy 	}
676*7f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(dev, "iores %p %s channel\n",
677*7f8d2ed0SStéphane Rochoy 		sc->iores, (sc->iores ? "direct" : "indirect"));
678155514eaSAndriy Gapon 
679*7f8d2ed0SStéphane Rochoy 	/* Enable GPIO groups */
680*7f8d2ed0SStéphane Rochoy 	for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) {
681*7f8d2ed0SStéphane Rochoy 		NCT_VERBOSE_PRINTF(dev,
682*7f8d2ed0SStéphane Rochoy 			"GPIO%d: %d pins, enable with mask 0x%x via ldn 0x%x reg 0x%x\n",
683*7f8d2ed0SStéphane Rochoy 			gp->grpnum, gp->npins, gp->enable_mask, gp->enable_ldn,
684*7f8d2ed0SStéphane Rochoy 			gp->enable_reg);
685*7f8d2ed0SStéphane Rochoy 		v = superio_ldn_read(dev, gp->enable_ldn, gp->enable_reg);
686*7f8d2ed0SStéphane Rochoy 		v |= gp->enable_mask;
687*7f8d2ed0SStéphane Rochoy 		superio_ldn_write(dev, gp->enable_ldn, gp->enable_reg, v);
688*7f8d2ed0SStéphane Rochoy 	}
689e3df342aSAndriy Gapon 
690e3df342aSAndriy Gapon 	GPIO_LOCK_INIT(sc);
691e3df342aSAndriy Gapon 	GPIO_LOCK(sc);
6926b7b2d80SAdrian Chadd 
693*7f8d2ed0SStéphane Rochoy 	pin_num   = 0;
694*7f8d2ed0SStéphane Rochoy 	sc->npins = 0;
695*7f8d2ed0SStéphane Rochoy 	for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) {
696*7f8d2ed0SStéphane Rochoy 		sc->npins += gp->npins;
697*7f8d2ed0SStéphane Rochoy 		for (i = 0; i < gp->npins; i++, pin_num++) {
698*7f8d2ed0SStéphane Rochoy 			struct gpio_pin *pin;
699*7f8d2ed0SStéphane Rochoy 
700*7f8d2ed0SStéphane Rochoy 			sc->pinmap[pin_num].group  = gp;
701*7f8d2ed0SStéphane Rochoy 			sc->pinmap[pin_num].grpnum = gp->grpnum;
702*7f8d2ed0SStéphane Rochoy 			sc->pinmap[pin_num].bit    = gp->pinbits[i];
703*7f8d2ed0SStéphane Rochoy 
704*7f8d2ed0SStéphane Rochoy 			sc->grpmap[gp->grpnum] = gp;
705*7f8d2ed0SStéphane Rochoy 
706*7f8d2ed0SStéphane Rochoy 			pin           = &sc->pins[pin_num];
707*7f8d2ed0SStéphane Rochoy 			pin->gp_pin   = pin_num;
708*7f8d2ed0SStéphane Rochoy 			pin->gp_caps  = gp->caps;
709*7f8d2ed0SStéphane Rochoy 			pin->gp_flags = 0;
710*7f8d2ed0SStéphane Rochoy 
711*7f8d2ed0SStéphane Rochoy 			snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%u%u",
712*7f8d2ed0SStéphane Rochoy 				gp->grpnum, gp->pinbits[i]);
713*7f8d2ed0SStéphane Rochoy 
714*7f8d2ed0SStéphane Rochoy 			if (nct_pin_is_input(sc, pin_num))
715*7f8d2ed0SStéphane Rochoy 				pin->gp_flags |= GPIO_PIN_INPUT;
716*7f8d2ed0SStéphane Rochoy 			else
717*7f8d2ed0SStéphane Rochoy 				pin->gp_flags |= GPIO_PIN_OUTPUT;
718*7f8d2ed0SStéphane Rochoy 
719*7f8d2ed0SStéphane Rochoy 			if (nct_pin_is_opendrain(sc, pin_num))
720*7f8d2ed0SStéphane Rochoy 				pin->gp_flags |= GPIO_PIN_OPENDRAIN;
721*7f8d2ed0SStéphane Rochoy 			else
722*7f8d2ed0SStéphane Rochoy 				pin->gp_flags |= GPIO_PIN_PUSHPULL;
723*7f8d2ed0SStéphane Rochoy 
724*7f8d2ed0SStéphane Rochoy 			if (nct_pin_is_inverted(sc, pin_num))
725*7f8d2ed0SStéphane Rochoy 				pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
726*7f8d2ed0SStéphane Rochoy 		}
727*7f8d2ed0SStéphane Rochoy 	}
728*7f8d2ed0SStéphane Rochoy 	NCT_VERBOSE_PRINTF(dev, "%d pins available\n", sc->npins);
729155514eaSAndriy Gapon 
730155514eaSAndriy Gapon 	/*
731155514eaSAndriy Gapon 	 * Caching input values is meaningless as an input can be changed at any
732155514eaSAndriy Gapon 	 * time by an external agent.  But outputs are controlled by this
733155514eaSAndriy Gapon 	 * driver, so it can cache their state.  Also, the hardware remembers
734155514eaSAndriy Gapon 	 * the output state of a pin when the pin is switched to input mode and
735155514eaSAndriy Gapon 	 * then back to output mode.  So, the cache stays valid.
736155514eaSAndriy Gapon 	 * The only problem is with pins that are in input mode at the attach
737155514eaSAndriy Gapon 	 * time.  For them the output state is not known until it is set by the
738155514eaSAndriy Gapon 	 * driver for the first time.
739155514eaSAndriy Gapon 	 * 'out' and 'out_known' bits form a tri-state output cache:
740155514eaSAndriy Gapon 	 * |-----+-----------+---------|
741155514eaSAndriy Gapon 	 * | out | out_known | cache   |
742155514eaSAndriy Gapon 	 * |-----+-----------+---------|
743155514eaSAndriy Gapon 	 * |   X |         0 | invalid |
744155514eaSAndriy Gapon 	 * |   0 |         1 |       0 |
745155514eaSAndriy Gapon 	 * |   1 |         1 |       1 |
746155514eaSAndriy Gapon 	 * |-----+-----------+---------|
747155514eaSAndriy Gapon 	 */
748*7f8d2ed0SStéphane Rochoy 	for (g = 0, gp = sc->nctdevp->groups; g < sc->nctdevp->ngroups; g++, gp++) {
749*7f8d2ed0SStéphane Rochoy 		sc->cache.inv[gp->grpnum]       = nct_read_reg(sc, REG_INV, gp->grpnum);
750*7f8d2ed0SStéphane Rochoy 		sc->cache.ior[gp->grpnum]       = nct_read_reg(sc, REG_IOR, gp->grpnum);
751*7f8d2ed0SStéphane Rochoy 		sc->cache.out[gp->grpnum]       = nct_read_reg(sc, REG_DAT, gp->grpnum);
752*7f8d2ed0SStéphane Rochoy 		sc->cache.out_known[gp->grpnum] = ~sc->cache.ior[gp->grpnum];
7536b7b2d80SAdrian Chadd 	}
754*7f8d2ed0SStéphane Rochoy 
7556b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
7566b7b2d80SAdrian Chadd 
7576b7b2d80SAdrian Chadd 	sc->busdev = gpiobus_attach_bus(dev);
7586b7b2d80SAdrian Chadd 	if (sc->busdev == NULL) {
759*7f8d2ed0SStéphane Rochoy 		device_printf(dev, "failed to attach to gpiobus\n");
7606b7b2d80SAdrian Chadd 		GPIO_LOCK_DESTROY(sc);
7616b7b2d80SAdrian Chadd 		return (ENXIO);
7626b7b2d80SAdrian Chadd 	}
7636b7b2d80SAdrian Chadd 
7646b7b2d80SAdrian Chadd 	return (0);
7656b7b2d80SAdrian Chadd }
7666b7b2d80SAdrian Chadd 
7676b7b2d80SAdrian Chadd static int
7686b7b2d80SAdrian Chadd nct_detach(device_t dev)
7696b7b2d80SAdrian Chadd {
7706b7b2d80SAdrian Chadd 	struct nct_softc *sc;
7716b7b2d80SAdrian Chadd 
7726b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
7736b7b2d80SAdrian Chadd 	gpiobus_detach_bus(dev);
7746b7b2d80SAdrian Chadd 
775155514eaSAndriy Gapon 	if (sc->iores != NULL)
776155514eaSAndriy Gapon 		bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->iores);
7776b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
7786b7b2d80SAdrian Chadd 	GPIO_LOCK_DESTROY(sc);
7796b7b2d80SAdrian Chadd 
7806b7b2d80SAdrian Chadd 	return (0);
7816b7b2d80SAdrian Chadd }
7826b7b2d80SAdrian Chadd 
7836b7b2d80SAdrian Chadd static device_t
7846b7b2d80SAdrian Chadd nct_gpio_get_bus(device_t dev)
7856b7b2d80SAdrian Chadd {
7866b7b2d80SAdrian Chadd 	struct nct_softc *sc;
7876b7b2d80SAdrian Chadd 
7886b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
7896b7b2d80SAdrian Chadd 
7906b7b2d80SAdrian Chadd 	return (sc->busdev);
7916b7b2d80SAdrian Chadd }
7926b7b2d80SAdrian Chadd 
7936b7b2d80SAdrian Chadd static int
794*7f8d2ed0SStéphane Rochoy nct_gpio_pin_max(device_t dev, int *maxpin)
7956b7b2d80SAdrian Chadd {
796*7f8d2ed0SStéphane Rochoy 	struct nct_softc *sc;
7976b7b2d80SAdrian Chadd 
798*7f8d2ed0SStéphane Rochoy 	sc      = device_get_softc(dev);
799*7f8d2ed0SStéphane Rochoy 	*maxpin = sc->npins - 1;
8006b7b2d80SAdrian Chadd 	return (0);
8016b7b2d80SAdrian Chadd }
8026b7b2d80SAdrian Chadd 
8036b7b2d80SAdrian Chadd static int
8046b7b2d80SAdrian Chadd nct_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value)
8056b7b2d80SAdrian Chadd {
8066b7b2d80SAdrian Chadd 	struct nct_softc *sc;
8076b7b2d80SAdrian Chadd 
808*7f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
809*7f8d2ed0SStéphane Rochoy 
810*7f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
8116b7b2d80SAdrian Chadd 		return (EINVAL);
8126b7b2d80SAdrian Chadd 
8136b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
814155514eaSAndriy Gapon 	if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) {
815155514eaSAndriy Gapon 		GPIO_UNLOCK(sc);
816155514eaSAndriy Gapon 		return (EINVAL);
817155514eaSAndriy Gapon 	}
8186b7b2d80SAdrian Chadd 	nct_write_pin(sc, pin_num, pin_value);
8196b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
8206b7b2d80SAdrian Chadd 
8216b7b2d80SAdrian Chadd 	return (0);
8226b7b2d80SAdrian Chadd }
8236b7b2d80SAdrian Chadd 
8246b7b2d80SAdrian Chadd static int
8256b7b2d80SAdrian Chadd nct_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value)
8266b7b2d80SAdrian Chadd {
8276b7b2d80SAdrian Chadd 	struct nct_softc *sc;
8286b7b2d80SAdrian Chadd 
829*7f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
830*7f8d2ed0SStéphane Rochoy 
831*7f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
8326b7b2d80SAdrian Chadd 		return (EINVAL);
8336b7b2d80SAdrian Chadd 
8346b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
8356b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
8366b7b2d80SAdrian Chadd 	*pin_value = nct_read_pin(sc, pin_num);
8376b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
8386b7b2d80SAdrian Chadd 
8396b7b2d80SAdrian Chadd 	return (0);
8406b7b2d80SAdrian Chadd }
8416b7b2d80SAdrian Chadd 
8426b7b2d80SAdrian Chadd static int
8436b7b2d80SAdrian Chadd nct_gpio_pin_toggle(device_t dev, uint32_t pin_num)
8446b7b2d80SAdrian Chadd {
8456b7b2d80SAdrian Chadd 	struct nct_softc *sc;
8466b7b2d80SAdrian Chadd 
847*7f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
848*7f8d2ed0SStéphane Rochoy 
849*7f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
8506b7b2d80SAdrian Chadd 		return (EINVAL);
8516b7b2d80SAdrian Chadd 
8526b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
8536b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
854155514eaSAndriy Gapon 	if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) {
855155514eaSAndriy Gapon 		GPIO_UNLOCK(sc);
856155514eaSAndriy Gapon 		return (EINVAL);
857155514eaSAndriy Gapon 	}
8586b7b2d80SAdrian Chadd 	if (nct_read_pin(sc, pin_num))
8596b7b2d80SAdrian Chadd 		nct_write_pin(sc, pin_num, 0);
8606b7b2d80SAdrian Chadd 	else
8616b7b2d80SAdrian Chadd 		nct_write_pin(sc, pin_num, 1);
8626b7b2d80SAdrian Chadd 
8636b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
8646b7b2d80SAdrian Chadd 
8656b7b2d80SAdrian Chadd 	return (0);
8666b7b2d80SAdrian Chadd }
8676b7b2d80SAdrian Chadd 
8686b7b2d80SAdrian Chadd static int
8696b7b2d80SAdrian Chadd nct_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps)
8706b7b2d80SAdrian Chadd {
8716b7b2d80SAdrian Chadd 	struct nct_softc *sc;
8726b7b2d80SAdrian Chadd 
873*7f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
874*7f8d2ed0SStéphane Rochoy 
875*7f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
8766b7b2d80SAdrian Chadd 		return (EINVAL);
8776b7b2d80SAdrian Chadd 
8786b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
8796b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
8806b7b2d80SAdrian Chadd 	*caps = sc->pins[pin_num].gp_caps;
8816b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
8826b7b2d80SAdrian Chadd 
8836b7b2d80SAdrian Chadd 	return (0);
8846b7b2d80SAdrian Chadd }
8856b7b2d80SAdrian Chadd 
8866b7b2d80SAdrian Chadd static int
8876b7b2d80SAdrian Chadd nct_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags)
8886b7b2d80SAdrian Chadd {
8896b7b2d80SAdrian Chadd 	struct nct_softc *sc;
8906b7b2d80SAdrian Chadd 
891*7f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
892*7f8d2ed0SStéphane Rochoy 
893*7f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
8946b7b2d80SAdrian Chadd 		return (EINVAL);
8956b7b2d80SAdrian Chadd 
8966b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
8976b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
8986b7b2d80SAdrian Chadd 	*flags = sc->pins[pin_num].gp_flags;
8996b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
9006b7b2d80SAdrian Chadd 
9016b7b2d80SAdrian Chadd 	return (0);
9026b7b2d80SAdrian Chadd }
9036b7b2d80SAdrian Chadd 
9046b7b2d80SAdrian Chadd static int
9056b7b2d80SAdrian Chadd nct_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name)
9066b7b2d80SAdrian Chadd {
9076b7b2d80SAdrian Chadd 	struct nct_softc *sc;
9086b7b2d80SAdrian Chadd 
909*7f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
910*7f8d2ed0SStéphane Rochoy 
911*7f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
9126b7b2d80SAdrian Chadd 		return (EINVAL);
9136b7b2d80SAdrian Chadd 
9146b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
9156b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
9166b7b2d80SAdrian Chadd 	memcpy(name, sc->pins[pin_num].gp_name, GPIOMAXNAME);
9176b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
9186b7b2d80SAdrian Chadd 
9196b7b2d80SAdrian Chadd 	return (0);
9206b7b2d80SAdrian Chadd }
9216b7b2d80SAdrian Chadd 
9226b7b2d80SAdrian Chadd static int
9236b7b2d80SAdrian Chadd nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags)
9246b7b2d80SAdrian Chadd {
9256b7b2d80SAdrian Chadd 	struct nct_softc *sc;
9266b7b2d80SAdrian Chadd 	struct gpio_pin *pin;
9276b7b2d80SAdrian Chadd 
928*7f8d2ed0SStéphane Rochoy 	sc = device_get_softc(dev);
929*7f8d2ed0SStéphane Rochoy 
930*7f8d2ed0SStéphane Rochoy 	if (!NCT_PIN_IS_VALID(sc, pin_num))
9316b7b2d80SAdrian Chadd 		return (EINVAL);
9326b7b2d80SAdrian Chadd 
9336b7b2d80SAdrian Chadd 	pin = &sc->pins[pin_num];
9346b7b2d80SAdrian Chadd 	if ((flags & pin->gp_caps) != flags)
9356b7b2d80SAdrian Chadd 		return (EINVAL);
9366b7b2d80SAdrian Chadd 
9376b7b2d80SAdrian Chadd 	if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
9386b7b2d80SAdrian Chadd 		(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
9396b7b2d80SAdrian Chadd 			return (EINVAL);
9406b7b2d80SAdrian Chadd 	}
9416b7b2d80SAdrian Chadd 	if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) ==
9426b7b2d80SAdrian Chadd 		(GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) {
943155514eaSAndriy Gapon 			return (EINVAL);
944155514eaSAndriy Gapon 	}
945155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) ==
946155514eaSAndriy Gapon 		(GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) {
9476b7b2d80SAdrian Chadd 			return (EINVAL);
9486b7b2d80SAdrian Chadd 	}
9496b7b2d80SAdrian Chadd 
950155514eaSAndriy Gapon 	GPIO_ASSERT_UNLOCKED(sc);
951155514eaSAndriy Gapon 	GPIO_LOCK(sc);
952*7f8d2ed0SStéphane Rochoy 
953*7f8d2ed0SStéphane Rochoy 	/* input or output */
954155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) != 0) {
955155514eaSAndriy Gapon 		nct_set_pin_input(sc, pin_num, (flags & GPIO_PIN_INPUT) != 0);
956155514eaSAndriy Gapon 		pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
957155514eaSAndriy Gapon 		pin->gp_flags |= flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
958155514eaSAndriy Gapon 	}
959*7f8d2ed0SStéphane Rochoy 
960*7f8d2ed0SStéphane Rochoy 	/* invert */
961155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) != 0) {
962*7f8d2ed0SStéphane Rochoy 		nct_set_pin_inverted(sc, pin_num, 1);
963*7f8d2ed0SStéphane Rochoy 		pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
964*7f8d2ed0SStéphane Rochoy 	} else {
965*7f8d2ed0SStéphane Rochoy 		nct_set_pin_inverted(sc, pin_num, 0);
966155514eaSAndriy Gapon 		pin->gp_flags &= ~(GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
967155514eaSAndriy Gapon 	}
968*7f8d2ed0SStéphane Rochoy 
969*7f8d2ed0SStéphane Rochoy 	/* Open drain or push pull */
970155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) != 0) {
9716b7b2d80SAdrian Chadd 		if (flags & GPIO_PIN_OPENDRAIN)
9726b7b2d80SAdrian Chadd 			nct_set_pin_opendrain(sc, pin_num);
9736b7b2d80SAdrian Chadd 		else
9746b7b2d80SAdrian Chadd 			nct_set_pin_pushpull(sc, pin_num);
975155514eaSAndriy Gapon 		pin->gp_flags &= ~(GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL);
976155514eaSAndriy Gapon 		pin->gp_flags |=
977155514eaSAndriy Gapon 		    flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL);
9786b7b2d80SAdrian Chadd 	}
9796b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
9806b7b2d80SAdrian Chadd 
9816b7b2d80SAdrian Chadd 	return (0);
9826b7b2d80SAdrian Chadd }
9836b7b2d80SAdrian Chadd 
9846b7b2d80SAdrian Chadd static device_method_t nct_methods[] = {
9856b7b2d80SAdrian Chadd 	/* Device interface */
9866b7b2d80SAdrian Chadd 	DEVMETHOD(device_probe,		nct_probe),
9876b7b2d80SAdrian Chadd 	DEVMETHOD(device_attach,	nct_attach),
9886b7b2d80SAdrian Chadd 	DEVMETHOD(device_detach,	nct_detach),
9896b7b2d80SAdrian Chadd 
9906b7b2d80SAdrian Chadd 	/* GPIO */
9916b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_get_bus,		nct_gpio_get_bus),
9926b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_max,		nct_gpio_pin_max),
9936b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_get,		nct_gpio_pin_get),
9946b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_set,		nct_gpio_pin_set),
9956b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_toggle,	nct_gpio_pin_toggle),
9966b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getname,	nct_gpio_pin_getname),
9976b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getcaps,	nct_gpio_pin_getcaps),
9986b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getflags,	nct_gpio_pin_getflags),
9996b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_setflags,	nct_gpio_pin_setflags),
10006b7b2d80SAdrian Chadd 
10016b7b2d80SAdrian Chadd 	DEVMETHOD_END
10026b7b2d80SAdrian Chadd };
10036b7b2d80SAdrian Chadd 
1004e3df342aSAndriy Gapon static driver_t nct_driver = {
10056b7b2d80SAdrian Chadd 	"gpio",
10066b7b2d80SAdrian Chadd 	nct_methods,
10076b7b2d80SAdrian Chadd 	sizeof(struct nct_softc)
10086b7b2d80SAdrian Chadd };
10096b7b2d80SAdrian Chadd 
101084c5f982SJohn Baldwin DRIVER_MODULE(nctgpio, superio, nct_driver, NULL, NULL);
10116b7b2d80SAdrian Chadd MODULE_DEPEND(nctgpio, gpiobus, 1, 1, 1);
1012e3df342aSAndriy Gapon MODULE_DEPEND(nctgpio, superio, 1, 1, 1);
1013e3df342aSAndriy Gapon MODULE_VERSION(nctgpio, 1);
1014