xref: /freebsd/sys/dev/nctgpio/nctgpio.c (revision 155514eacf0efd907f1ce2d1fd961964968868b4)
16b7b2d80SAdrian Chadd /*-
26b7b2d80SAdrian Chadd  * Copyright (c) 2016 Daniel Wyatt <Daniel.Wyatt@gmail.com>
36b7b2d80SAdrian Chadd  * All rights reserved.
46b7b2d80SAdrian Chadd  *
56b7b2d80SAdrian Chadd  * Redistribution and use in source and binary forms, with or without
66b7b2d80SAdrian Chadd  * modification, are permitted provided that the following conditions
76b7b2d80SAdrian Chadd  * are met:
86b7b2d80SAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
96b7b2d80SAdrian Chadd  *    notice, this list of conditions and the following disclaimer.
106b7b2d80SAdrian Chadd  * 2. Redistributions in binary form must reproduce the above copyright
116b7b2d80SAdrian Chadd  *    notice, this list of conditions and the following disclaimer in the
126b7b2d80SAdrian Chadd  *    documentation and/or other materials provided with the distribution.
136b7b2d80SAdrian Chadd  *
146b7b2d80SAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
156b7b2d80SAdrian Chadd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
166b7b2d80SAdrian Chadd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
176b7b2d80SAdrian Chadd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
186b7b2d80SAdrian Chadd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
196b7b2d80SAdrian Chadd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
206b7b2d80SAdrian Chadd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
216b7b2d80SAdrian Chadd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
226b7b2d80SAdrian Chadd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
236b7b2d80SAdrian Chadd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
246b7b2d80SAdrian Chadd  * SUCH DAMAGE.
256b7b2d80SAdrian Chadd  *
266b7b2d80SAdrian Chadd  * $FreeBSD$
276b7b2d80SAdrian Chadd  *
286b7b2d80SAdrian Chadd  */
296b7b2d80SAdrian Chadd 
306b7b2d80SAdrian Chadd /*
316b7b2d80SAdrian Chadd  * Nuvoton GPIO driver.
326b7b2d80SAdrian Chadd  *
336b7b2d80SAdrian Chadd  */
346b7b2d80SAdrian Chadd 
356b7b2d80SAdrian Chadd #include <sys/cdefs.h>
366b7b2d80SAdrian Chadd 
376b7b2d80SAdrian Chadd #include <sys/param.h>
386b7b2d80SAdrian Chadd #include <sys/kernel.h>
396b7b2d80SAdrian Chadd #include <sys/systm.h>
406b7b2d80SAdrian Chadd #include <sys/bus.h>
416b7b2d80SAdrian Chadd #include <sys/eventhandler.h>
426b7b2d80SAdrian Chadd #include <sys/lock.h>
436b7b2d80SAdrian Chadd 
446b7b2d80SAdrian Chadd #include <sys/module.h>
456b7b2d80SAdrian Chadd #include <sys/gpio.h>
466b7b2d80SAdrian Chadd 
476b7b2d80SAdrian Chadd #include <machine/bus.h>
486b7b2d80SAdrian Chadd 
496b7b2d80SAdrian Chadd #include <dev/gpio/gpiobusvar.h>
50e3df342aSAndriy Gapon #include <dev/superio/superio.h>
516b7b2d80SAdrian Chadd 
526b7b2d80SAdrian Chadd #include "gpio_if.h"
536b7b2d80SAdrian Chadd 
546b7b2d80SAdrian Chadd /* Logical Device Numbers. */
556b7b2d80SAdrian Chadd #define NCT_LDN_GPIO			0x07
566b7b2d80SAdrian Chadd #define NCT_LDN_GPIO_MODE		0x0f
576b7b2d80SAdrian Chadd 
586b7b2d80SAdrian Chadd /* Logical Device 7 */
596b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_IOR		0xe0
606b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_DAT		0xe1
616b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_INV		0xe2
626b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_DST		0xe3
636b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_IOR		0xe4
646b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_DAT		0xe5
656b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_INV		0xe6
666b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_DST		0xe7
676b7b2d80SAdrian Chadd 
686b7b2d80SAdrian Chadd /* Logical Device F */
696b7b2d80SAdrian Chadd #define NCT_LDF_GPIO0_OUTCFG		0xe0
706b7b2d80SAdrian Chadd #define NCT_LDF_GPIO1_OUTCFG		0xe1
716b7b2d80SAdrian Chadd 
72*155514eaSAndriy Gapon /* Direct I/O port access. */
73*155514eaSAndriy Gapon #define	NCT_IO_GSR			0
74*155514eaSAndriy Gapon #define	NCT_IO_IOR			1
75*155514eaSAndriy Gapon #define	NCT_IO_DAT			2
76*155514eaSAndriy Gapon #define	NCT_IO_INV			3
776b7b2d80SAdrian Chadd 
786b7b2d80SAdrian Chadd #define NCT_MAX_PIN			15
796b7b2d80SAdrian Chadd #define NCT_IS_VALID_PIN(_p)	((_p) >= 0 && (_p) <= NCT_MAX_PIN)
806b7b2d80SAdrian Chadd 
81*155514eaSAndriy Gapon #define NCT_PIN_BIT(_p)         (1 << ((_p) & 7))
826b7b2d80SAdrian Chadd 
836b7b2d80SAdrian Chadd #define NCT_GPIO_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
846b7b2d80SAdrian Chadd 	GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \
856b7b2d80SAdrian Chadd 	GPIO_PIN_INVIN | GPIO_PIN_INVOUT)
866b7b2d80SAdrian Chadd 
87*155514eaSAndriy Gapon /*
88*155514eaSAndriy Gapon  * Note that the values are important.
89*155514eaSAndriy Gapon  * They match actual register offsets.
90*155514eaSAndriy Gapon  */
91*155514eaSAndriy Gapon typedef enum {
92*155514eaSAndriy Gapon 	REG_IOR = 0,
93*155514eaSAndriy Gapon 	REG_DAT = 1,
94*155514eaSAndriy Gapon 	REG_INV = 2,
95*155514eaSAndriy Gapon } reg_t;
96*155514eaSAndriy Gapon 
976b7b2d80SAdrian Chadd struct nct_softc {
986b7b2d80SAdrian Chadd 	device_t			dev;
99e3df342aSAndriy Gapon 	device_t			dev_f;
1006b7b2d80SAdrian Chadd 	device_t			busdev;
1016b7b2d80SAdrian Chadd 	struct mtx			mtx;
102*155514eaSAndriy Gapon 	struct resource			*iores;
103*155514eaSAndriy Gapon 	int				iorid;
104*155514eaSAndriy Gapon 	int				curgrp;
105*155514eaSAndriy Gapon 	struct {
106*155514eaSAndriy Gapon 		/* direction, 1: pin is input */
107*155514eaSAndriy Gapon 		uint8_t			ior[2];
108*155514eaSAndriy Gapon 		/* output value */
109*155514eaSAndriy Gapon 		uint8_t			out[2];
110*155514eaSAndriy Gapon 		/* whether out is valid */
111*155514eaSAndriy Gapon 		uint8_t			out_known[2];
112*155514eaSAndriy Gapon 		/* inversion, 1: pin is inverted */
113*155514eaSAndriy Gapon 		uint8_t			inv[2];
114*155514eaSAndriy Gapon 	} 				cache;
115523af57eSConrad Meyer 	struct gpio_pin			pins[NCT_MAX_PIN + 1];
1166b7b2d80SAdrian Chadd };
1176b7b2d80SAdrian Chadd 
1186b7b2d80SAdrian Chadd #define GPIO_LOCK_INIT(_sc)	mtx_init(&(_sc)->mtx,		\
1196b7b2d80SAdrian Chadd 		device_get_nameunit(dev), NULL, MTX_DEF)
1206b7b2d80SAdrian Chadd #define GPIO_LOCK_DESTROY(_sc)		mtx_destroy(&(_sc)->mtx)
1216b7b2d80SAdrian Chadd #define GPIO_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
1226b7b2d80SAdrian Chadd #define GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->mtx)
1236b7b2d80SAdrian Chadd #define GPIO_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_OWNED)
1246b7b2d80SAdrian Chadd #define GPIO_ASSERT_UNLOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_NOTOWNED)
1256b7b2d80SAdrian Chadd 
1266b7b2d80SAdrian Chadd struct nuvoton_vendor_device_id {
1276b7b2d80SAdrian Chadd 	uint16_t		chip_id;
1286b7b2d80SAdrian Chadd 	const char *		descr;
1296b7b2d80SAdrian Chadd } nct_devs[] = {
1306b7b2d80SAdrian Chadd 	{
1316b7b2d80SAdrian Chadd 		.chip_id	= 0x1061,
1326b7b2d80SAdrian Chadd 		.descr		= "Nuvoton NCT5104D",
1336b7b2d80SAdrian Chadd 	},
1346b7b2d80SAdrian Chadd 	{
1356b7b2d80SAdrian Chadd 		.chip_id	= 0xc452,
1366b7b2d80SAdrian Chadd 		.descr		= "Nuvoton NCT5104D (PC-Engines APU)",
1376b7b2d80SAdrian Chadd 	},
1389d1208bbSOleksandr Tymoshenko 	{
1399d1208bbSOleksandr Tymoshenko 		.chip_id	= 0xc453,
1409d1208bbSOleksandr Tymoshenko 		.descr		= "Nuvoton NCT5104D (PC-Engines APU3)",
1419d1208bbSOleksandr Tymoshenko 	},
1426b7b2d80SAdrian Chadd };
1436b7b2d80SAdrian Chadd 
144*155514eaSAndriy Gapon static void
145*155514eaSAndriy Gapon nct_io_set_group(struct nct_softc *sc, int group)
1466b7b2d80SAdrian Chadd {
1476b7b2d80SAdrian Chadd 
148*155514eaSAndriy Gapon 	GPIO_ASSERT_LOCKED(sc);
149*155514eaSAndriy Gapon 	if (group != sc->curgrp) {
150*155514eaSAndriy Gapon 		bus_write_1(sc->iores, NCT_IO_GSR, group);
151*155514eaSAndriy Gapon 		sc->curgrp = group;
152*155514eaSAndriy Gapon 	}
153*155514eaSAndriy Gapon }
1546b7b2d80SAdrian Chadd 
155*155514eaSAndriy Gapon static uint8_t
156*155514eaSAndriy Gapon nct_io_read(struct nct_softc *sc, int group, uint8_t reg)
157*155514eaSAndriy Gapon {
158*155514eaSAndriy Gapon 	nct_io_set_group(sc, group);
159*155514eaSAndriy Gapon 	return (bus_read_1(sc->iores, reg));
160*155514eaSAndriy Gapon }
161*155514eaSAndriy Gapon 
162*155514eaSAndriy Gapon static void
163*155514eaSAndriy Gapon nct_io_write(struct nct_softc *sc, int group, uint8_t reg, uint8_t val)
164*155514eaSAndriy Gapon {
165*155514eaSAndriy Gapon 	nct_io_set_group(sc, group);
166*155514eaSAndriy Gapon 	return (bus_write_1(sc->iores, reg, val));
167*155514eaSAndriy Gapon }
168*155514eaSAndriy Gapon 
169*155514eaSAndriy Gapon static uint8_t
170*155514eaSAndriy Gapon nct_get_ioreg(struct nct_softc *sc, reg_t reg, int group)
171*155514eaSAndriy Gapon {
172*155514eaSAndriy Gapon 	uint8_t ioreg;
173*155514eaSAndriy Gapon 
174*155514eaSAndriy Gapon 	if (sc->iores != NULL)
175*155514eaSAndriy Gapon 		ioreg = NCT_IO_IOR + reg;
176*155514eaSAndriy Gapon 	else if (group == 0)
177*155514eaSAndriy Gapon 		ioreg = NCT_LD7_GPIO0_IOR + reg;
178*155514eaSAndriy Gapon 	else
179*155514eaSAndriy Gapon 		ioreg = NCT_LD7_GPIO1_IOR + reg;
180*155514eaSAndriy Gapon 	return (ioreg);
181*155514eaSAndriy Gapon }
182*155514eaSAndriy Gapon 
183*155514eaSAndriy Gapon static uint8_t
184*155514eaSAndriy Gapon nct_read_reg(struct nct_softc *sc, reg_t reg, int group)
185*155514eaSAndriy Gapon {
186*155514eaSAndriy Gapon 	uint8_t ioreg;
187*155514eaSAndriy Gapon 	uint8_t val;
188*155514eaSAndriy Gapon 
189*155514eaSAndriy Gapon 	ioreg = nct_get_ioreg(sc, reg, group);
190*155514eaSAndriy Gapon 	if (sc->iores != NULL)
191*155514eaSAndriy Gapon 		val = nct_io_read(sc, group, ioreg);
192*155514eaSAndriy Gapon 	else
193*155514eaSAndriy Gapon 		val = superio_read(sc->dev, ioreg);
194*155514eaSAndriy Gapon 
195*155514eaSAndriy Gapon 	return (val);
196*155514eaSAndriy Gapon }
197*155514eaSAndriy Gapon 
198*155514eaSAndriy Gapon #define GET_BIT(v, b)	(((v) >> (b)) & 1)
199*155514eaSAndriy Gapon static bool
200*155514eaSAndriy Gapon nct_get_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num)
201*155514eaSAndriy Gapon {
202*155514eaSAndriy Gapon 	uint8_t bit;
203*155514eaSAndriy Gapon 	uint8_t group;
204*155514eaSAndriy Gapon 	uint8_t val;
205*155514eaSAndriy Gapon 
206*155514eaSAndriy Gapon 	KASSERT(NCT_IS_VALID_PIN(pin_num), ("%s: invalid pin number %d",
207*155514eaSAndriy Gapon 	    __func__, pin_num));
208*155514eaSAndriy Gapon 
209*155514eaSAndriy Gapon 	group = pin_num >> 3;
210*155514eaSAndriy Gapon 	bit = pin_num & 7;
211*155514eaSAndriy Gapon 	val = nct_read_reg(sc, reg, group);
212*155514eaSAndriy Gapon 	return (GET_BIT(val, bit));
213*155514eaSAndriy Gapon }
214*155514eaSAndriy Gapon 
215*155514eaSAndriy Gapon static int
216*155514eaSAndriy Gapon nct_get_pin_cache(struct nct_softc *sc, uint32_t pin_num, uint8_t *cache)
217*155514eaSAndriy Gapon {
218*155514eaSAndriy Gapon 	uint8_t bit;
219*155514eaSAndriy Gapon 	uint8_t group;
220*155514eaSAndriy Gapon 	uint8_t val;
221*155514eaSAndriy Gapon 
222*155514eaSAndriy Gapon 	KASSERT(NCT_IS_VALID_PIN(pin_num), ("%s: invalid pin number %d",
223*155514eaSAndriy Gapon 	    __func__, pin_num));
224*155514eaSAndriy Gapon 
225*155514eaSAndriy Gapon 	group = pin_num >> 3;
226*155514eaSAndriy Gapon 	bit = pin_num & 7;
227*155514eaSAndriy Gapon 	val = cache[group];
228*155514eaSAndriy Gapon 	return (GET_BIT(val, bit));
229*155514eaSAndriy Gapon }
230*155514eaSAndriy Gapon 
231*155514eaSAndriy Gapon static void
232*155514eaSAndriy Gapon nct_write_reg(struct nct_softc *sc, reg_t reg, int group, uint8_t val)
233*155514eaSAndriy Gapon {
234*155514eaSAndriy Gapon 	uint8_t ioreg;
235*155514eaSAndriy Gapon 
236*155514eaSAndriy Gapon 	ioreg = nct_get_ioreg(sc, reg, group);
237*155514eaSAndriy Gapon 	if (sc->iores != NULL)
238*155514eaSAndriy Gapon 		nct_io_write(sc, group, ioreg, val);
239*155514eaSAndriy Gapon 	else
240*155514eaSAndriy Gapon 		superio_write(sc->dev, ioreg, val);
241*155514eaSAndriy Gapon }
242*155514eaSAndriy Gapon 
243*155514eaSAndriy Gapon static void
244*155514eaSAndriy Gapon nct_set_pin_reg(struct nct_softc *sc, reg_t reg, uint32_t pin_num, bool val)
245*155514eaSAndriy Gapon {
246*155514eaSAndriy Gapon 	uint8_t *cache;
247*155514eaSAndriy Gapon 	uint8_t bit;
248*155514eaSAndriy Gapon 	uint8_t bitval;
249*155514eaSAndriy Gapon 	uint8_t group;
250*155514eaSAndriy Gapon 	uint8_t mask;
251*155514eaSAndriy Gapon 
252*155514eaSAndriy Gapon 	KASSERT(NCT_IS_VALID_PIN(pin_num),
253*155514eaSAndriy Gapon 	    ("%s: invalid pin number %d", __func__, pin_num));
254*155514eaSAndriy Gapon 	KASSERT(reg == REG_IOR || reg == REG_INV,
255*155514eaSAndriy Gapon 	    ("%s: unsupported register %d", __func__, reg));
256*155514eaSAndriy Gapon 
257*155514eaSAndriy Gapon 	group = pin_num >> 3;
258*155514eaSAndriy Gapon 	bit = pin_num & 7;
259*155514eaSAndriy Gapon 	mask = (uint8_t)1 << bit;
260*155514eaSAndriy Gapon 	bitval = (uint8_t)val << bit;
261*155514eaSAndriy Gapon 
262*155514eaSAndriy Gapon 	if (reg == REG_IOR)
263*155514eaSAndriy Gapon 		cache = &sc->cache.ior[group];
264*155514eaSAndriy Gapon 	else
265*155514eaSAndriy Gapon 		cache = &sc->cache.inv[group];
266*155514eaSAndriy Gapon 	if ((*cache & mask) == bitval)
267*155514eaSAndriy Gapon 		return;
268*155514eaSAndriy Gapon 	*cache &= ~mask;
269*155514eaSAndriy Gapon 	*cache |= bitval;
270*155514eaSAndriy Gapon 	nct_write_reg(sc, reg, group, *cache);
2716b7b2d80SAdrian Chadd }
2726b7b2d80SAdrian Chadd 
2736b7b2d80SAdrian Chadd /*
274*155514eaSAndriy Gapon  * Set a pin to input (val is true) or output (val is false) mode.
2756b7b2d80SAdrian Chadd  */
2766b7b2d80SAdrian Chadd static void
277*155514eaSAndriy Gapon nct_set_pin_input(struct nct_softc *sc, uint32_t pin_num, bool val)
2786b7b2d80SAdrian Chadd {
279*155514eaSAndriy Gapon 	nct_set_pin_reg(sc, REG_IOR, pin_num, val);
2806b7b2d80SAdrian Chadd }
2816b7b2d80SAdrian Chadd 
2826b7b2d80SAdrian Chadd /*
2836b7b2d80SAdrian Chadd  * Check whether a pin is configured as an input.
2846b7b2d80SAdrian Chadd  */
2856b7b2d80SAdrian Chadd static bool
2866b7b2d80SAdrian Chadd nct_pin_is_input(struct nct_softc *sc, uint32_t pin_num)
2876b7b2d80SAdrian Chadd {
288*155514eaSAndriy Gapon 	return (nct_get_pin_cache(sc, pin_num, sc->cache.ior));
2896b7b2d80SAdrian Chadd }
2906b7b2d80SAdrian Chadd 
2916b7b2d80SAdrian Chadd /*
292*155514eaSAndriy Gapon  * Set a pin to inverted (val is true) or normal (val is false) mode.
2936b7b2d80SAdrian Chadd  */
2946b7b2d80SAdrian Chadd static void
295*155514eaSAndriy Gapon nct_set_pin_inverted(struct nct_softc *sc, uint32_t pin_num, bool val)
2966b7b2d80SAdrian Chadd {
297*155514eaSAndriy Gapon 	nct_set_pin_reg(sc, REG_INV, pin_num, val);
2986b7b2d80SAdrian Chadd }
2996b7b2d80SAdrian Chadd 
3006b7b2d80SAdrian Chadd static bool
3016b7b2d80SAdrian Chadd nct_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num)
3026b7b2d80SAdrian Chadd {
303*155514eaSAndriy Gapon 	return (nct_get_pin_cache(sc, pin_num, sc->cache.inv));
3046b7b2d80SAdrian Chadd }
3056b7b2d80SAdrian Chadd 
306*155514eaSAndriy Gapon /*
307*155514eaSAndriy Gapon  * Write a value to an output pin.
308*155514eaSAndriy Gapon  * NB: the hardware remembers last output value across switching from
309*155514eaSAndriy Gapon  * output mode to input mode and back.
310*155514eaSAndriy Gapon  * Writes to a pin in input mode are not allowed here as they cannot
311*155514eaSAndriy Gapon  * have any effect and would corrupt the output value cache.
312*155514eaSAndriy Gapon  */
313*155514eaSAndriy Gapon static void
314*155514eaSAndriy Gapon nct_write_pin(struct nct_softc *sc, uint32_t pin_num, bool val)
315*155514eaSAndriy Gapon {
316*155514eaSAndriy Gapon 	uint8_t bit;
317*155514eaSAndriy Gapon 	uint8_t group;
318*155514eaSAndriy Gapon 
319*155514eaSAndriy Gapon 	KASSERT(!nct_pin_is_input(sc, pin_num), ("attempt to write input pin"));
320*155514eaSAndriy Gapon 	group = pin_num >> 3;
321*155514eaSAndriy Gapon 	bit = pin_num & 7;
322*155514eaSAndriy Gapon 	if (GET_BIT(sc->cache.out_known[group], bit) &&
323*155514eaSAndriy Gapon 	    GET_BIT(sc->cache.out[group], bit) == val) {
324*155514eaSAndriy Gapon 		/* The pin is already in requested state. */
325*155514eaSAndriy Gapon 		return;
326*155514eaSAndriy Gapon 	}
327*155514eaSAndriy Gapon 	sc->cache.out_known[group] |= 1 << bit;
328*155514eaSAndriy Gapon 	if (val)
329*155514eaSAndriy Gapon 		sc->cache.out[group] |= 1 << bit;
330*155514eaSAndriy Gapon 	else
331*155514eaSAndriy Gapon 		sc->cache.out[group] &= ~(1 << bit);
332*155514eaSAndriy Gapon 	nct_write_reg(sc, REG_DAT, group, sc->cache.out[group]);
333*155514eaSAndriy Gapon }
334*155514eaSAndriy Gapon 
335*155514eaSAndriy Gapon /*
336*155514eaSAndriy Gapon  * NB: state of an input pin cannot be cached, of course.
337*155514eaSAndriy Gapon  * For an output we can either take the value from the cache if it's valid
338*155514eaSAndriy Gapon  * or read the state from the hadrware and cache it.
339*155514eaSAndriy Gapon  */
340*155514eaSAndriy Gapon static bool
341*155514eaSAndriy Gapon nct_read_pin(struct nct_softc *sc, uint32_t pin_num)
342*155514eaSAndriy Gapon {
343*155514eaSAndriy Gapon 	uint8_t bit;
344*155514eaSAndriy Gapon 	uint8_t group;
345*155514eaSAndriy Gapon 	bool val;
346*155514eaSAndriy Gapon 
347*155514eaSAndriy Gapon 	if (nct_pin_is_input(sc, pin_num))
348*155514eaSAndriy Gapon 		return (nct_get_pin_reg(sc, REG_DAT, pin_num));
349*155514eaSAndriy Gapon 
350*155514eaSAndriy Gapon 	group = pin_num >> 3;
351*155514eaSAndriy Gapon 	bit = pin_num & 7;
352*155514eaSAndriy Gapon 	if (GET_BIT(sc->cache.out_known[group], bit))
353*155514eaSAndriy Gapon 		return (GET_BIT(sc->cache.out[group], bit));
354*155514eaSAndriy Gapon 
355*155514eaSAndriy Gapon 	val = nct_get_pin_reg(sc, REG_DAT, pin_num);
356*155514eaSAndriy Gapon 	sc->cache.out_known[group] |= 1 << bit;
357*155514eaSAndriy Gapon 	if (val)
358*155514eaSAndriy Gapon 		sc->cache.out[group] |= 1 << bit;
359*155514eaSAndriy Gapon 	else
360*155514eaSAndriy Gapon 		sc->cache.out[group] &= ~(1 << bit);
361*155514eaSAndriy Gapon 	return (val);
362*155514eaSAndriy Gapon }
363*155514eaSAndriy Gapon 
364*155514eaSAndriy Gapon static uint8_t
365*155514eaSAndriy Gapon nct_outcfg_addr(uint32_t pin_num)
366*155514eaSAndriy Gapon {
367*155514eaSAndriy Gapon 	KASSERT(NCT_IS_VALID_PIN(pin_num), ("%s: invalid pin number %d",
368*155514eaSAndriy Gapon 	    __func__, pin_num));
369*155514eaSAndriy Gapon 	if ((pin_num >> 3) == 0)
370*155514eaSAndriy Gapon 		return (NCT_LDF_GPIO0_OUTCFG);
371*155514eaSAndriy Gapon 	else
372*155514eaSAndriy Gapon 		return (NCT_LDF_GPIO1_OUTCFG);
373*155514eaSAndriy Gapon }
374*155514eaSAndriy Gapon 
375*155514eaSAndriy Gapon /*
376*155514eaSAndriy Gapon  * NB: PP/OD can be configured only via configuration registers.
377*155514eaSAndriy Gapon  * Also, the registers are in a different logical device.
378*155514eaSAndriy Gapon  * So, this is a special case.  No caching too.
379*155514eaSAndriy Gapon  */
3806b7b2d80SAdrian Chadd static void
3816b7b2d80SAdrian Chadd nct_set_pin_opendrain(struct nct_softc *sc, uint32_t pin_num)
3826b7b2d80SAdrian Chadd {
3836b7b2d80SAdrian Chadd 	uint8_t reg;
3846b7b2d80SAdrian Chadd 	uint8_t outcfg;
3856b7b2d80SAdrian Chadd 
3866b7b2d80SAdrian Chadd 	reg = nct_outcfg_addr(pin_num);
387e3df342aSAndriy Gapon 	outcfg = superio_read(sc->dev_f, reg);
388e3df342aSAndriy Gapon 	outcfg |= NCT_PIN_BIT(pin_num);
389e3df342aSAndriy Gapon 	superio_write(sc->dev_f, reg, outcfg);
3906b7b2d80SAdrian Chadd }
3916b7b2d80SAdrian Chadd 
3926b7b2d80SAdrian Chadd static void
3936b7b2d80SAdrian Chadd nct_set_pin_pushpull(struct nct_softc *sc, uint32_t pin_num)
3946b7b2d80SAdrian Chadd {
3956b7b2d80SAdrian Chadd 	uint8_t reg;
3966b7b2d80SAdrian Chadd 	uint8_t outcfg;
3976b7b2d80SAdrian Chadd 
3986b7b2d80SAdrian Chadd 	reg = nct_outcfg_addr(pin_num);
399e3df342aSAndriy Gapon 	outcfg = superio_read(sc->dev_f, reg);
400e3df342aSAndriy Gapon 	outcfg &= ~NCT_PIN_BIT(pin_num);
401e3df342aSAndriy Gapon 	superio_write(sc->dev_f, reg, outcfg);
4026b7b2d80SAdrian Chadd }
4036b7b2d80SAdrian Chadd 
4046b7b2d80SAdrian Chadd static bool
4056b7b2d80SAdrian Chadd nct_pin_is_opendrain(struct nct_softc *sc, uint32_t pin_num)
4066b7b2d80SAdrian Chadd {
4076b7b2d80SAdrian Chadd 	uint8_t reg;
4086b7b2d80SAdrian Chadd 	uint8_t outcfg;
4096b7b2d80SAdrian Chadd 
4106b7b2d80SAdrian Chadd 	reg = nct_outcfg_addr(pin_num);
411e3df342aSAndriy Gapon 	outcfg = superio_read(sc->dev_f, reg);
4126b7b2d80SAdrian Chadd 	return (outcfg & NCT_PIN_BIT(pin_num));
4136b7b2d80SAdrian Chadd }
4146b7b2d80SAdrian Chadd 
4156b7b2d80SAdrian Chadd static int
4166b7b2d80SAdrian Chadd nct_probe(device_t dev)
4176b7b2d80SAdrian Chadd {
418e3df342aSAndriy Gapon 	int j;
4196b7b2d80SAdrian Chadd 	uint16_t chipid;
4206b7b2d80SAdrian Chadd 
421e3df342aSAndriy Gapon 	if (superio_vendor(dev) != SUPERIO_VENDOR_NUVOTON)
422e3df342aSAndriy Gapon 		return (ENXIO);
423e3df342aSAndriy Gapon 	if (superio_get_type(dev) != SUPERIO_DEV_GPIO)
4246b7b2d80SAdrian Chadd 		return (ENXIO);
4256b7b2d80SAdrian Chadd 
426e3df342aSAndriy Gapon 	/*
427e3df342aSAndriy Gapon 	 * There are several GPIO devices, we attach only to one of them
428e3df342aSAndriy Gapon 	 * and use the rest without attaching.
429e3df342aSAndriy Gapon 	 */
430e3df342aSAndriy Gapon 	if (superio_get_ldn(dev) != NCT_LDN_GPIO)
431e3df342aSAndriy Gapon 		return (ENXIO);
4326b7b2d80SAdrian Chadd 
433e3df342aSAndriy Gapon 	chipid = superio_devid(dev);
43473a1170aSPedro F. Giffuni 	for (j = 0; j < nitems(nct_devs); j++) {
4356b7b2d80SAdrian Chadd 		if (chipid == nct_devs[j].chip_id) {
436e3df342aSAndriy Gapon 			device_set_desc(dev, "Nuvoton GPIO controller");
4376b7b2d80SAdrian Chadd 			return (BUS_PROBE_DEFAULT);
4386b7b2d80SAdrian Chadd 		}
4396b7b2d80SAdrian Chadd 	}
4406b7b2d80SAdrian Chadd 	return (ENXIO);
4416b7b2d80SAdrian Chadd }
4426b7b2d80SAdrian Chadd 
4436b7b2d80SAdrian Chadd static int
4446b7b2d80SAdrian Chadd nct_attach(device_t dev)
4456b7b2d80SAdrian Chadd {
4466b7b2d80SAdrian Chadd 	struct nct_softc *sc;
447*155514eaSAndriy Gapon 	device_t dev_8;
448*155514eaSAndriy Gapon 	uint16_t iobase;
449*155514eaSAndriy Gapon 	int err;
4506b7b2d80SAdrian Chadd 	int i;
4516b7b2d80SAdrian Chadd 
4526b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
453e3df342aSAndriy Gapon 	sc->dev = dev;
454e3df342aSAndriy Gapon 	sc->dev_f = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_GPIO,
455e3df342aSAndriy Gapon 	    NCT_LDN_GPIO_MODE);
456e3df342aSAndriy Gapon 	if (sc->dev_f == NULL) {
457e3df342aSAndriy Gapon 		device_printf(dev, "failed to find LDN F\n");
4586b7b2d80SAdrian Chadd 		return (ENXIO);
4596b7b2d80SAdrian Chadd 	}
4606b7b2d80SAdrian Chadd 
461*155514eaSAndriy Gapon 	/*
462*155514eaSAndriy Gapon 	 * As strange as it may seem, I/O port base is configured in the
463*155514eaSAndriy Gapon 	 * Logical Device 8 which is primarily used for WDT, but also plays
464*155514eaSAndriy Gapon 	 * a role in GPIO configuration.
465*155514eaSAndriy Gapon 	 */
466*155514eaSAndriy Gapon 	iobase = 0;
467*155514eaSAndriy Gapon 	dev_8 = superio_find_dev(device_get_parent(dev), SUPERIO_DEV_WDT, 8);
468*155514eaSAndriy Gapon 	if (dev_8 != NULL)
469*155514eaSAndriy Gapon 		iobase = superio_get_iobase(dev_8);
470*155514eaSAndriy Gapon 	if (iobase != 0 && iobase != 0xffff) {
471*155514eaSAndriy Gapon 		sc->curgrp = -1;
472*155514eaSAndriy Gapon 		sc->iorid = 0;
473*155514eaSAndriy Gapon 		err = bus_set_resource(dev, SYS_RES_IOPORT, sc->iorid,
474*155514eaSAndriy Gapon 		    iobase, 7);
475*155514eaSAndriy Gapon 		if (err == 0) {
476*155514eaSAndriy Gapon 			sc->iores = bus_alloc_resource_any(dev, SYS_RES_IOPORT,
477*155514eaSAndriy Gapon 			    &sc->iorid, RF_ACTIVE);
478*155514eaSAndriy Gapon 			if (sc->iores == NULL) {
479*155514eaSAndriy Gapon 				device_printf(dev, "can't map i/o space, "
480*155514eaSAndriy Gapon 				    "iobase=0x%04x\n", iobase);
481*155514eaSAndriy Gapon 			}
482*155514eaSAndriy Gapon 		} else {
483*155514eaSAndriy Gapon 			device_printf(dev,
484*155514eaSAndriy Gapon 			    "failed to set io port resource at 0x%x\n", iobase);
485*155514eaSAndriy Gapon 		}
486*155514eaSAndriy Gapon 	}
487*155514eaSAndriy Gapon 
4886b7b2d80SAdrian Chadd 	/* Enable gpio0 and gpio1. */
489e3df342aSAndriy Gapon 	superio_dev_enable(dev, 0x03);
490e3df342aSAndriy Gapon 
491e3df342aSAndriy Gapon 	GPIO_LOCK_INIT(sc);
492e3df342aSAndriy Gapon 	GPIO_LOCK(sc);
4936b7b2d80SAdrian Chadd 
494*155514eaSAndriy Gapon 	sc->cache.inv[0] = nct_read_reg(sc, REG_INV, 0);
495*155514eaSAndriy Gapon 	sc->cache.inv[1] = nct_read_reg(sc, REG_INV, 1);
496*155514eaSAndriy Gapon 	sc->cache.ior[0] = nct_read_reg(sc, REG_IOR, 0);
497*155514eaSAndriy Gapon 	sc->cache.ior[1] = nct_read_reg(sc, REG_IOR, 1);
498*155514eaSAndriy Gapon 
499*155514eaSAndriy Gapon 	/*
500*155514eaSAndriy Gapon 	 * Caching input values is meaningless as an input can be changed at any
501*155514eaSAndriy Gapon 	 * time by an external agent.  But outputs are controlled by this
502*155514eaSAndriy Gapon 	 * driver, so it can cache their state.  Also, the hardware remembers
503*155514eaSAndriy Gapon 	 * the output state of a pin when the pin is switched to input mode and
504*155514eaSAndriy Gapon 	 * then back to output mode.  So, the cache stays valid.
505*155514eaSAndriy Gapon 	 * The only problem is with pins that are in input mode at the attach
506*155514eaSAndriy Gapon 	 * time.  For them the output state is not known until it is set by the
507*155514eaSAndriy Gapon 	 * driver for the first time.
508*155514eaSAndriy Gapon 	 * 'out' and 'out_known' bits form a tri-state output cache:
509*155514eaSAndriy Gapon 	 * |-----+-----------+---------|
510*155514eaSAndriy Gapon 	 * | out | out_known | cache   |
511*155514eaSAndriy Gapon 	 * |-----+-----------+---------|
512*155514eaSAndriy Gapon 	 * |   X |         0 | invalid |
513*155514eaSAndriy Gapon 	 * |   0 |         1 |       0 |
514*155514eaSAndriy Gapon 	 * |   1 |         1 |       1 |
515*155514eaSAndriy Gapon 	 * |-----+-----------+---------|
516*155514eaSAndriy Gapon 	 */
517*155514eaSAndriy Gapon 	sc->cache.out[0] = nct_read_reg(sc, REG_DAT, 0);
518*155514eaSAndriy Gapon 	sc->cache.out[1] = nct_read_reg(sc, REG_DAT, 1);
519*155514eaSAndriy Gapon 	sc->cache.out_known[0] = ~sc->cache.ior[0];
520*155514eaSAndriy Gapon 	sc->cache.out_known[1] = ~sc->cache.ior[1];
521*155514eaSAndriy Gapon 
5226b7b2d80SAdrian Chadd 	for (i = 0; i <= NCT_MAX_PIN; i++) {
5236b7b2d80SAdrian Chadd 		struct gpio_pin *pin;
5246b7b2d80SAdrian Chadd 
5256b7b2d80SAdrian Chadd 		pin = &sc->pins[i];
5266b7b2d80SAdrian Chadd 		pin->gp_pin = i;
5276b7b2d80SAdrian Chadd 		pin->gp_caps = NCT_GPIO_CAPS;
5286b7b2d80SAdrian Chadd 		pin->gp_flags = 0;
5296b7b2d80SAdrian Chadd 
53002226256SAndriy Gapon 		snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%02o", i);
5316b7b2d80SAdrian Chadd 		pin->gp_name[GPIOMAXNAME - 1] = '\0';
5326b7b2d80SAdrian Chadd 
5336b7b2d80SAdrian Chadd 		if (nct_pin_is_input(sc, i))
5346b7b2d80SAdrian Chadd 			pin->gp_flags |= GPIO_PIN_INPUT;
5356b7b2d80SAdrian Chadd 		else
5366b7b2d80SAdrian Chadd 			pin->gp_flags |= GPIO_PIN_OUTPUT;
5376b7b2d80SAdrian Chadd 
5386b7b2d80SAdrian Chadd 		if (nct_pin_is_opendrain(sc, i))
5396b7b2d80SAdrian Chadd 			pin->gp_flags |= GPIO_PIN_OPENDRAIN;
5406b7b2d80SAdrian Chadd 		else
5416b7b2d80SAdrian Chadd 			pin->gp_flags |= GPIO_PIN_PUSHPULL;
5426b7b2d80SAdrian Chadd 
5436b7b2d80SAdrian Chadd 		if (nct_pin_is_inverted(sc, i))
5446b7b2d80SAdrian Chadd 			pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
5456b7b2d80SAdrian Chadd 	}
5466b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
5476b7b2d80SAdrian Chadd 
5486b7b2d80SAdrian Chadd 	sc->busdev = gpiobus_attach_bus(dev);
5496b7b2d80SAdrian Chadd 	if (sc->busdev == NULL) {
5506b7b2d80SAdrian Chadd 		GPIO_LOCK_DESTROY(sc);
5516b7b2d80SAdrian Chadd 		return (ENXIO);
5526b7b2d80SAdrian Chadd 	}
5536b7b2d80SAdrian Chadd 
5546b7b2d80SAdrian Chadd 	return (0);
5556b7b2d80SAdrian Chadd }
5566b7b2d80SAdrian Chadd 
5576b7b2d80SAdrian Chadd static int
5586b7b2d80SAdrian Chadd nct_detach(device_t dev)
5596b7b2d80SAdrian Chadd {
5606b7b2d80SAdrian Chadd 	struct nct_softc *sc;
5616b7b2d80SAdrian Chadd 
5626b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
5636b7b2d80SAdrian Chadd 	gpiobus_detach_bus(dev);
5646b7b2d80SAdrian Chadd 
565*155514eaSAndriy Gapon 	if (sc->iores != NULL)
566*155514eaSAndriy Gapon 		bus_release_resource(dev, SYS_RES_IOPORT, sc->iorid, sc->iores);
5676b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
5686b7b2d80SAdrian Chadd 	GPIO_LOCK_DESTROY(sc);
5696b7b2d80SAdrian Chadd 
5706b7b2d80SAdrian Chadd 	return (0);
5716b7b2d80SAdrian Chadd }
5726b7b2d80SAdrian Chadd 
5736b7b2d80SAdrian Chadd static device_t
5746b7b2d80SAdrian Chadd nct_gpio_get_bus(device_t dev)
5756b7b2d80SAdrian Chadd {
5766b7b2d80SAdrian Chadd 	struct nct_softc *sc;
5776b7b2d80SAdrian Chadd 
5786b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
5796b7b2d80SAdrian Chadd 
5806b7b2d80SAdrian Chadd 	return (sc->busdev);
5816b7b2d80SAdrian Chadd }
5826b7b2d80SAdrian Chadd 
5836b7b2d80SAdrian Chadd static int
5846b7b2d80SAdrian Chadd nct_gpio_pin_max(device_t dev, int *npins)
5856b7b2d80SAdrian Chadd {
5866b7b2d80SAdrian Chadd 	*npins = NCT_MAX_PIN;
5876b7b2d80SAdrian Chadd 
5886b7b2d80SAdrian Chadd 	return (0);
5896b7b2d80SAdrian Chadd }
5906b7b2d80SAdrian Chadd 
5916b7b2d80SAdrian Chadd static int
5926b7b2d80SAdrian Chadd nct_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value)
5936b7b2d80SAdrian Chadd {
5946b7b2d80SAdrian Chadd 	struct nct_softc *sc;
5956b7b2d80SAdrian Chadd 
5966b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
5976b7b2d80SAdrian Chadd 		return (EINVAL);
5986b7b2d80SAdrian Chadd 
5996b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
6006b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
601*155514eaSAndriy Gapon 	if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) {
602*155514eaSAndriy Gapon 		GPIO_UNLOCK(sc);
603*155514eaSAndriy Gapon 		return (EINVAL);
604*155514eaSAndriy Gapon 	}
6056b7b2d80SAdrian Chadd 	nct_write_pin(sc, pin_num, pin_value);
6066b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
6076b7b2d80SAdrian Chadd 
6086b7b2d80SAdrian Chadd 	return (0);
6096b7b2d80SAdrian Chadd }
6106b7b2d80SAdrian Chadd 
6116b7b2d80SAdrian Chadd static int
6126b7b2d80SAdrian Chadd nct_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value)
6136b7b2d80SAdrian Chadd {
6146b7b2d80SAdrian Chadd 	struct nct_softc *sc;
6156b7b2d80SAdrian Chadd 
6166b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
6176b7b2d80SAdrian Chadd 		return (EINVAL);
6186b7b2d80SAdrian Chadd 
6196b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
6206b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
6216b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
6226b7b2d80SAdrian Chadd 	*pin_value = nct_read_pin(sc, pin_num);
6236b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
6246b7b2d80SAdrian Chadd 
6256b7b2d80SAdrian Chadd 	return (0);
6266b7b2d80SAdrian Chadd }
6276b7b2d80SAdrian Chadd 
6286b7b2d80SAdrian Chadd static int
6296b7b2d80SAdrian Chadd nct_gpio_pin_toggle(device_t dev, uint32_t pin_num)
6306b7b2d80SAdrian Chadd {
6316b7b2d80SAdrian Chadd 	struct nct_softc *sc;
6326b7b2d80SAdrian Chadd 
6336b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
6346b7b2d80SAdrian Chadd 		return (EINVAL);
6356b7b2d80SAdrian Chadd 
6366b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
6376b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
6386b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
639*155514eaSAndriy Gapon 	if ((sc->pins[pin_num].gp_flags & GPIO_PIN_OUTPUT) == 0) {
640*155514eaSAndriy Gapon 		GPIO_UNLOCK(sc);
641*155514eaSAndriy Gapon 		return (EINVAL);
642*155514eaSAndriy Gapon 	}
6436b7b2d80SAdrian Chadd 	if (nct_read_pin(sc, pin_num))
6446b7b2d80SAdrian Chadd 		nct_write_pin(sc, pin_num, 0);
6456b7b2d80SAdrian Chadd 	else
6466b7b2d80SAdrian Chadd 		nct_write_pin(sc, pin_num, 1);
6476b7b2d80SAdrian Chadd 
6486b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
6496b7b2d80SAdrian Chadd 
6506b7b2d80SAdrian Chadd 	return (0);
6516b7b2d80SAdrian Chadd }
6526b7b2d80SAdrian Chadd 
6536b7b2d80SAdrian Chadd static int
6546b7b2d80SAdrian Chadd nct_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps)
6556b7b2d80SAdrian Chadd {
6566b7b2d80SAdrian Chadd 	struct nct_softc *sc;
6576b7b2d80SAdrian Chadd 
6586b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
6596b7b2d80SAdrian Chadd 		return (EINVAL);
6606b7b2d80SAdrian Chadd 
6616b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
6626b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
6636b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
6646b7b2d80SAdrian Chadd 	*caps = sc->pins[pin_num].gp_caps;
6656b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
6666b7b2d80SAdrian Chadd 
6676b7b2d80SAdrian Chadd 	return (0);
6686b7b2d80SAdrian Chadd }
6696b7b2d80SAdrian Chadd 
6706b7b2d80SAdrian Chadd static int
6716b7b2d80SAdrian Chadd nct_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags)
6726b7b2d80SAdrian Chadd {
6736b7b2d80SAdrian Chadd 	struct nct_softc *sc;
6746b7b2d80SAdrian Chadd 
6756b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
6766b7b2d80SAdrian Chadd 		return (EINVAL);
6776b7b2d80SAdrian Chadd 
6786b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
6796b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
6806b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
6816b7b2d80SAdrian Chadd 	*flags = sc->pins[pin_num].gp_flags;
6826b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
6836b7b2d80SAdrian Chadd 
6846b7b2d80SAdrian Chadd 	return (0);
6856b7b2d80SAdrian Chadd }
6866b7b2d80SAdrian Chadd 
6876b7b2d80SAdrian Chadd static int
6886b7b2d80SAdrian Chadd nct_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name)
6896b7b2d80SAdrian Chadd {
6906b7b2d80SAdrian Chadd 	struct nct_softc *sc;
6916b7b2d80SAdrian Chadd 
6926b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
6936b7b2d80SAdrian Chadd 		return (EINVAL);
6946b7b2d80SAdrian Chadd 
6956b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
6966b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
6976b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
6986b7b2d80SAdrian Chadd 	memcpy(name, sc->pins[pin_num].gp_name, GPIOMAXNAME);
6996b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
7006b7b2d80SAdrian Chadd 
7016b7b2d80SAdrian Chadd 	return (0);
7026b7b2d80SAdrian Chadd }
7036b7b2d80SAdrian Chadd 
7046b7b2d80SAdrian Chadd static int
7056b7b2d80SAdrian Chadd nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags)
7066b7b2d80SAdrian Chadd {
7076b7b2d80SAdrian Chadd 	struct nct_softc *sc;
7086b7b2d80SAdrian Chadd 	struct gpio_pin *pin;
7096b7b2d80SAdrian Chadd 
7106b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
7116b7b2d80SAdrian Chadd 		return (EINVAL);
7126b7b2d80SAdrian Chadd 
7136b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
7146b7b2d80SAdrian Chadd 	pin = &sc->pins[pin_num];
7156b7b2d80SAdrian Chadd 	if ((flags & pin->gp_caps) != flags)
7166b7b2d80SAdrian Chadd 		return (EINVAL);
7176b7b2d80SAdrian Chadd 
7186b7b2d80SAdrian Chadd 	if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
7196b7b2d80SAdrian Chadd 		(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
7206b7b2d80SAdrian Chadd 			return (EINVAL);
7216b7b2d80SAdrian Chadd 	}
7226b7b2d80SAdrian Chadd 	if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) ==
7236b7b2d80SAdrian Chadd 		(GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) {
724*155514eaSAndriy Gapon 			return (EINVAL);
725*155514eaSAndriy Gapon 	}
726*155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) ==
727*155514eaSAndriy Gapon 		(GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) {
7286b7b2d80SAdrian Chadd 			return (EINVAL);
7296b7b2d80SAdrian Chadd 	}
7306b7b2d80SAdrian Chadd 
731*155514eaSAndriy Gapon 	GPIO_ASSERT_UNLOCKED(sc);
732*155514eaSAndriy Gapon 	GPIO_LOCK(sc);
733*155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) != 0) {
734*155514eaSAndriy Gapon 		nct_set_pin_input(sc, pin_num, (flags & GPIO_PIN_INPUT) != 0);
735*155514eaSAndriy Gapon 		pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
736*155514eaSAndriy Gapon 		pin->gp_flags |= flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT);
737*155514eaSAndriy Gapon 	}
738*155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) != 0) {
739*155514eaSAndriy Gapon 		nct_set_pin_inverted(sc, pin_num,
740*155514eaSAndriy Gapon 		    (flags & GPIO_PIN_INVIN) != 0);
741*155514eaSAndriy Gapon 		pin->gp_flags &= ~(GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
742*155514eaSAndriy Gapon 		pin->gp_flags |= flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
743*155514eaSAndriy Gapon 	}
744*155514eaSAndriy Gapon 	if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) != 0) {
7456b7b2d80SAdrian Chadd 		if (flags & GPIO_PIN_OPENDRAIN)
7466b7b2d80SAdrian Chadd 			nct_set_pin_opendrain(sc, pin_num);
7476b7b2d80SAdrian Chadd 		else
7486b7b2d80SAdrian Chadd 			nct_set_pin_pushpull(sc, pin_num);
749*155514eaSAndriy Gapon 		pin->gp_flags &= ~(GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL);
750*155514eaSAndriy Gapon 		pin->gp_flags |=
751*155514eaSAndriy Gapon 		    flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL);
7526b7b2d80SAdrian Chadd 	}
7536b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
7546b7b2d80SAdrian Chadd 
7556b7b2d80SAdrian Chadd 	return (0);
7566b7b2d80SAdrian Chadd }
7576b7b2d80SAdrian Chadd 
7586b7b2d80SAdrian Chadd static device_method_t nct_methods[] = {
7596b7b2d80SAdrian Chadd 	/* Device interface */
7606b7b2d80SAdrian Chadd 	DEVMETHOD(device_probe,		nct_probe),
7616b7b2d80SAdrian Chadd 	DEVMETHOD(device_attach,	nct_attach),
7626b7b2d80SAdrian Chadd 	DEVMETHOD(device_detach,	nct_detach),
7636b7b2d80SAdrian Chadd 
7646b7b2d80SAdrian Chadd 	/* GPIO */
7656b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_get_bus,		nct_gpio_get_bus),
7666b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_max,		nct_gpio_pin_max),
7676b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_get,		nct_gpio_pin_get),
7686b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_set,		nct_gpio_pin_set),
7696b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_toggle,	nct_gpio_pin_toggle),
7706b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getname,	nct_gpio_pin_getname),
7716b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getcaps,	nct_gpio_pin_getcaps),
7726b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getflags,	nct_gpio_pin_getflags),
7736b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_setflags,	nct_gpio_pin_setflags),
7746b7b2d80SAdrian Chadd 
7756b7b2d80SAdrian Chadd 	DEVMETHOD_END
7766b7b2d80SAdrian Chadd };
7776b7b2d80SAdrian Chadd 
778e3df342aSAndriy Gapon static driver_t nct_driver = {
7796b7b2d80SAdrian Chadd 	"gpio",
7806b7b2d80SAdrian Chadd 	nct_methods,
7816b7b2d80SAdrian Chadd 	sizeof(struct nct_softc)
7826b7b2d80SAdrian Chadd };
7836b7b2d80SAdrian Chadd 
7846b7b2d80SAdrian Chadd static devclass_t nct_devclass;
7856b7b2d80SAdrian Chadd 
786e3df342aSAndriy Gapon DRIVER_MODULE(nctgpio, superio, nct_driver, nct_devclass, NULL, NULL);
7876b7b2d80SAdrian Chadd MODULE_DEPEND(nctgpio, gpiobus, 1, 1, 1);
788e3df342aSAndriy Gapon MODULE_DEPEND(nctgpio, superio, 1, 1, 1);
789e3df342aSAndriy Gapon MODULE_VERSION(nctgpio, 1);
790e3df342aSAndriy Gapon 
791