xref: /freebsd/sys/dev/nctgpio/nctgpio.c (revision 6b7b2d80ed4d728d3ffd12c422e57798c1b63a84)
1*6b7b2d80SAdrian Chadd /*-
2*6b7b2d80SAdrian Chadd  * Copyright (c) 2016 Daniel Wyatt <Daniel.Wyatt@gmail.com>
3*6b7b2d80SAdrian Chadd  * All rights reserved.
4*6b7b2d80SAdrian Chadd  *
5*6b7b2d80SAdrian Chadd  * Redistribution and use in source and binary forms, with or without
6*6b7b2d80SAdrian Chadd  * modification, are permitted provided that the following conditions
7*6b7b2d80SAdrian Chadd  * are met:
8*6b7b2d80SAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
9*6b7b2d80SAdrian Chadd  *    notice, this list of conditions and the following disclaimer.
10*6b7b2d80SAdrian Chadd  * 2. Redistributions in binary form must reproduce the above copyright
11*6b7b2d80SAdrian Chadd  *    notice, this list of conditions and the following disclaimer in the
12*6b7b2d80SAdrian Chadd  *    documentation and/or other materials provided with the distribution.
13*6b7b2d80SAdrian Chadd  *
14*6b7b2d80SAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15*6b7b2d80SAdrian Chadd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16*6b7b2d80SAdrian Chadd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*6b7b2d80SAdrian Chadd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18*6b7b2d80SAdrian Chadd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*6b7b2d80SAdrian Chadd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*6b7b2d80SAdrian Chadd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*6b7b2d80SAdrian Chadd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22*6b7b2d80SAdrian Chadd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23*6b7b2d80SAdrian Chadd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24*6b7b2d80SAdrian Chadd  * SUCH DAMAGE.
25*6b7b2d80SAdrian Chadd  *
26*6b7b2d80SAdrian Chadd  * $FreeBSD$
27*6b7b2d80SAdrian Chadd  *
28*6b7b2d80SAdrian Chadd  */
29*6b7b2d80SAdrian Chadd 
30*6b7b2d80SAdrian Chadd /*
31*6b7b2d80SAdrian Chadd  * Nuvoton GPIO driver.
32*6b7b2d80SAdrian Chadd  *
33*6b7b2d80SAdrian Chadd  */
34*6b7b2d80SAdrian Chadd 
35*6b7b2d80SAdrian Chadd #include <sys/cdefs.h>
36*6b7b2d80SAdrian Chadd 
37*6b7b2d80SAdrian Chadd #include <sys/param.h>
38*6b7b2d80SAdrian Chadd #include <sys/kernel.h>
39*6b7b2d80SAdrian Chadd #include <sys/systm.h>
40*6b7b2d80SAdrian Chadd #include <sys/bus.h>
41*6b7b2d80SAdrian Chadd #include <sys/eventhandler.h>
42*6b7b2d80SAdrian Chadd #include <sys/lock.h>
43*6b7b2d80SAdrian Chadd 
44*6b7b2d80SAdrian Chadd #include <sys/module.h>
45*6b7b2d80SAdrian Chadd #include <sys/rman.h>
46*6b7b2d80SAdrian Chadd #include <sys/gpio.h>
47*6b7b2d80SAdrian Chadd 
48*6b7b2d80SAdrian Chadd #include <isa/isavar.h>
49*6b7b2d80SAdrian Chadd 
50*6b7b2d80SAdrian Chadd #include <machine/bus.h>
51*6b7b2d80SAdrian Chadd #include <machine/resource.h>
52*6b7b2d80SAdrian Chadd 
53*6b7b2d80SAdrian Chadd #include <dev/gpio/gpiobusvar.h>
54*6b7b2d80SAdrian Chadd 
55*6b7b2d80SAdrian Chadd #include "gpio_if.h"
56*6b7b2d80SAdrian Chadd 
57*6b7b2d80SAdrian Chadd /*
58*6b7b2d80SAdrian Chadd  * Global configuration registers (CR).
59*6b7b2d80SAdrian Chadd  */
60*6b7b2d80SAdrian Chadd #define NCT_CR_LDN			0x07	/* Logical Device Number */
61*6b7b2d80SAdrian Chadd #define NCT_CR_CHIP_ID			0x20 	/* Chip ID */
62*6b7b2d80SAdrian Chadd #define NCT_CR_CHIP_ID_H		0x20 	/* Chip ID (high byte) */
63*6b7b2d80SAdrian Chadd #define NCT_CR_CHIP_ID_L		0x21 	/* Chip ID (low byte) */
64*6b7b2d80SAdrian Chadd #define NCT_CR_OPT_1			0x26	/* Global Options (1) */
65*6b7b2d80SAdrian Chadd 
66*6b7b2d80SAdrian Chadd /* Logical Device Numbers. */
67*6b7b2d80SAdrian Chadd #define NCT_LDN_GPIO			0x07
68*6b7b2d80SAdrian Chadd #define NCT_LDN_GPIO_CFG		0x08
69*6b7b2d80SAdrian Chadd #define NCT_LDN_GPIO_MODE		0x0f
70*6b7b2d80SAdrian Chadd 
71*6b7b2d80SAdrian Chadd /* Logical Device 7 */
72*6b7b2d80SAdrian Chadd #define NCT_LD7_GPIO_ENABLE		0x30
73*6b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_IOR		0xe0
74*6b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_DAT		0xe1
75*6b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_INV		0xe2
76*6b7b2d80SAdrian Chadd #define NCT_LD7_GPIO0_DST		0xe3
77*6b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_IOR		0xe4
78*6b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_DAT		0xe5
79*6b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_INV		0xe6
80*6b7b2d80SAdrian Chadd #define NCT_LD7_GPIO1_DST		0xe7
81*6b7b2d80SAdrian Chadd 
82*6b7b2d80SAdrian Chadd /* Logical Device F */
83*6b7b2d80SAdrian Chadd #define NCT_LDF_GPIO0_OUTCFG		0xe0
84*6b7b2d80SAdrian Chadd #define NCT_LDF_GPIO1_OUTCFG		0xe1
85*6b7b2d80SAdrian Chadd 
86*6b7b2d80SAdrian Chadd #define NCT_EXTFUNC_ENTER		0x87
87*6b7b2d80SAdrian Chadd #define NCT_EXTFUNC_EXIT		0xaa
88*6b7b2d80SAdrian Chadd 
89*6b7b2d80SAdrian Chadd #define NCT_MAX_PIN			15
90*6b7b2d80SAdrian Chadd #define NCT_IS_VALID_PIN(_p)	((_p) >= 0 && (_p) <= NCT_MAX_PIN)
91*6b7b2d80SAdrian Chadd 
92*6b7b2d80SAdrian Chadd #define NCT_PIN_BIT(_p)         (1 << ((_p) % 8))
93*6b7b2d80SAdrian Chadd 
94*6b7b2d80SAdrian Chadd #define NCT_GPIO_CAPS	(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \
95*6b7b2d80SAdrian Chadd 	GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | \
96*6b7b2d80SAdrian Chadd 	GPIO_PIN_INVIN | GPIO_PIN_INVOUT)
97*6b7b2d80SAdrian Chadd 
98*6b7b2d80SAdrian Chadd struct nct_softc {
99*6b7b2d80SAdrian Chadd 	device_t			dev;
100*6b7b2d80SAdrian Chadd 	device_t			busdev;
101*6b7b2d80SAdrian Chadd 	struct mtx			mtx;
102*6b7b2d80SAdrian Chadd 	struct resource			*portres;
103*6b7b2d80SAdrian Chadd 	int				rid;
104*6b7b2d80SAdrian Chadd 	struct gpio_pin			pins[NCT_MAX_PIN];
105*6b7b2d80SAdrian Chadd };
106*6b7b2d80SAdrian Chadd 
107*6b7b2d80SAdrian Chadd #define GPIO_LOCK_INIT(_sc)	mtx_init(&(_sc)->mtx,		\
108*6b7b2d80SAdrian Chadd 		device_get_nameunit(dev), NULL, MTX_DEF)
109*6b7b2d80SAdrian Chadd #define GPIO_LOCK_DESTROY(_sc)		mtx_destroy(&(_sc)->mtx)
110*6b7b2d80SAdrian Chadd #define GPIO_LOCK(_sc)		mtx_lock(&(_sc)->mtx)
111*6b7b2d80SAdrian Chadd #define GPIO_UNLOCK(_sc)	mtx_unlock(&(_sc)->mtx)
112*6b7b2d80SAdrian Chadd #define GPIO_ASSERT_LOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_OWNED)
113*6b7b2d80SAdrian Chadd #define GPIO_ASSERT_UNLOCKED(_sc)	mtx_assert(&(_sc)->mtx, MA_NOTOWNED)
114*6b7b2d80SAdrian Chadd 
115*6b7b2d80SAdrian Chadd #define NCT_BARRIER_WRITE(_sc)	\
116*6b7b2d80SAdrian Chadd 	bus_barrier((_sc)->portres, 0, 2, BUS_SPACE_BARRIER_WRITE)
117*6b7b2d80SAdrian Chadd 
118*6b7b2d80SAdrian Chadd #define NCT_BARRIER_READ_WRITE(_sc)	\
119*6b7b2d80SAdrian Chadd 	bus_barrier((_sc)->portres, 0, 2, \
120*6b7b2d80SAdrian Chadd 		BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE)
121*6b7b2d80SAdrian Chadd 
122*6b7b2d80SAdrian Chadd static void	ext_cfg_enter(struct nct_softc *);
123*6b7b2d80SAdrian Chadd static void	ext_cfg_exit(struct nct_softc *);
124*6b7b2d80SAdrian Chadd 
125*6b7b2d80SAdrian Chadd /*
126*6b7b2d80SAdrian Chadd  * Potential Extended Function Enable Register addresses.
127*6b7b2d80SAdrian Chadd  * Same address as EFIR.
128*6b7b2d80SAdrian Chadd  */
129*6b7b2d80SAdrian Chadd uint8_t probe_addrs[] = {0x2e, 0x4e};
130*6b7b2d80SAdrian Chadd 
131*6b7b2d80SAdrian Chadd struct nuvoton_vendor_device_id {
132*6b7b2d80SAdrian Chadd 	uint16_t		chip_id;
133*6b7b2d80SAdrian Chadd 	const char *		descr;
134*6b7b2d80SAdrian Chadd } nct_devs[] = {
135*6b7b2d80SAdrian Chadd 	{
136*6b7b2d80SAdrian Chadd 		.chip_id	= 0x1061,
137*6b7b2d80SAdrian Chadd 		.descr		= "Nuvoton NCT5104D",
138*6b7b2d80SAdrian Chadd 	},
139*6b7b2d80SAdrian Chadd 	{
140*6b7b2d80SAdrian Chadd 		.chip_id	= 0xc452,
141*6b7b2d80SAdrian Chadd 		.descr		= "Nuvoton NCT5104D (PC-Engines APU)",
142*6b7b2d80SAdrian Chadd 	},
143*6b7b2d80SAdrian Chadd };
144*6b7b2d80SAdrian Chadd 
145*6b7b2d80SAdrian Chadd static void
146*6b7b2d80SAdrian Chadd write_cfg_reg_1(struct nct_softc *sc, uint8_t reg, uint8_t value)
147*6b7b2d80SAdrian Chadd {
148*6b7b2d80SAdrian Chadd 	GPIO_ASSERT_LOCKED(sc);
149*6b7b2d80SAdrian Chadd 	bus_write_1(sc->portres, 0, reg);
150*6b7b2d80SAdrian Chadd 	NCT_BARRIER_WRITE(sc);
151*6b7b2d80SAdrian Chadd 	bus_write_1(sc->portres, 1, value);
152*6b7b2d80SAdrian Chadd 	NCT_BARRIER_WRITE(sc);
153*6b7b2d80SAdrian Chadd }
154*6b7b2d80SAdrian Chadd 
155*6b7b2d80SAdrian Chadd static uint8_t
156*6b7b2d80SAdrian Chadd read_cfg_reg_1(struct nct_softc *sc, uint8_t reg)
157*6b7b2d80SAdrian Chadd {
158*6b7b2d80SAdrian Chadd 	uint8_t value;
159*6b7b2d80SAdrian Chadd 
160*6b7b2d80SAdrian Chadd 	GPIO_ASSERT_LOCKED(sc);
161*6b7b2d80SAdrian Chadd 	bus_write_1(sc->portres, 0, reg);
162*6b7b2d80SAdrian Chadd 	NCT_BARRIER_READ_WRITE(sc);
163*6b7b2d80SAdrian Chadd 	value = bus_read_1(sc->portres, 1);
164*6b7b2d80SAdrian Chadd 	NCT_BARRIER_READ_WRITE(sc);
165*6b7b2d80SAdrian Chadd 
166*6b7b2d80SAdrian Chadd 	return (value);
167*6b7b2d80SAdrian Chadd }
168*6b7b2d80SAdrian Chadd 
169*6b7b2d80SAdrian Chadd static uint16_t
170*6b7b2d80SAdrian Chadd read_cfg_reg_2(struct nct_softc *sc, uint8_t reg)
171*6b7b2d80SAdrian Chadd {
172*6b7b2d80SAdrian Chadd 	uint16_t value;
173*6b7b2d80SAdrian Chadd 
174*6b7b2d80SAdrian Chadd 	value = read_cfg_reg_1(sc, reg) << 8;
175*6b7b2d80SAdrian Chadd 	value |= read_cfg_reg_1(sc, reg + 1);
176*6b7b2d80SAdrian Chadd 
177*6b7b2d80SAdrian Chadd 	return (value);
178*6b7b2d80SAdrian Chadd }
179*6b7b2d80SAdrian Chadd 
180*6b7b2d80SAdrian Chadd /*
181*6b7b2d80SAdrian Chadd  * Enable extended function mode.
182*6b7b2d80SAdrian Chadd  *
183*6b7b2d80SAdrian Chadd  */
184*6b7b2d80SAdrian Chadd static void
185*6b7b2d80SAdrian Chadd ext_cfg_enter(struct nct_softc *sc)
186*6b7b2d80SAdrian Chadd {
187*6b7b2d80SAdrian Chadd 	GPIO_ASSERT_LOCKED(sc);
188*6b7b2d80SAdrian Chadd 	bus_write_1(sc->portres, 0, NCT_EXTFUNC_ENTER);
189*6b7b2d80SAdrian Chadd 	NCT_BARRIER_WRITE(sc);
190*6b7b2d80SAdrian Chadd 	bus_write_1(sc->portres, 0, NCT_EXTFUNC_ENTER);
191*6b7b2d80SAdrian Chadd 	NCT_BARRIER_WRITE(sc);
192*6b7b2d80SAdrian Chadd }
193*6b7b2d80SAdrian Chadd 
194*6b7b2d80SAdrian Chadd /*
195*6b7b2d80SAdrian Chadd  * Disable extended function mode.
196*6b7b2d80SAdrian Chadd  *
197*6b7b2d80SAdrian Chadd  */
198*6b7b2d80SAdrian Chadd static void
199*6b7b2d80SAdrian Chadd ext_cfg_exit(struct nct_softc *sc)
200*6b7b2d80SAdrian Chadd {
201*6b7b2d80SAdrian Chadd 	GPIO_ASSERT_LOCKED(sc);
202*6b7b2d80SAdrian Chadd 	bus_write_1(sc->portres, 0, NCT_EXTFUNC_EXIT);
203*6b7b2d80SAdrian Chadd 	NCT_BARRIER_WRITE(sc);
204*6b7b2d80SAdrian Chadd }
205*6b7b2d80SAdrian Chadd 
206*6b7b2d80SAdrian Chadd /*
207*6b7b2d80SAdrian Chadd  * Select a Logical Device.
208*6b7b2d80SAdrian Chadd  */
209*6b7b2d80SAdrian Chadd static void
210*6b7b2d80SAdrian Chadd select_ldn(struct nct_softc *sc, uint8_t ldn)
211*6b7b2d80SAdrian Chadd {
212*6b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, NCT_CR_LDN, ldn);
213*6b7b2d80SAdrian Chadd }
214*6b7b2d80SAdrian Chadd 
215*6b7b2d80SAdrian Chadd /*
216*6b7b2d80SAdrian Chadd  * Get the GPIO Input/Output register address
217*6b7b2d80SAdrian Chadd  * for a pin.
218*6b7b2d80SAdrian Chadd  */
219*6b7b2d80SAdrian Chadd static uint8_t
220*6b7b2d80SAdrian Chadd nct_ior_addr(uint32_t pin_num)
221*6b7b2d80SAdrian Chadd {
222*6b7b2d80SAdrian Chadd 	uint8_t addr;
223*6b7b2d80SAdrian Chadd 
224*6b7b2d80SAdrian Chadd 	addr = NCT_LD7_GPIO0_IOR;
225*6b7b2d80SAdrian Chadd 	if (pin_num > 7)
226*6b7b2d80SAdrian Chadd 		addr = NCT_LD7_GPIO1_IOR;
227*6b7b2d80SAdrian Chadd 
228*6b7b2d80SAdrian Chadd 	return (addr);
229*6b7b2d80SAdrian Chadd }
230*6b7b2d80SAdrian Chadd 
231*6b7b2d80SAdrian Chadd /*
232*6b7b2d80SAdrian Chadd  * Get the GPIO Data register address for a pin.
233*6b7b2d80SAdrian Chadd  */
234*6b7b2d80SAdrian Chadd static uint8_t
235*6b7b2d80SAdrian Chadd nct_dat_addr(uint32_t pin_num)
236*6b7b2d80SAdrian Chadd {
237*6b7b2d80SAdrian Chadd 	uint8_t addr;
238*6b7b2d80SAdrian Chadd 
239*6b7b2d80SAdrian Chadd 	addr = NCT_LD7_GPIO0_DAT;
240*6b7b2d80SAdrian Chadd 	if (pin_num > 7)
241*6b7b2d80SAdrian Chadd 		addr = NCT_LD7_GPIO1_DAT;
242*6b7b2d80SAdrian Chadd 
243*6b7b2d80SAdrian Chadd 	return (addr);
244*6b7b2d80SAdrian Chadd }
245*6b7b2d80SAdrian Chadd 
246*6b7b2d80SAdrian Chadd /*
247*6b7b2d80SAdrian Chadd  * Get the GPIO Inversion register address
248*6b7b2d80SAdrian Chadd  * for a pin.
249*6b7b2d80SAdrian Chadd  */
250*6b7b2d80SAdrian Chadd static uint8_t
251*6b7b2d80SAdrian Chadd nct_inv_addr(uint32_t pin_num)
252*6b7b2d80SAdrian Chadd {
253*6b7b2d80SAdrian Chadd 	uint8_t addr;
254*6b7b2d80SAdrian Chadd 
255*6b7b2d80SAdrian Chadd 	addr = NCT_LD7_GPIO0_INV;
256*6b7b2d80SAdrian Chadd 	if (pin_num > 7)
257*6b7b2d80SAdrian Chadd 		addr = NCT_LD7_GPIO1_INV;
258*6b7b2d80SAdrian Chadd 
259*6b7b2d80SAdrian Chadd 	return (addr);
260*6b7b2d80SAdrian Chadd }
261*6b7b2d80SAdrian Chadd 
262*6b7b2d80SAdrian Chadd /*
263*6b7b2d80SAdrian Chadd  * Get the GPIO Output Configuration/Mode
264*6b7b2d80SAdrian Chadd  * register address for a pin.
265*6b7b2d80SAdrian Chadd  */
266*6b7b2d80SAdrian Chadd static uint8_t
267*6b7b2d80SAdrian Chadd nct_outcfg_addr(uint32_t pin_num)
268*6b7b2d80SAdrian Chadd {
269*6b7b2d80SAdrian Chadd 	uint8_t addr;
270*6b7b2d80SAdrian Chadd 
271*6b7b2d80SAdrian Chadd 	addr = NCT_LDF_GPIO0_OUTCFG;
272*6b7b2d80SAdrian Chadd 	if (pin_num > 7)
273*6b7b2d80SAdrian Chadd 		addr = NCT_LDF_GPIO1_OUTCFG;
274*6b7b2d80SAdrian Chadd 
275*6b7b2d80SAdrian Chadd 	return (addr);
276*6b7b2d80SAdrian Chadd }
277*6b7b2d80SAdrian Chadd 
278*6b7b2d80SAdrian Chadd /*
279*6b7b2d80SAdrian Chadd  * Set a pin to output mode.
280*6b7b2d80SAdrian Chadd  */
281*6b7b2d80SAdrian Chadd static void
282*6b7b2d80SAdrian Chadd nct_set_pin_is_output(struct nct_softc *sc, uint32_t pin_num)
283*6b7b2d80SAdrian Chadd {
284*6b7b2d80SAdrian Chadd 	uint8_t reg;
285*6b7b2d80SAdrian Chadd 	uint8_t ior;
286*6b7b2d80SAdrian Chadd 
287*6b7b2d80SAdrian Chadd 	reg = nct_ior_addr(pin_num);
288*6b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
289*6b7b2d80SAdrian Chadd 	ior = read_cfg_reg_1(sc, reg);
290*6b7b2d80SAdrian Chadd 	ior &= ~(NCT_PIN_BIT(pin_num));
291*6b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, reg, ior);
292*6b7b2d80SAdrian Chadd }
293*6b7b2d80SAdrian Chadd 
294*6b7b2d80SAdrian Chadd /*
295*6b7b2d80SAdrian Chadd  * Set a pin to input mode.
296*6b7b2d80SAdrian Chadd  */
297*6b7b2d80SAdrian Chadd static void
298*6b7b2d80SAdrian Chadd nct_set_pin_is_input(struct nct_softc *sc, uint32_t pin_num)
299*6b7b2d80SAdrian Chadd {
300*6b7b2d80SAdrian Chadd 	uint8_t reg;
301*6b7b2d80SAdrian Chadd 	uint8_t ior;
302*6b7b2d80SAdrian Chadd 
303*6b7b2d80SAdrian Chadd 	reg = nct_ior_addr(pin_num);
304*6b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
305*6b7b2d80SAdrian Chadd 	ior = read_cfg_reg_1(sc, reg);
306*6b7b2d80SAdrian Chadd 	ior |= NCT_PIN_BIT(pin_num);
307*6b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, reg, ior);
308*6b7b2d80SAdrian Chadd }
309*6b7b2d80SAdrian Chadd 
310*6b7b2d80SAdrian Chadd /*
311*6b7b2d80SAdrian Chadd  * Check whether a pin is configured as an input.
312*6b7b2d80SAdrian Chadd  */
313*6b7b2d80SAdrian Chadd static bool
314*6b7b2d80SAdrian Chadd nct_pin_is_input(struct nct_softc *sc, uint32_t pin_num)
315*6b7b2d80SAdrian Chadd {
316*6b7b2d80SAdrian Chadd 	uint8_t reg;
317*6b7b2d80SAdrian Chadd 	uint8_t ior;
318*6b7b2d80SAdrian Chadd 
319*6b7b2d80SAdrian Chadd 	reg = nct_ior_addr(pin_num);
320*6b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
321*6b7b2d80SAdrian Chadd 	ior = read_cfg_reg_1(sc, reg);
322*6b7b2d80SAdrian Chadd 
323*6b7b2d80SAdrian Chadd 	return (ior & NCT_PIN_BIT(pin_num));
324*6b7b2d80SAdrian Chadd }
325*6b7b2d80SAdrian Chadd 
326*6b7b2d80SAdrian Chadd /*
327*6b7b2d80SAdrian Chadd  * Write a value to an output pin.
328*6b7b2d80SAdrian Chadd  */
329*6b7b2d80SAdrian Chadd static void
330*6b7b2d80SAdrian Chadd nct_write_pin(struct nct_softc *sc, uint32_t pin_num, uint8_t data)
331*6b7b2d80SAdrian Chadd {
332*6b7b2d80SAdrian Chadd 	uint8_t reg;
333*6b7b2d80SAdrian Chadd 	uint8_t value;
334*6b7b2d80SAdrian Chadd 
335*6b7b2d80SAdrian Chadd 	reg = nct_dat_addr(pin_num);
336*6b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
337*6b7b2d80SAdrian Chadd 	value = read_cfg_reg_1(sc, reg);
338*6b7b2d80SAdrian Chadd 	if (data)
339*6b7b2d80SAdrian Chadd 		value |= NCT_PIN_BIT(pin_num);
340*6b7b2d80SAdrian Chadd 	else
341*6b7b2d80SAdrian Chadd 		value &= ~(NCT_PIN_BIT(pin_num));
342*6b7b2d80SAdrian Chadd 
343*6b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, reg, value);
344*6b7b2d80SAdrian Chadd }
345*6b7b2d80SAdrian Chadd 
346*6b7b2d80SAdrian Chadd static bool
347*6b7b2d80SAdrian Chadd nct_read_pin(struct nct_softc *sc, uint32_t pin_num)
348*6b7b2d80SAdrian Chadd {
349*6b7b2d80SAdrian Chadd 	uint8_t reg;
350*6b7b2d80SAdrian Chadd 
351*6b7b2d80SAdrian Chadd 	reg = nct_dat_addr(pin_num);
352*6b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
353*6b7b2d80SAdrian Chadd 
354*6b7b2d80SAdrian Chadd 	return (read_cfg_reg_1(sc, reg) & NCT_PIN_BIT(pin_num));
355*6b7b2d80SAdrian Chadd }
356*6b7b2d80SAdrian Chadd 
357*6b7b2d80SAdrian Chadd static void
358*6b7b2d80SAdrian Chadd nct_set_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num)
359*6b7b2d80SAdrian Chadd {
360*6b7b2d80SAdrian Chadd 	uint8_t reg;
361*6b7b2d80SAdrian Chadd 	uint8_t inv;
362*6b7b2d80SAdrian Chadd 
363*6b7b2d80SAdrian Chadd 	reg = nct_inv_addr(pin_num);
364*6b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
365*6b7b2d80SAdrian Chadd 	inv = read_cfg_reg_1(sc, reg);
366*6b7b2d80SAdrian Chadd 	inv |= (NCT_PIN_BIT(pin_num));
367*6b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, reg, inv);
368*6b7b2d80SAdrian Chadd }
369*6b7b2d80SAdrian Chadd 
370*6b7b2d80SAdrian Chadd static void
371*6b7b2d80SAdrian Chadd nct_set_pin_not_inverted(struct nct_softc *sc, uint32_t pin_num)
372*6b7b2d80SAdrian Chadd {
373*6b7b2d80SAdrian Chadd 	uint8_t reg;
374*6b7b2d80SAdrian Chadd 	uint8_t inv;
375*6b7b2d80SAdrian Chadd 
376*6b7b2d80SAdrian Chadd 	reg = nct_inv_addr(pin_num);
377*6b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
378*6b7b2d80SAdrian Chadd 	inv = read_cfg_reg_1(sc, reg);
379*6b7b2d80SAdrian Chadd 	inv &= ~(NCT_PIN_BIT(pin_num));
380*6b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, reg, inv);
381*6b7b2d80SAdrian Chadd }
382*6b7b2d80SAdrian Chadd 
383*6b7b2d80SAdrian Chadd static bool
384*6b7b2d80SAdrian Chadd nct_pin_is_inverted(struct nct_softc *sc, uint32_t pin_num)
385*6b7b2d80SAdrian Chadd {
386*6b7b2d80SAdrian Chadd 	uint8_t reg;
387*6b7b2d80SAdrian Chadd 	uint8_t inv;
388*6b7b2d80SAdrian Chadd 
389*6b7b2d80SAdrian Chadd 	reg = nct_inv_addr(pin_num);
390*6b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
391*6b7b2d80SAdrian Chadd 	inv = read_cfg_reg_1(sc, reg);
392*6b7b2d80SAdrian Chadd 
393*6b7b2d80SAdrian Chadd 	return (inv & NCT_PIN_BIT(pin_num));
394*6b7b2d80SAdrian Chadd }
395*6b7b2d80SAdrian Chadd 
396*6b7b2d80SAdrian Chadd static void
397*6b7b2d80SAdrian Chadd nct_set_pin_opendrain(struct nct_softc *sc, uint32_t pin_num)
398*6b7b2d80SAdrian Chadd {
399*6b7b2d80SAdrian Chadd 	uint8_t reg;
400*6b7b2d80SAdrian Chadd 	uint8_t outcfg;
401*6b7b2d80SAdrian Chadd 
402*6b7b2d80SAdrian Chadd 	reg = nct_outcfg_addr(pin_num);
403*6b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO_MODE);
404*6b7b2d80SAdrian Chadd 	outcfg = read_cfg_reg_1(sc, reg);
405*6b7b2d80SAdrian Chadd 	outcfg |= (NCT_PIN_BIT(pin_num));
406*6b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, reg, outcfg);
407*6b7b2d80SAdrian Chadd }
408*6b7b2d80SAdrian Chadd 
409*6b7b2d80SAdrian Chadd static void
410*6b7b2d80SAdrian Chadd nct_set_pin_pushpull(struct nct_softc *sc, uint32_t pin_num)
411*6b7b2d80SAdrian Chadd {
412*6b7b2d80SAdrian Chadd 	uint8_t reg;
413*6b7b2d80SAdrian Chadd 	uint8_t outcfg;
414*6b7b2d80SAdrian Chadd 
415*6b7b2d80SAdrian Chadd 	reg = nct_outcfg_addr(pin_num);
416*6b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO_MODE);
417*6b7b2d80SAdrian Chadd 	outcfg = read_cfg_reg_1(sc, reg);
418*6b7b2d80SAdrian Chadd 	outcfg &= ~(NCT_PIN_BIT(pin_num));
419*6b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, reg, outcfg);
420*6b7b2d80SAdrian Chadd }
421*6b7b2d80SAdrian Chadd 
422*6b7b2d80SAdrian Chadd static bool
423*6b7b2d80SAdrian Chadd nct_pin_is_opendrain(struct nct_softc *sc, uint32_t pin_num)
424*6b7b2d80SAdrian Chadd {
425*6b7b2d80SAdrian Chadd 	uint8_t reg;
426*6b7b2d80SAdrian Chadd 	uint8_t outcfg;
427*6b7b2d80SAdrian Chadd 
428*6b7b2d80SAdrian Chadd 	reg = nct_outcfg_addr(pin_num);
429*6b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO_MODE);
430*6b7b2d80SAdrian Chadd 	outcfg = read_cfg_reg_1(sc, reg);
431*6b7b2d80SAdrian Chadd 
432*6b7b2d80SAdrian Chadd 	return (outcfg & NCT_PIN_BIT(pin_num));
433*6b7b2d80SAdrian Chadd }
434*6b7b2d80SAdrian Chadd 
435*6b7b2d80SAdrian Chadd static void
436*6b7b2d80SAdrian Chadd nct_identify(driver_t *driver, device_t parent)
437*6b7b2d80SAdrian Chadd {
438*6b7b2d80SAdrian Chadd 	if (device_find_child(parent, driver->name, 0) != NULL)
439*6b7b2d80SAdrian Chadd 		return;
440*6b7b2d80SAdrian Chadd 
441*6b7b2d80SAdrian Chadd 	BUS_ADD_CHILD(parent, 0, driver->name, 0);
442*6b7b2d80SAdrian Chadd }
443*6b7b2d80SAdrian Chadd 
444*6b7b2d80SAdrian Chadd static int
445*6b7b2d80SAdrian Chadd nct_probe(device_t dev)
446*6b7b2d80SAdrian Chadd {
447*6b7b2d80SAdrian Chadd 	int i, j;
448*6b7b2d80SAdrian Chadd 	int rc;
449*6b7b2d80SAdrian Chadd 	struct nct_softc *sc;
450*6b7b2d80SAdrian Chadd 	uint16_t chipid;
451*6b7b2d80SAdrian Chadd 
452*6b7b2d80SAdrian Chadd 	/* Make sure we do not claim some ISA PNP device. */
453*6b7b2d80SAdrian Chadd 	if (isa_get_logicalid(dev) != 0)
454*6b7b2d80SAdrian Chadd 		return (ENXIO);
455*6b7b2d80SAdrian Chadd 
456*6b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
457*6b7b2d80SAdrian Chadd 
458*6b7b2d80SAdrian Chadd 	for (i = 0; i < sizeof(probe_addrs) / sizeof(*probe_addrs); i++) {
459*6b7b2d80SAdrian Chadd 		sc->rid = 0;
460*6b7b2d80SAdrian Chadd 		sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid,
461*6b7b2d80SAdrian Chadd 			probe_addrs[i], probe_addrs[i] + 1, 2, RF_ACTIVE);
462*6b7b2d80SAdrian Chadd 		if (sc->portres == NULL)
463*6b7b2d80SAdrian Chadd 			continue;
464*6b7b2d80SAdrian Chadd 
465*6b7b2d80SAdrian Chadd 		GPIO_LOCK_INIT(sc);
466*6b7b2d80SAdrian Chadd 
467*6b7b2d80SAdrian Chadd 		GPIO_ASSERT_UNLOCKED(sc);
468*6b7b2d80SAdrian Chadd 		GPIO_LOCK(sc);
469*6b7b2d80SAdrian Chadd 		ext_cfg_enter(sc);
470*6b7b2d80SAdrian Chadd 		chipid = read_cfg_reg_2(sc, NCT_CR_CHIP_ID);
471*6b7b2d80SAdrian Chadd 		ext_cfg_exit(sc);
472*6b7b2d80SAdrian Chadd 		GPIO_UNLOCK(sc);
473*6b7b2d80SAdrian Chadd 
474*6b7b2d80SAdrian Chadd 		GPIO_LOCK_DESTROY(sc);
475*6b7b2d80SAdrian Chadd 
476*6b7b2d80SAdrian Chadd 		bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
477*6b7b2d80SAdrian Chadd 		bus_delete_resource(dev, SYS_RES_IOPORT, sc->rid);
478*6b7b2d80SAdrian Chadd 
479*6b7b2d80SAdrian Chadd 		for (j = 0; j < sizeof(nct_devs) / sizeof(*nct_devs); j++) {
480*6b7b2d80SAdrian Chadd 			if (chipid == nct_devs[j].chip_id) {
481*6b7b2d80SAdrian Chadd 				rc = bus_set_resource(dev, SYS_RES_IOPORT, 0, probe_addrs[i], 2);
482*6b7b2d80SAdrian Chadd 				if (rc != 0) {
483*6b7b2d80SAdrian Chadd 					device_printf(dev, "bus_set_resource failed for address 0x%02X\n", probe_addrs[i]);
484*6b7b2d80SAdrian Chadd 					continue;
485*6b7b2d80SAdrian Chadd 				}
486*6b7b2d80SAdrian Chadd 				device_set_desc(dev, nct_devs[j].descr);
487*6b7b2d80SAdrian Chadd 				return (BUS_PROBE_DEFAULT);
488*6b7b2d80SAdrian Chadd 			}
489*6b7b2d80SAdrian Chadd 		}
490*6b7b2d80SAdrian Chadd 	}
491*6b7b2d80SAdrian Chadd 	return (ENXIO);
492*6b7b2d80SAdrian Chadd }
493*6b7b2d80SAdrian Chadd 
494*6b7b2d80SAdrian Chadd static int
495*6b7b2d80SAdrian Chadd nct_attach(device_t dev)
496*6b7b2d80SAdrian Chadd {
497*6b7b2d80SAdrian Chadd 	struct nct_softc *sc;
498*6b7b2d80SAdrian Chadd 	int i;
499*6b7b2d80SAdrian Chadd 
500*6b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
501*6b7b2d80SAdrian Chadd 
502*6b7b2d80SAdrian Chadd 	sc->rid = 0;
503*6b7b2d80SAdrian Chadd 	sc->portres = bus_alloc_resource(dev, SYS_RES_IOPORT, &sc->rid,
504*6b7b2d80SAdrian Chadd 		0ul, ~0ul, 2, RF_ACTIVE);
505*6b7b2d80SAdrian Chadd 	if (sc->portres == NULL) {
506*6b7b2d80SAdrian Chadd 		device_printf(dev, "cannot allocate ioport\n");
507*6b7b2d80SAdrian Chadd 		return (ENXIO);
508*6b7b2d80SAdrian Chadd 	}
509*6b7b2d80SAdrian Chadd 
510*6b7b2d80SAdrian Chadd 	GPIO_LOCK_INIT(sc);
511*6b7b2d80SAdrian Chadd 
512*6b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
513*6b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
514*6b7b2d80SAdrian Chadd 	ext_cfg_enter(sc);
515*6b7b2d80SAdrian Chadd 	select_ldn(sc, NCT_LDN_GPIO);
516*6b7b2d80SAdrian Chadd 	/* Enable gpio0 and gpio1. */
517*6b7b2d80SAdrian Chadd 	write_cfg_reg_1(sc, NCT_LD7_GPIO_ENABLE,
518*6b7b2d80SAdrian Chadd 		read_cfg_reg_1(sc, NCT_LD7_GPIO_ENABLE) | 0x03);
519*6b7b2d80SAdrian Chadd 
520*6b7b2d80SAdrian Chadd 	for (i = 0; i <= NCT_MAX_PIN; i++) {
521*6b7b2d80SAdrian Chadd 		struct gpio_pin *pin;
522*6b7b2d80SAdrian Chadd 
523*6b7b2d80SAdrian Chadd 		pin = &sc->pins[i];
524*6b7b2d80SAdrian Chadd 		pin->gp_pin = i;
525*6b7b2d80SAdrian Chadd 		pin->gp_caps = NCT_GPIO_CAPS;
526*6b7b2d80SAdrian Chadd 		pin->gp_flags = 0;
527*6b7b2d80SAdrian Chadd 
528*6b7b2d80SAdrian Chadd 		snprintf(pin->gp_name, GPIOMAXNAME, "GPIO%02u", i);
529*6b7b2d80SAdrian Chadd 		pin->gp_name[GPIOMAXNAME - 1] = '\0';
530*6b7b2d80SAdrian Chadd 
531*6b7b2d80SAdrian Chadd 		if (nct_pin_is_input(sc, i))
532*6b7b2d80SAdrian Chadd 			pin->gp_flags |= GPIO_PIN_INPUT;
533*6b7b2d80SAdrian Chadd 		else
534*6b7b2d80SAdrian Chadd 			pin->gp_flags |= GPIO_PIN_OUTPUT;
535*6b7b2d80SAdrian Chadd 
536*6b7b2d80SAdrian Chadd 		if (nct_pin_is_opendrain(sc, i))
537*6b7b2d80SAdrian Chadd 			pin->gp_flags |= GPIO_PIN_OPENDRAIN;
538*6b7b2d80SAdrian Chadd 		else
539*6b7b2d80SAdrian Chadd 			pin->gp_flags |= GPIO_PIN_PUSHPULL;
540*6b7b2d80SAdrian Chadd 
541*6b7b2d80SAdrian Chadd 		if (nct_pin_is_inverted(sc, i))
542*6b7b2d80SAdrian Chadd 			pin->gp_flags |= (GPIO_PIN_INVIN | GPIO_PIN_INVOUT);
543*6b7b2d80SAdrian Chadd 	}
544*6b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
545*6b7b2d80SAdrian Chadd 
546*6b7b2d80SAdrian Chadd 	sc->busdev = gpiobus_attach_bus(dev);
547*6b7b2d80SAdrian Chadd 	if (sc->busdev == NULL) {
548*6b7b2d80SAdrian Chadd 		GPIO_ASSERT_UNLOCKED(sc);
549*6b7b2d80SAdrian Chadd 		GPIO_LOCK(sc);
550*6b7b2d80SAdrian Chadd 		ext_cfg_exit(sc);
551*6b7b2d80SAdrian Chadd 		GPIO_UNLOCK(sc);
552*6b7b2d80SAdrian Chadd 		bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
553*6b7b2d80SAdrian Chadd 		GPIO_LOCK_DESTROY(sc);
554*6b7b2d80SAdrian Chadd 
555*6b7b2d80SAdrian Chadd 		return (ENXIO);
556*6b7b2d80SAdrian Chadd 	}
557*6b7b2d80SAdrian Chadd 
558*6b7b2d80SAdrian Chadd 	return (0);
559*6b7b2d80SAdrian Chadd }
560*6b7b2d80SAdrian Chadd 
561*6b7b2d80SAdrian Chadd static int
562*6b7b2d80SAdrian Chadd nct_detach(device_t dev)
563*6b7b2d80SAdrian Chadd {
564*6b7b2d80SAdrian Chadd 	struct nct_softc *sc;
565*6b7b2d80SAdrian Chadd 
566*6b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
567*6b7b2d80SAdrian Chadd 	gpiobus_detach_bus(dev);
568*6b7b2d80SAdrian Chadd 
569*6b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
570*6b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
571*6b7b2d80SAdrian Chadd 	ext_cfg_exit(sc);
572*6b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
573*6b7b2d80SAdrian Chadd 
574*6b7b2d80SAdrian Chadd 	/* Cleanup resources. */
575*6b7b2d80SAdrian Chadd 	bus_release_resource(dev, SYS_RES_IOPORT, sc->rid, sc->portres);
576*6b7b2d80SAdrian Chadd 
577*6b7b2d80SAdrian Chadd 	GPIO_LOCK_DESTROY(sc);
578*6b7b2d80SAdrian Chadd 
579*6b7b2d80SAdrian Chadd 	return (0);
580*6b7b2d80SAdrian Chadd }
581*6b7b2d80SAdrian Chadd 
582*6b7b2d80SAdrian Chadd static device_t
583*6b7b2d80SAdrian Chadd nct_gpio_get_bus(device_t dev)
584*6b7b2d80SAdrian Chadd {
585*6b7b2d80SAdrian Chadd 	struct nct_softc *sc;
586*6b7b2d80SAdrian Chadd 
587*6b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
588*6b7b2d80SAdrian Chadd 
589*6b7b2d80SAdrian Chadd 	return (sc->busdev);
590*6b7b2d80SAdrian Chadd }
591*6b7b2d80SAdrian Chadd 
592*6b7b2d80SAdrian Chadd static int
593*6b7b2d80SAdrian Chadd nct_gpio_pin_max(device_t dev, int *npins)
594*6b7b2d80SAdrian Chadd {
595*6b7b2d80SAdrian Chadd 	*npins = NCT_MAX_PIN;
596*6b7b2d80SAdrian Chadd 
597*6b7b2d80SAdrian Chadd 	return (0);
598*6b7b2d80SAdrian Chadd }
599*6b7b2d80SAdrian Chadd 
600*6b7b2d80SAdrian Chadd static int
601*6b7b2d80SAdrian Chadd nct_gpio_pin_set(device_t dev, uint32_t pin_num, uint32_t pin_value)
602*6b7b2d80SAdrian Chadd {
603*6b7b2d80SAdrian Chadd 	struct nct_softc *sc;
604*6b7b2d80SAdrian Chadd 
605*6b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
606*6b7b2d80SAdrian Chadd 		return (EINVAL);
607*6b7b2d80SAdrian Chadd 
608*6b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
609*6b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
610*6b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
611*6b7b2d80SAdrian Chadd 	nct_write_pin(sc, pin_num, pin_value);
612*6b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
613*6b7b2d80SAdrian Chadd 
614*6b7b2d80SAdrian Chadd 	return (0);
615*6b7b2d80SAdrian Chadd }
616*6b7b2d80SAdrian Chadd 
617*6b7b2d80SAdrian Chadd static int
618*6b7b2d80SAdrian Chadd nct_gpio_pin_get(device_t dev, uint32_t pin_num, uint32_t *pin_value)
619*6b7b2d80SAdrian Chadd {
620*6b7b2d80SAdrian Chadd 	struct nct_softc *sc;
621*6b7b2d80SAdrian Chadd 
622*6b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
623*6b7b2d80SAdrian Chadd 		return (EINVAL);
624*6b7b2d80SAdrian Chadd 
625*6b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
626*6b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
627*6b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
628*6b7b2d80SAdrian Chadd 	*pin_value = nct_read_pin(sc, pin_num);
629*6b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
630*6b7b2d80SAdrian Chadd 
631*6b7b2d80SAdrian Chadd 	return (0);
632*6b7b2d80SAdrian Chadd }
633*6b7b2d80SAdrian Chadd 
634*6b7b2d80SAdrian Chadd static int
635*6b7b2d80SAdrian Chadd nct_gpio_pin_toggle(device_t dev, uint32_t pin_num)
636*6b7b2d80SAdrian Chadd {
637*6b7b2d80SAdrian Chadd 	struct nct_softc *sc;
638*6b7b2d80SAdrian Chadd 
639*6b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
640*6b7b2d80SAdrian Chadd 		return (EINVAL);
641*6b7b2d80SAdrian Chadd 
642*6b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
643*6b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
644*6b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
645*6b7b2d80SAdrian Chadd 	if (nct_read_pin(sc, pin_num))
646*6b7b2d80SAdrian Chadd 		nct_write_pin(sc, pin_num, 0);
647*6b7b2d80SAdrian Chadd 	else
648*6b7b2d80SAdrian Chadd 		nct_write_pin(sc, pin_num, 1);
649*6b7b2d80SAdrian Chadd 
650*6b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
651*6b7b2d80SAdrian Chadd 
652*6b7b2d80SAdrian Chadd 	return (0);
653*6b7b2d80SAdrian Chadd }
654*6b7b2d80SAdrian Chadd 
655*6b7b2d80SAdrian Chadd static int
656*6b7b2d80SAdrian Chadd nct_gpio_pin_getcaps(device_t dev, uint32_t pin_num, uint32_t *caps)
657*6b7b2d80SAdrian Chadd {
658*6b7b2d80SAdrian Chadd 	struct nct_softc *sc;
659*6b7b2d80SAdrian Chadd 
660*6b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
661*6b7b2d80SAdrian Chadd 		return (EINVAL);
662*6b7b2d80SAdrian Chadd 
663*6b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
664*6b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
665*6b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
666*6b7b2d80SAdrian Chadd 	*caps = sc->pins[pin_num].gp_caps;
667*6b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
668*6b7b2d80SAdrian Chadd 
669*6b7b2d80SAdrian Chadd 	return (0);
670*6b7b2d80SAdrian Chadd }
671*6b7b2d80SAdrian Chadd 
672*6b7b2d80SAdrian Chadd static int
673*6b7b2d80SAdrian Chadd nct_gpio_pin_getflags(device_t dev, uint32_t pin_num, uint32_t *flags)
674*6b7b2d80SAdrian Chadd {
675*6b7b2d80SAdrian Chadd 	struct nct_softc *sc;
676*6b7b2d80SAdrian Chadd 
677*6b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
678*6b7b2d80SAdrian Chadd 		return (EINVAL);
679*6b7b2d80SAdrian Chadd 
680*6b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
681*6b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
682*6b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
683*6b7b2d80SAdrian Chadd 	*flags = sc->pins[pin_num].gp_flags;
684*6b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
685*6b7b2d80SAdrian Chadd 
686*6b7b2d80SAdrian Chadd 	return (0);
687*6b7b2d80SAdrian Chadd }
688*6b7b2d80SAdrian Chadd 
689*6b7b2d80SAdrian Chadd static int
690*6b7b2d80SAdrian Chadd nct_gpio_pin_getname(device_t dev, uint32_t pin_num, char *name)
691*6b7b2d80SAdrian Chadd {
692*6b7b2d80SAdrian Chadd 	struct nct_softc *sc;
693*6b7b2d80SAdrian Chadd 
694*6b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
695*6b7b2d80SAdrian Chadd 		return (EINVAL);
696*6b7b2d80SAdrian Chadd 
697*6b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
698*6b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
699*6b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
700*6b7b2d80SAdrian Chadd 	memcpy(name, sc->pins[pin_num].gp_name, GPIOMAXNAME);
701*6b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
702*6b7b2d80SAdrian Chadd 
703*6b7b2d80SAdrian Chadd 	return (0);
704*6b7b2d80SAdrian Chadd }
705*6b7b2d80SAdrian Chadd 
706*6b7b2d80SAdrian Chadd static int
707*6b7b2d80SAdrian Chadd nct_gpio_pin_setflags(device_t dev, uint32_t pin_num, uint32_t flags)
708*6b7b2d80SAdrian Chadd {
709*6b7b2d80SAdrian Chadd 	struct nct_softc *sc;
710*6b7b2d80SAdrian Chadd 	struct gpio_pin *pin;
711*6b7b2d80SAdrian Chadd 
712*6b7b2d80SAdrian Chadd 	if (!NCT_IS_VALID_PIN(pin_num))
713*6b7b2d80SAdrian Chadd 		return (EINVAL);
714*6b7b2d80SAdrian Chadd 
715*6b7b2d80SAdrian Chadd 	sc = device_get_softc(dev);
716*6b7b2d80SAdrian Chadd 	pin = &sc->pins[pin_num];
717*6b7b2d80SAdrian Chadd 	if ((flags & pin->gp_caps) != flags)
718*6b7b2d80SAdrian Chadd 		return (EINVAL);
719*6b7b2d80SAdrian Chadd 
720*6b7b2d80SAdrian Chadd 	GPIO_ASSERT_UNLOCKED(sc);
721*6b7b2d80SAdrian Chadd 	GPIO_LOCK(sc);
722*6b7b2d80SAdrian Chadd 	if (flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
723*6b7b2d80SAdrian Chadd 		if ((flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) ==
724*6b7b2d80SAdrian Chadd 			(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) {
725*6b7b2d80SAdrian Chadd 				GPIO_UNLOCK(sc);
726*6b7b2d80SAdrian Chadd 				return (EINVAL);
727*6b7b2d80SAdrian Chadd 		}
728*6b7b2d80SAdrian Chadd 
729*6b7b2d80SAdrian Chadd 		if (flags & GPIO_PIN_INPUT)
730*6b7b2d80SAdrian Chadd 			nct_set_pin_is_input(sc, pin_num);
731*6b7b2d80SAdrian Chadd 		else
732*6b7b2d80SAdrian Chadd 			nct_set_pin_is_output(sc, pin_num);
733*6b7b2d80SAdrian Chadd 	}
734*6b7b2d80SAdrian Chadd 
735*6b7b2d80SAdrian Chadd 	if (flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) {
736*6b7b2d80SAdrian Chadd 		if (flags & GPIO_PIN_INPUT) {
737*6b7b2d80SAdrian Chadd 			GPIO_UNLOCK(sc);
738*6b7b2d80SAdrian Chadd 			return (EINVAL);
739*6b7b2d80SAdrian Chadd 		}
740*6b7b2d80SAdrian Chadd 
741*6b7b2d80SAdrian Chadd 		if ((flags & (GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) ==
742*6b7b2d80SAdrian Chadd 			(GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL)) {
743*6b7b2d80SAdrian Chadd 				GPIO_UNLOCK(sc);
744*6b7b2d80SAdrian Chadd 				return (EINVAL);
745*6b7b2d80SAdrian Chadd 		}
746*6b7b2d80SAdrian Chadd 
747*6b7b2d80SAdrian Chadd 		if (flags & GPIO_PIN_OPENDRAIN)
748*6b7b2d80SAdrian Chadd 			nct_set_pin_opendrain(sc, pin_num);
749*6b7b2d80SAdrian Chadd 		else
750*6b7b2d80SAdrian Chadd 			nct_set_pin_pushpull(sc, pin_num);
751*6b7b2d80SAdrian Chadd 	}
752*6b7b2d80SAdrian Chadd 
753*6b7b2d80SAdrian Chadd 	if (flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) {
754*6b7b2d80SAdrian Chadd 		if ((flags & (GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) !=
755*6b7b2d80SAdrian Chadd 			(GPIO_PIN_INVIN | GPIO_PIN_INVOUT)) {
756*6b7b2d80SAdrian Chadd 				GPIO_UNLOCK(sc);
757*6b7b2d80SAdrian Chadd 				return (EINVAL);
758*6b7b2d80SAdrian Chadd 		}
759*6b7b2d80SAdrian Chadd 
760*6b7b2d80SAdrian Chadd 		if (flags & GPIO_PIN_INVIN)
761*6b7b2d80SAdrian Chadd 			nct_set_pin_is_inverted(sc, pin_num);
762*6b7b2d80SAdrian Chadd 		else
763*6b7b2d80SAdrian Chadd 			nct_set_pin_not_inverted(sc, pin_num);
764*6b7b2d80SAdrian Chadd 	}
765*6b7b2d80SAdrian Chadd 
766*6b7b2d80SAdrian Chadd 	pin->gp_flags = flags;
767*6b7b2d80SAdrian Chadd 	GPIO_UNLOCK(sc);
768*6b7b2d80SAdrian Chadd 
769*6b7b2d80SAdrian Chadd 	return (0);
770*6b7b2d80SAdrian Chadd }
771*6b7b2d80SAdrian Chadd 
772*6b7b2d80SAdrian Chadd static device_method_t nct_methods[] = {
773*6b7b2d80SAdrian Chadd 	/* Device interface */
774*6b7b2d80SAdrian Chadd 	DEVMETHOD(device_identify,	nct_identify),
775*6b7b2d80SAdrian Chadd 	DEVMETHOD(device_probe,		nct_probe),
776*6b7b2d80SAdrian Chadd 	DEVMETHOD(device_attach,	nct_attach),
777*6b7b2d80SAdrian Chadd 	DEVMETHOD(device_detach,	nct_detach),
778*6b7b2d80SAdrian Chadd 
779*6b7b2d80SAdrian Chadd 	/* GPIO */
780*6b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_get_bus,			nct_gpio_get_bus),
781*6b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_max,			nct_gpio_pin_max),
782*6b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_get,			nct_gpio_pin_get),
783*6b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_set,			nct_gpio_pin_set),
784*6b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_toggle,		nct_gpio_pin_toggle),
785*6b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getname,		nct_gpio_pin_getname),
786*6b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getcaps,		nct_gpio_pin_getcaps),
787*6b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_getflags,	nct_gpio_pin_getflags),
788*6b7b2d80SAdrian Chadd 	DEVMETHOD(gpio_pin_setflags,	nct_gpio_pin_setflags),
789*6b7b2d80SAdrian Chadd 
790*6b7b2d80SAdrian Chadd 	DEVMETHOD_END
791*6b7b2d80SAdrian Chadd };
792*6b7b2d80SAdrian Chadd 
793*6b7b2d80SAdrian Chadd static driver_t nct_isa_driver = {
794*6b7b2d80SAdrian Chadd 	"gpio",
795*6b7b2d80SAdrian Chadd 	nct_methods,
796*6b7b2d80SAdrian Chadd 	sizeof(struct nct_softc)
797*6b7b2d80SAdrian Chadd };
798*6b7b2d80SAdrian Chadd 
799*6b7b2d80SAdrian Chadd static devclass_t nct_devclass;
800*6b7b2d80SAdrian Chadd 
801*6b7b2d80SAdrian Chadd DRIVER_MODULE(nctgpio, isa, nct_isa_driver, nct_devclass, NULL, NULL);
802*6b7b2d80SAdrian Chadd MODULE_DEPEND(nctgpio, gpiobus, 1, 1, 1);
803