xref: /freebsd/sys/arm/nvidia/as3722_gpio.c (revision dca7f696322a06247a1f73ad0e843ff9795457a1)
1ef2ee5d0SMichal Meloun /*-
2ef2ee5d0SMichal Meloun  * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org>
3ef2ee5d0SMichal Meloun  * All rights reserved.
4ef2ee5d0SMichal Meloun  *
5ef2ee5d0SMichal Meloun  * Redistribution and use in source and binary forms, with or without
6ef2ee5d0SMichal Meloun  * modification, are permitted provided that the following conditions
7ef2ee5d0SMichal Meloun  * are met:
8ef2ee5d0SMichal Meloun  * 1. Redistributions of source code must retain the above copyright
9ef2ee5d0SMichal Meloun  *    notice, this list of conditions and the following disclaimer.
10ef2ee5d0SMichal Meloun  * 2. Redistributions in binary form must reproduce the above copyright
11ef2ee5d0SMichal Meloun  *    notice, this list of conditions and the following disclaimer in the
12ef2ee5d0SMichal Meloun  *    documentation and/or other materials provided with the distribution.
13ef2ee5d0SMichal Meloun  *
14ef2ee5d0SMichal Meloun  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15ef2ee5d0SMichal Meloun  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16ef2ee5d0SMichal Meloun  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17ef2ee5d0SMichal Meloun  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18ef2ee5d0SMichal Meloun  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19ef2ee5d0SMichal Meloun  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20ef2ee5d0SMichal Meloun  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21ef2ee5d0SMichal Meloun  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22ef2ee5d0SMichal Meloun  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23ef2ee5d0SMichal Meloun  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24ef2ee5d0SMichal Meloun  * SUCH DAMAGE.
25ef2ee5d0SMichal Meloun  */
26ef2ee5d0SMichal Meloun 
27ef2ee5d0SMichal Meloun #include <sys/cdefs.h>
28ef2ee5d0SMichal Meloun __FBSDID("$FreeBSD$");
29ef2ee5d0SMichal Meloun 
30ef2ee5d0SMichal Meloun #include <sys/param.h>
31ef2ee5d0SMichal Meloun #include <sys/systm.h>
32ef2ee5d0SMichal Meloun #include <sys/bus.h>
33ef2ee5d0SMichal Meloun #include <sys/gpio.h>
34ef2ee5d0SMichal Meloun #include <sys/kernel.h>
35ef2ee5d0SMichal Meloun #include <sys/malloc.h>
36ef2ee5d0SMichal Meloun #include <sys/sx.h>
37ef2ee5d0SMichal Meloun 
38ef2ee5d0SMichal Meloun #include <machine/bus.h>
39ef2ee5d0SMichal Meloun 
40ef2ee5d0SMichal Meloun #include <dev/fdt/fdt_common.h>
41ef2ee5d0SMichal Meloun #include <dev/gpio/gpiobusvar.h>
42ef2ee5d0SMichal Meloun 
43ef2ee5d0SMichal Meloun #include "as3722.h"
44ef2ee5d0SMichal Meloun 
45ef2ee5d0SMichal Meloun MALLOC_DEFINE(M_AS3722_GPIO, "AS3722 gpio", "AS3722 GPIO");
46ef2ee5d0SMichal Meloun 
47ef2ee5d0SMichal Meloun /* AS3722_GPIOx_CONTROL	 MODE and IOSF definition. */
48ef2ee5d0SMichal Meloun #define	AS3722_IOSF_GPIO				0x00
49ef2ee5d0SMichal Meloun #define	AS3722_IOSF_INTERRUPT_OUT			0x01
50ef2ee5d0SMichal Meloun #define	AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT	0x02
51ef2ee5d0SMichal Meloun #define	AS3722_IOSF_GPIO_IN_INTERRUPT			0x03
52ef2ee5d0SMichal Meloun #define	AS3722_IOSF_PWM_IN				0x04
53ef2ee5d0SMichal Meloun #define	AS3722_IOSF_VOLTAGE_IN_STANDBY			0x05
54ef2ee5d0SMichal Meloun #define	AS3722_IOSF_OC_PG_SD0				0x06
55ef2ee5d0SMichal Meloun #define	AS3722_IOSF_POWERGOOD_OUT			0x07
56ef2ee5d0SMichal Meloun #define	AS3722_IOSF_CLK32K_OUT				0x08
57ef2ee5d0SMichal Meloun #define	AS3722_IOSF_WATCHDOG_IN				0x09
58ef2ee5d0SMichal Meloun #define	AS3722_IOSF_SOFT_RESET_IN			0x0b
59ef2ee5d0SMichal Meloun #define	AS3722_IOSF_PWM_OUT				0x0c
60ef2ee5d0SMichal Meloun #define	AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT		0x0d
61ef2ee5d0SMichal Meloun #define	AS3722_IOSF_OC_PG_SD6				0x0e
62ef2ee5d0SMichal Meloun 
63ef2ee5d0SMichal Meloun #define	AS3722_MODE_INPUT				0
64ef2ee5d0SMichal Meloun #define	AS3722_MODE_PUSH_PULL				1
65ef2ee5d0SMichal Meloun #define	AS3722_MODE_OPEN_DRAIN				2
66ef2ee5d0SMichal Meloun #define	AS3722_MODE_TRISTATE				3
67ef2ee5d0SMichal Meloun #define	AS3722_MODE_INPUT_PULL_UP_LV			4
68ef2ee5d0SMichal Meloun #define	AS3722_MODE_INPUT_PULL_DOWN			5
69ef2ee5d0SMichal Meloun #define	AS3722_MODE_OPEN_DRAIN_LV			6
70ef2ee5d0SMichal Meloun #define	AS3722_MODE_PUSH_PULL_LV			7
71ef2ee5d0SMichal Meloun 
72ef2ee5d0SMichal Meloun #define	NGPIO		8
73ef2ee5d0SMichal Meloun 
74ef2ee5d0SMichal Meloun #define	GPIO_LOCK(_sc)	sx_slock(&(_sc)->gpio_lock)
75ef2ee5d0SMichal Meloun #define	GPIO_UNLOCK(_sc)	sx_unlock(&(_sc)->gpio_lock)
76ef2ee5d0SMichal Meloun #define	GPIO_ASSERT(_sc)	sx_assert(&(_sc)->gpio_lock, SA_LOCKED)
77ef2ee5d0SMichal Meloun 
78ef2ee5d0SMichal Meloun #define	AS3722_CFG_BIAS_DISABLE		0x0001
79ef2ee5d0SMichal Meloun #define	AS3722_CFG_BIAS_PULL_UP		0x0002
80ef2ee5d0SMichal Meloun #define	AS3722_CFG_BIAS_PULL_DOWN	0x0004
81ef2ee5d0SMichal Meloun #define	AS3722_CFG_BIAS_HIGH_IMPEDANCE	0x0008
82ef2ee5d0SMichal Meloun #define	AS3722_CFG_OPEN_DRAIN		0x0010
83ef2ee5d0SMichal Meloun 
84ef2ee5d0SMichal Meloun static const struct {
85ef2ee5d0SMichal Meloun 	const char	*name;
86ef2ee5d0SMichal Meloun 	int  		config;		/* AS3722_CFG_  */
87ef2ee5d0SMichal Meloun } as3722_cfg_names[] = {
88ef2ee5d0SMichal Meloun 	{"bias-disable",	AS3722_CFG_BIAS_DISABLE},
89ef2ee5d0SMichal Meloun 	{"bias-pull-up",	AS3722_CFG_BIAS_PULL_UP},
90ef2ee5d0SMichal Meloun 	{"bias-pull-down",	AS3722_CFG_BIAS_PULL_DOWN},
91ef2ee5d0SMichal Meloun 	{"bias-high-impedance",	AS3722_CFG_BIAS_HIGH_IMPEDANCE},
92ef2ee5d0SMichal Meloun 	{"drive-open-drain",	AS3722_CFG_OPEN_DRAIN},
93ef2ee5d0SMichal Meloun };
94ef2ee5d0SMichal Meloun 
95ef2ee5d0SMichal Meloun static struct {
96ef2ee5d0SMichal Meloun 	const char *name;
97ef2ee5d0SMichal Meloun 	int fnc_val;
98ef2ee5d0SMichal Meloun } as3722_fnc_table[] = {
99ef2ee5d0SMichal Meloun 	{"gpio",			AS3722_IOSF_GPIO},
100ef2ee5d0SMichal Meloun 	{"interrupt-out",		AS3722_IOSF_INTERRUPT_OUT},
101ef2ee5d0SMichal Meloun 	{"vsup-vbat-low-undebounce-out", AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT},
102ef2ee5d0SMichal Meloun 	{"gpio-in-interrupt",		AS3722_IOSF_GPIO_IN_INTERRUPT},
103ef2ee5d0SMichal Meloun 	{"pwm-in",			AS3722_IOSF_PWM_IN},
104ef2ee5d0SMichal Meloun 	{"voltage-in-standby",		AS3722_IOSF_VOLTAGE_IN_STANDBY},
105ef2ee5d0SMichal Meloun 	{"oc-pg-sd0",			AS3722_IOSF_OC_PG_SD0},
106ef2ee5d0SMichal Meloun 	{"powergood-out",		AS3722_IOSF_POWERGOOD_OUT},
107ef2ee5d0SMichal Meloun 	{"clk32k-out",			AS3722_IOSF_CLK32K_OUT},
108ef2ee5d0SMichal Meloun 	{"watchdog-in",			AS3722_IOSF_WATCHDOG_IN},
109ef2ee5d0SMichal Meloun 	{"soft-reset-in",		AS3722_IOSF_SOFT_RESET_IN},
110ef2ee5d0SMichal Meloun 	{"pwm-out",			AS3722_IOSF_PWM_OUT},
111ef2ee5d0SMichal Meloun 	{"vsup-vbat-low-debounce-out",	AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT},
112ef2ee5d0SMichal Meloun 	{"oc-pg-sd6",			AS3722_IOSF_OC_PG_SD6},
113ef2ee5d0SMichal Meloun };
114ef2ee5d0SMichal Meloun 
115ef2ee5d0SMichal Meloun struct as3722_pincfg {
116ef2ee5d0SMichal Meloun 	char	*function;
117ef2ee5d0SMichal Meloun 	int	flags;
118ef2ee5d0SMichal Meloun };
119ef2ee5d0SMichal Meloun 
120ef2ee5d0SMichal Meloun struct as3722_gpio_pin {
121ef2ee5d0SMichal Meloun 	int	pin_caps;
122ef2ee5d0SMichal Meloun 	uint8_t	pin_ctrl_reg;
123ef2ee5d0SMichal Meloun 	char	pin_name[GPIOMAXNAME];
124ef2ee5d0SMichal Meloun 	int	pin_cfg_flags;
125ef2ee5d0SMichal Meloun };
126ef2ee5d0SMichal Meloun 
127ef2ee5d0SMichal Meloun /* --------------------------------------------------------------------------
128ef2ee5d0SMichal Meloun  *
129ef2ee5d0SMichal Meloun  *  Pinmux functions.
130ef2ee5d0SMichal Meloun  */
131ef2ee5d0SMichal Meloun static int
132ef2ee5d0SMichal Meloun as3722_pinmux_get_function(struct as3722_softc *sc, char *name)
133ef2ee5d0SMichal Meloun {
134ef2ee5d0SMichal Meloun 	int i;
135ef2ee5d0SMichal Meloun 
136ef2ee5d0SMichal Meloun 	for (i = 0; i < nitems(as3722_fnc_table); i++) {
137ef2ee5d0SMichal Meloun 		if (strcmp(as3722_fnc_table[i].name, name) == 0)
138ef2ee5d0SMichal Meloun 			 return (as3722_fnc_table[i].fnc_val);
139ef2ee5d0SMichal Meloun 	}
140ef2ee5d0SMichal Meloun 	return (-1);
141ef2ee5d0SMichal Meloun }
142ef2ee5d0SMichal Meloun 
143ef2ee5d0SMichal Meloun static int
144ef2ee5d0SMichal Meloun as3722_pinmux_config_node(struct as3722_softc *sc, char *pin_name,
145ef2ee5d0SMichal Meloun     struct as3722_pincfg *cfg)
146ef2ee5d0SMichal Meloun {
147ef2ee5d0SMichal Meloun 	uint8_t ctrl;
148ef2ee5d0SMichal Meloun 	int rv, fnc, pin;
149ef2ee5d0SMichal Meloun 
150ef2ee5d0SMichal Meloun 	for (pin = 0; pin < sc->gpio_npins; pin++) {
151ef2ee5d0SMichal Meloun 		if (strcmp(sc->gpio_pins[pin]->pin_name, pin_name) == 0)
152ef2ee5d0SMichal Meloun 			 break;
153ef2ee5d0SMichal Meloun 	}
154ef2ee5d0SMichal Meloun 	if (pin >= sc->gpio_npins) {
155ef2ee5d0SMichal Meloun 		device_printf(sc->dev, "Unknown pin: %s\n", pin_name);
156ef2ee5d0SMichal Meloun 		return (ENXIO);
157ef2ee5d0SMichal Meloun 	}
158ef2ee5d0SMichal Meloun 
159ef2ee5d0SMichal Meloun 	ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
160ef2ee5d0SMichal Meloun 	sc->gpio_pins[pin]->pin_cfg_flags = cfg->flags;
161ef2ee5d0SMichal Meloun 	if (cfg->function != NULL) {
162ef2ee5d0SMichal Meloun 		fnc = as3722_pinmux_get_function(sc, cfg->function);
163ef2ee5d0SMichal Meloun 		if (fnc == -1) {
164ef2ee5d0SMichal Meloun 			device_printf(sc->dev,
165ef2ee5d0SMichal Meloun 			    "Unknown function %s for pin %s\n", cfg->function,
166ef2ee5d0SMichal Meloun 			    sc->gpio_pins[pin]->pin_name);
167ef2ee5d0SMichal Meloun 			return (ENXIO);
168ef2ee5d0SMichal Meloun 		}
169ef2ee5d0SMichal Meloun 		switch (fnc) {
170ef2ee5d0SMichal Meloun 		case AS3722_IOSF_INTERRUPT_OUT:
171ef2ee5d0SMichal Meloun 		case AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT:
172ef2ee5d0SMichal Meloun 		case AS3722_IOSF_OC_PG_SD0:
173ef2ee5d0SMichal Meloun 		case AS3722_IOSF_POWERGOOD_OUT:
174ef2ee5d0SMichal Meloun 		case AS3722_IOSF_CLK32K_OUT:
175ef2ee5d0SMichal Meloun 		case AS3722_IOSF_PWM_OUT:
176ef2ee5d0SMichal Meloun 		case AS3722_IOSF_OC_PG_SD6:
177ef2ee5d0SMichal Meloun 			ctrl &= ~(AS3722_GPIO_MODE_MASK <<
178ef2ee5d0SMichal Meloun 			    AS3722_GPIO_MODE_SHIFT);
179ef2ee5d0SMichal Meloun 			ctrl |= AS3722_MODE_PUSH_PULL << AS3722_GPIO_MODE_SHIFT;
180ef2ee5d0SMichal Meloun 			/* XXX Handle flags (OC + pullup) */
181ef2ee5d0SMichal Meloun 			break;
182ef2ee5d0SMichal Meloun 		case AS3722_IOSF_GPIO_IN_INTERRUPT:
183ef2ee5d0SMichal Meloun 		case AS3722_IOSF_PWM_IN:
184ef2ee5d0SMichal Meloun 		case AS3722_IOSF_VOLTAGE_IN_STANDBY:
185ef2ee5d0SMichal Meloun 		case AS3722_IOSF_WATCHDOG_IN:
186ef2ee5d0SMichal Meloun 		case AS3722_IOSF_SOFT_RESET_IN:
187ef2ee5d0SMichal Meloun 			ctrl &= ~(AS3722_GPIO_MODE_MASK <<
188ef2ee5d0SMichal Meloun 			    AS3722_GPIO_MODE_SHIFT);
189ef2ee5d0SMichal Meloun 			ctrl |= AS3722_MODE_INPUT << AS3722_GPIO_MODE_SHIFT;
190ef2ee5d0SMichal Meloun 			/* XXX Handle flags (pulldown + pullup) */
191ef2ee5d0SMichal Meloun 
192ef2ee5d0SMichal Meloun 		default:
193ef2ee5d0SMichal Meloun 			break;
194ef2ee5d0SMichal Meloun 		}
195ef2ee5d0SMichal Meloun 		ctrl &= ~(AS3722_GPIO_IOSF_MASK << AS3722_GPIO_IOSF_SHIFT);
196ef2ee5d0SMichal Meloun 		ctrl |= fnc << AS3722_GPIO_IOSF_SHIFT;
197ef2ee5d0SMichal Meloun 	}
198ef2ee5d0SMichal Meloun 	rv = 0;
199ef2ee5d0SMichal Meloun 	if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) {
200ef2ee5d0SMichal Meloun 		rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl);
201ef2ee5d0SMichal Meloun 		sc->gpio_pins[pin]->pin_ctrl_reg = ctrl;
202ef2ee5d0SMichal Meloun 	}
203ef2ee5d0SMichal Meloun 	return (rv);
204ef2ee5d0SMichal Meloun }
205ef2ee5d0SMichal Meloun 
206ef2ee5d0SMichal Meloun static int
207ef2ee5d0SMichal Meloun as3722_pinmux_read_node(struct as3722_softc *sc, phandle_t node,
208ef2ee5d0SMichal Meloun      struct as3722_pincfg *cfg, char **pins, int *lpins)
209ef2ee5d0SMichal Meloun {
210ef2ee5d0SMichal Meloun 	int rv, i;
211ef2ee5d0SMichal Meloun 
212217d17bcSOleksandr Tymoshenko 	*lpins = OF_getprop_alloc(node, "pins", (void **)pins);
213ef2ee5d0SMichal Meloun 	if (*lpins <= 0)
214ef2ee5d0SMichal Meloun 		return (ENOENT);
215ef2ee5d0SMichal Meloun 
216ef2ee5d0SMichal Meloun 	/* Read function (mux) settings. */
217217d17bcSOleksandr Tymoshenko 	rv = OF_getprop_alloc(node, "function", (void **)&cfg->function);
218ef2ee5d0SMichal Meloun 	if (rv <= 0)
219ef2ee5d0SMichal Meloun 		cfg->function = NULL;
220ef2ee5d0SMichal Meloun 
221ef2ee5d0SMichal Meloun 	/* Read boolean properties. */
222ef2ee5d0SMichal Meloun 	for (i = 0; i < nitems(as3722_cfg_names); i++) {
223ef2ee5d0SMichal Meloun 		if (OF_hasprop(node, as3722_cfg_names[i].name))
224ef2ee5d0SMichal Meloun 			cfg->flags |= as3722_cfg_names[i].config;
225ef2ee5d0SMichal Meloun 	}
226ef2ee5d0SMichal Meloun 	return (0);
227ef2ee5d0SMichal Meloun }
228ef2ee5d0SMichal Meloun 
229ef2ee5d0SMichal Meloun static int
230ef2ee5d0SMichal Meloun as3722_pinmux_process_node(struct as3722_softc *sc, phandle_t node)
231ef2ee5d0SMichal Meloun {
232ef2ee5d0SMichal Meloun 	struct as3722_pincfg cfg;
233ef2ee5d0SMichal Meloun 	char *pins, *pname;
234ef2ee5d0SMichal Meloun 	int i, len, lpins, rv;
235ef2ee5d0SMichal Meloun 
236ef2ee5d0SMichal Meloun 	rv = as3722_pinmux_read_node(sc, node, &cfg, &pins, &lpins);
237ef2ee5d0SMichal Meloun 	if (rv != 0)
238ef2ee5d0SMichal Meloun 		return (rv);
239ef2ee5d0SMichal Meloun 
240ef2ee5d0SMichal Meloun 	len = 0;
241ef2ee5d0SMichal Meloun 	pname = pins;
242ef2ee5d0SMichal Meloun 	do {
243ef2ee5d0SMichal Meloun 		i = strlen(pname) + 1;
244ef2ee5d0SMichal Meloun 		rv = as3722_pinmux_config_node(sc, pname, &cfg);
245ef2ee5d0SMichal Meloun 		if (rv != 0) {
246ef2ee5d0SMichal Meloun 			device_printf(sc->dev,
247ef2ee5d0SMichal Meloun 			    "Cannot configure pin: %s: %d\n", pname, rv);
248ef2ee5d0SMichal Meloun 		}
249ef2ee5d0SMichal Meloun 		len += i;
250ef2ee5d0SMichal Meloun 		pname += i;
251ef2ee5d0SMichal Meloun 	} while (len < lpins);
252ef2ee5d0SMichal Meloun 
253ef2ee5d0SMichal Meloun 	if (pins != NULL)
254bebd5269SOleksandr Tymoshenko 		OF_prop_free(pins);
255ef2ee5d0SMichal Meloun 	if (cfg.function != NULL)
256bebd5269SOleksandr Tymoshenko 		OF_prop_free(cfg.function);
257ef2ee5d0SMichal Meloun 
258ef2ee5d0SMichal Meloun 	return (rv);
259ef2ee5d0SMichal Meloun }
260ef2ee5d0SMichal Meloun 
261ef2ee5d0SMichal Meloun int as3722_pinmux_configure(device_t dev, phandle_t cfgxref)
262ef2ee5d0SMichal Meloun {
263ef2ee5d0SMichal Meloun 	struct as3722_softc *sc;
264ef2ee5d0SMichal Meloun 	phandle_t node, cfgnode;
265ef2ee5d0SMichal Meloun 	int rv;
266ef2ee5d0SMichal Meloun 
267ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
268ef2ee5d0SMichal Meloun 	cfgnode = OF_node_from_xref(cfgxref);
269ef2ee5d0SMichal Meloun 
270ef2ee5d0SMichal Meloun 	for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) {
2717bc28467SAndrew Turner 		if (!ofw_bus_node_status_okay(node))
272ef2ee5d0SMichal Meloun 			continue;
273ef2ee5d0SMichal Meloun 		rv = as3722_pinmux_process_node(sc, node);
274ef2ee5d0SMichal Meloun 		if (rv != 0)
275ef2ee5d0SMichal Meloun 			device_printf(dev, "Failed to process pinmux");
276ef2ee5d0SMichal Meloun 	}
277ef2ee5d0SMichal Meloun 	return (0);
278ef2ee5d0SMichal Meloun }
279ef2ee5d0SMichal Meloun 
280ef2ee5d0SMichal Meloun /* --------------------------------------------------------------------------
281ef2ee5d0SMichal Meloun  *
282ef2ee5d0SMichal Meloun  *  GPIO
283ef2ee5d0SMichal Meloun  */
284ef2ee5d0SMichal Meloun device_t
285ef2ee5d0SMichal Meloun as3722_gpio_get_bus(device_t dev)
286ef2ee5d0SMichal Meloun {
287ef2ee5d0SMichal Meloun 	struct as3722_softc *sc;
288ef2ee5d0SMichal Meloun 
289ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
290ef2ee5d0SMichal Meloun 	return (sc->gpio_busdev);
291ef2ee5d0SMichal Meloun }
292ef2ee5d0SMichal Meloun 
293ef2ee5d0SMichal Meloun int
294ef2ee5d0SMichal Meloun as3722_gpio_pin_max(device_t dev, int *maxpin)
295ef2ee5d0SMichal Meloun {
296ef2ee5d0SMichal Meloun 
297ef2ee5d0SMichal Meloun 	*maxpin = NGPIO - 1;
298ef2ee5d0SMichal Meloun 	return (0);
299ef2ee5d0SMichal Meloun }
300ef2ee5d0SMichal Meloun 
301ef2ee5d0SMichal Meloun int
302ef2ee5d0SMichal Meloun as3722_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
303ef2ee5d0SMichal Meloun {
304ef2ee5d0SMichal Meloun 	struct as3722_softc *sc;
305ef2ee5d0SMichal Meloun 
306ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
307ef2ee5d0SMichal Meloun 	if (pin >= sc->gpio_npins)
308ef2ee5d0SMichal Meloun 		return (EINVAL);
309ef2ee5d0SMichal Meloun 	GPIO_LOCK(sc);
310ef2ee5d0SMichal Meloun 	*caps = sc->gpio_pins[pin]->pin_caps;
311ef2ee5d0SMichal Meloun 	GPIO_UNLOCK(sc);
312ef2ee5d0SMichal Meloun 	return (0);
313ef2ee5d0SMichal Meloun }
314ef2ee5d0SMichal Meloun 
315ef2ee5d0SMichal Meloun int
316ef2ee5d0SMichal Meloun as3722_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
317ef2ee5d0SMichal Meloun {
318ef2ee5d0SMichal Meloun 	struct as3722_softc *sc;
319ef2ee5d0SMichal Meloun 
320ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
321ef2ee5d0SMichal Meloun 	if (pin >= sc->gpio_npins)
322ef2ee5d0SMichal Meloun 		return (EINVAL);
323ef2ee5d0SMichal Meloun 	GPIO_LOCK(sc);
324ef2ee5d0SMichal Meloun 	memcpy(name, sc->gpio_pins[pin]->pin_name, GPIOMAXNAME);
325ef2ee5d0SMichal Meloun 	GPIO_UNLOCK(sc);
326ef2ee5d0SMichal Meloun 	return (0);
327ef2ee5d0SMichal Meloun }
328ef2ee5d0SMichal Meloun 
329ef2ee5d0SMichal Meloun int
330ef2ee5d0SMichal Meloun as3722_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *out_flags)
331ef2ee5d0SMichal Meloun {
332ef2ee5d0SMichal Meloun 	struct as3722_softc *sc;
333ef2ee5d0SMichal Meloun 	uint8_t tmp, mode, iosf;
334ef2ee5d0SMichal Meloun 	uint32_t flags;
335ef2ee5d0SMichal Meloun 	bool inverted;
336ef2ee5d0SMichal Meloun 
337ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
338ef2ee5d0SMichal Meloun 	if (pin >= sc->gpio_npins)
339ef2ee5d0SMichal Meloun 		return (EINVAL);
340ef2ee5d0SMichal Meloun 
341ef2ee5d0SMichal Meloun 	GPIO_LOCK(sc);
342ef2ee5d0SMichal Meloun 	tmp = sc->gpio_pins[pin]->pin_ctrl_reg;
343ef2ee5d0SMichal Meloun 	GPIO_UNLOCK(sc);
344ef2ee5d0SMichal Meloun 	iosf = (tmp >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK;
345ef2ee5d0SMichal Meloun 	mode = (tmp >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK;
346ef2ee5d0SMichal Meloun 	inverted = (tmp & AS3722_GPIO_INVERT) != 0;
347ef2ee5d0SMichal Meloun 	/* Is pin in GPIO mode ? */
348ef2ee5d0SMichal Meloun 	if (iosf != AS3722_IOSF_GPIO)
349ef2ee5d0SMichal Meloun 		return (ENXIO);
350ef2ee5d0SMichal Meloun 
351ef2ee5d0SMichal Meloun 	flags = 0;
352ef2ee5d0SMichal Meloun 	switch (mode) {
353ef2ee5d0SMichal Meloun 	case AS3722_MODE_INPUT:
354ef2ee5d0SMichal Meloun 		flags = GPIO_PIN_INPUT;
355ef2ee5d0SMichal Meloun 		break;
356ef2ee5d0SMichal Meloun 	case AS3722_MODE_PUSH_PULL:
357ef2ee5d0SMichal Meloun 	case AS3722_MODE_PUSH_PULL_LV:
358ef2ee5d0SMichal Meloun 		flags = GPIO_PIN_OUTPUT;
359ef2ee5d0SMichal Meloun 		break;
360ef2ee5d0SMichal Meloun 	case AS3722_MODE_OPEN_DRAIN:
361ef2ee5d0SMichal Meloun 	case AS3722_MODE_OPEN_DRAIN_LV:
362ef2ee5d0SMichal Meloun 		flags = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN;
363ef2ee5d0SMichal Meloun 		break;
364ef2ee5d0SMichal Meloun 	case AS3722_MODE_TRISTATE:
365ef2ee5d0SMichal Meloun 		flags = GPIO_PIN_TRISTATE;
366ef2ee5d0SMichal Meloun 		break;
367ef2ee5d0SMichal Meloun 	case AS3722_MODE_INPUT_PULL_UP_LV:
368ef2ee5d0SMichal Meloun 		flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP;
369ef2ee5d0SMichal Meloun 		break;
370ef2ee5d0SMichal Meloun 
371ef2ee5d0SMichal Meloun 	case AS3722_MODE_INPUT_PULL_DOWN:
372ef2ee5d0SMichal Meloun 		flags = GPIO_PIN_OUTPUT | GPIO_PIN_PULLDOWN;
373ef2ee5d0SMichal Meloun 		break;
374ef2ee5d0SMichal Meloun 	}
375ef2ee5d0SMichal Meloun 	if (inverted)
376ef2ee5d0SMichal Meloun 		flags |= GPIO_PIN_INVIN | GPIO_PIN_INVOUT;
377ef2ee5d0SMichal Meloun 	*out_flags = flags;
378ef2ee5d0SMichal Meloun 	return (0);
379ef2ee5d0SMichal Meloun }
380ef2ee5d0SMichal Meloun 
381ef2ee5d0SMichal Meloun static int
382ef2ee5d0SMichal Meloun as3722_gpio_get_mode(struct as3722_softc *sc, uint32_t pin, uint32_t gpio_flags)
383ef2ee5d0SMichal Meloun {
384ef2ee5d0SMichal Meloun 	int flags;
385ef2ee5d0SMichal Meloun 
386ef2ee5d0SMichal Meloun 	flags =  sc->gpio_pins[pin]->pin_cfg_flags;
387ef2ee5d0SMichal Meloun 
388ef2ee5d0SMichal Meloun 	/* Tristate mode. */
389ef2ee5d0SMichal Meloun 	if (flags & AS3722_CFG_BIAS_HIGH_IMPEDANCE ||
390ef2ee5d0SMichal Meloun 	    gpio_flags & GPIO_PIN_TRISTATE)
391ef2ee5d0SMichal Meloun 		return (AS3722_MODE_TRISTATE);
392ef2ee5d0SMichal Meloun 
393ef2ee5d0SMichal Meloun 	/* Open drain modes. */
394ef2ee5d0SMichal Meloun 	if (flags & AS3722_CFG_OPEN_DRAIN || gpio_flags & GPIO_PIN_OPENDRAIN) {
395ef2ee5d0SMichal Meloun 		/* Only pull up have effect */
396ef2ee5d0SMichal Meloun 		if (flags & AS3722_CFG_BIAS_PULL_UP ||
397ef2ee5d0SMichal Meloun 		    gpio_flags & GPIO_PIN_PULLUP)
398ef2ee5d0SMichal Meloun 			return (AS3722_MODE_OPEN_DRAIN_LV);
399ef2ee5d0SMichal Meloun 		return (AS3722_MODE_OPEN_DRAIN);
400ef2ee5d0SMichal Meloun 	}
401ef2ee5d0SMichal Meloun 	/* Input modes. */
402ef2ee5d0SMichal Meloun 	if (gpio_flags & GPIO_PIN_INPUT) {
403ef2ee5d0SMichal Meloun 		/* Accept pull up or pull down. */
404ef2ee5d0SMichal Meloun 		if (flags & AS3722_CFG_BIAS_PULL_UP ||
405ef2ee5d0SMichal Meloun 		    gpio_flags & GPIO_PIN_PULLUP)
406ef2ee5d0SMichal Meloun 			return (AS3722_MODE_INPUT_PULL_UP_LV);
407ef2ee5d0SMichal Meloun 
408ef2ee5d0SMichal Meloun 		if (flags & AS3722_CFG_BIAS_PULL_DOWN ||
409ef2ee5d0SMichal Meloun 		    gpio_flags & GPIO_PIN_PULLDOWN)
410ef2ee5d0SMichal Meloun 			return (AS3722_MODE_INPUT_PULL_DOWN);
411ef2ee5d0SMichal Meloun 		return (AS3722_MODE_INPUT);
412ef2ee5d0SMichal Meloun 	}
413ef2ee5d0SMichal Meloun 	/*
414ef2ee5d0SMichal Meloun 	 * Output modes.
415ef2ee5d0SMichal Meloun 	 * Pull down is used as indicator of low voltage output.
416ef2ee5d0SMichal Meloun 	 */
417ef2ee5d0SMichal Meloun 	if (flags & AS3722_CFG_BIAS_PULL_DOWN ||
418ef2ee5d0SMichal Meloun 		    gpio_flags & GPIO_PIN_PULLDOWN)
419ef2ee5d0SMichal Meloun 		return (AS3722_MODE_PUSH_PULL_LV);
420ef2ee5d0SMichal Meloun 	return (AS3722_MODE_PUSH_PULL);
421ef2ee5d0SMichal Meloun }
422ef2ee5d0SMichal Meloun 
423ef2ee5d0SMichal Meloun int
424ef2ee5d0SMichal Meloun as3722_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
425ef2ee5d0SMichal Meloun {
426ef2ee5d0SMichal Meloun 	struct as3722_softc *sc;
427ef2ee5d0SMichal Meloun 	uint8_t ctrl, mode, iosf;
428ef2ee5d0SMichal Meloun 	int rv;
429ef2ee5d0SMichal Meloun 
430ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
431ef2ee5d0SMichal Meloun 	if (pin >= sc->gpio_npins)
432ef2ee5d0SMichal Meloun 		return (EINVAL);
433ef2ee5d0SMichal Meloun 
434ef2ee5d0SMichal Meloun 	GPIO_LOCK(sc);
435ef2ee5d0SMichal Meloun 	ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
436ef2ee5d0SMichal Meloun 	iosf = (ctrl >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK;
437ef2ee5d0SMichal Meloun 	/* Is pin in GPIO mode ? */
438ef2ee5d0SMichal Meloun 	if (iosf != AS3722_IOSF_GPIO) {
439ef2ee5d0SMichal Meloun 		GPIO_UNLOCK(sc);
440ef2ee5d0SMichal Meloun 		return (ENXIO);
441ef2ee5d0SMichal Meloun 	}
442ef2ee5d0SMichal Meloun 	mode = as3722_gpio_get_mode(sc, pin, flags);
443ef2ee5d0SMichal Meloun 	ctrl &= ~(AS3722_GPIO_MODE_MASK << AS3722_GPIO_MODE_SHIFT);
444*dca7f696SJohn Baldwin 	ctrl |= mode << AS3722_GPIO_MODE_SHIFT;
445ef2ee5d0SMichal Meloun 	rv = 0;
446ef2ee5d0SMichal Meloun 	if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) {
447ef2ee5d0SMichal Meloun 		rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl);
448ef2ee5d0SMichal Meloun 		sc->gpio_pins[pin]->pin_ctrl_reg = ctrl;
449ef2ee5d0SMichal Meloun 	}
450ef2ee5d0SMichal Meloun 	GPIO_UNLOCK(sc);
451ef2ee5d0SMichal Meloun 	return (rv);
452ef2ee5d0SMichal Meloun }
453ef2ee5d0SMichal Meloun 
454ef2ee5d0SMichal Meloun int
455ef2ee5d0SMichal Meloun as3722_gpio_pin_set(device_t dev, uint32_t pin, uint32_t val)
456ef2ee5d0SMichal Meloun {
457ef2ee5d0SMichal Meloun 	struct as3722_softc *sc;
458ef2ee5d0SMichal Meloun 	uint8_t tmp;
459ef2ee5d0SMichal Meloun 	int rv;
460ef2ee5d0SMichal Meloun 
461ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
462ef2ee5d0SMichal Meloun 	if (pin >= sc->gpio_npins)
463ef2ee5d0SMichal Meloun 		return (EINVAL);
464ef2ee5d0SMichal Meloun 
465ef2ee5d0SMichal Meloun 	tmp =  (val != 0) ? 1 : 0;
466ef2ee5d0SMichal Meloun 	if (sc->gpio_pins[pin]->pin_ctrl_reg & AS3722_GPIO_INVERT)
467ef2ee5d0SMichal Meloun 		tmp ^= 1;
468ef2ee5d0SMichal Meloun 
469ef2ee5d0SMichal Meloun 	GPIO_LOCK(sc);
470ef2ee5d0SMichal Meloun 	rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), (tmp << pin));
471ef2ee5d0SMichal Meloun 	GPIO_UNLOCK(sc);
472ef2ee5d0SMichal Meloun 	return (rv);
473ef2ee5d0SMichal Meloun }
474ef2ee5d0SMichal Meloun 
475ef2ee5d0SMichal Meloun int
476ef2ee5d0SMichal Meloun as3722_gpio_pin_get(device_t dev, uint32_t pin, uint32_t *val)
477ef2ee5d0SMichal Meloun {
478ef2ee5d0SMichal Meloun 	struct as3722_softc *sc;
479ef2ee5d0SMichal Meloun 	uint8_t tmp, mode, ctrl;
480ef2ee5d0SMichal Meloun 	int rv;
481ef2ee5d0SMichal Meloun 
482ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
483ef2ee5d0SMichal Meloun 	if (pin >= sc->gpio_npins)
484ef2ee5d0SMichal Meloun 		return (EINVAL);
485ef2ee5d0SMichal Meloun 
486ef2ee5d0SMichal Meloun 	GPIO_LOCK(sc);
487ef2ee5d0SMichal Meloun 	ctrl = sc->gpio_pins[pin]->pin_ctrl_reg;
488ef2ee5d0SMichal Meloun 	mode = (ctrl >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK;
489ef2ee5d0SMichal Meloun 	if ((mode == AS3722_MODE_PUSH_PULL) ||
490ef2ee5d0SMichal Meloun 	    (mode == AS3722_MODE_PUSH_PULL_LV))
491ef2ee5d0SMichal Meloun 		rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp);
492ef2ee5d0SMichal Meloun 	else
493ef2ee5d0SMichal Meloun 		rv = RD1(sc, AS3722_GPIO_SIGNAL_IN, &tmp);
494ef2ee5d0SMichal Meloun 	GPIO_UNLOCK(sc);
495ef2ee5d0SMichal Meloun 	if (rv != 0)
496ef2ee5d0SMichal Meloun 		return (rv);
497ef2ee5d0SMichal Meloun 
498ef2ee5d0SMichal Meloun 	*val = tmp & (1 << pin) ? 1 : 0;
499ef2ee5d0SMichal Meloun 	if (ctrl & AS3722_GPIO_INVERT)
500ef2ee5d0SMichal Meloun 		*val ^= 1;
501ef2ee5d0SMichal Meloun 	return (0);
502ef2ee5d0SMichal Meloun }
503ef2ee5d0SMichal Meloun 
504ef2ee5d0SMichal Meloun int
505ef2ee5d0SMichal Meloun as3722_gpio_pin_toggle(device_t dev, uint32_t pin)
506ef2ee5d0SMichal Meloun {
507ef2ee5d0SMichal Meloun 	struct as3722_softc *sc;
508ef2ee5d0SMichal Meloun 	uint8_t tmp;
509ef2ee5d0SMichal Meloun 	int rv;
510ef2ee5d0SMichal Meloun 
511ef2ee5d0SMichal Meloun 	sc = device_get_softc(dev);
512ef2ee5d0SMichal Meloun 	if (pin >= sc->gpio_npins)
513ef2ee5d0SMichal Meloun 		return (EINVAL);
514ef2ee5d0SMichal Meloun 
515ef2ee5d0SMichal Meloun 	GPIO_LOCK(sc);
516ef2ee5d0SMichal Meloun 	rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp);
517ef2ee5d0SMichal Meloun 	if (rv != 0) {
518ef2ee5d0SMichal Meloun 		GPIO_UNLOCK(sc);
519ef2ee5d0SMichal Meloun 		return (rv);
520ef2ee5d0SMichal Meloun 	}
521ef2ee5d0SMichal Meloun 	tmp ^= (1 <<pin);
522ef2ee5d0SMichal Meloun 	rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), tmp);
523ef2ee5d0SMichal Meloun 	GPIO_UNLOCK(sc);
524ef2ee5d0SMichal Meloun 	return (0);
525ef2ee5d0SMichal Meloun }
526ef2ee5d0SMichal Meloun 
527ef2ee5d0SMichal Meloun int
528ef2ee5d0SMichal Meloun as3722_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent,
529ef2ee5d0SMichal Meloun     int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags)
530ef2ee5d0SMichal Meloun {
531ef2ee5d0SMichal Meloun 
532ef2ee5d0SMichal Meloun 	if (gcells != 2)
533ef2ee5d0SMichal Meloun 		return (ERANGE);
534ef2ee5d0SMichal Meloun 	*pin = gpios[0];
535ef2ee5d0SMichal Meloun 	*flags= gpios[1];
536ef2ee5d0SMichal Meloun 	return (0);
537ef2ee5d0SMichal Meloun }
538ef2ee5d0SMichal Meloun 
539ef2ee5d0SMichal Meloun int
540ef2ee5d0SMichal Meloun as3722_gpio_attach(struct as3722_softc *sc, phandle_t node)
541ef2ee5d0SMichal Meloun {
542ef2ee5d0SMichal Meloun 	struct as3722_gpio_pin *pin;
543ef2ee5d0SMichal Meloun 	int i, rv;
544ef2ee5d0SMichal Meloun 
545ef2ee5d0SMichal Meloun 	sx_init(&sc->gpio_lock, "AS3722 GPIO lock");
546ef2ee5d0SMichal Meloun 	sc->gpio_npins = NGPIO;
547ef2ee5d0SMichal Meloun 	sc->gpio_pins = malloc(sizeof(struct as3722_gpio_pin *) *
548ef2ee5d0SMichal Meloun 	    sc->gpio_npins, M_AS3722_GPIO, M_WAITOK | M_ZERO);
549ef2ee5d0SMichal Meloun 
550ef2ee5d0SMichal Meloun 	sc->gpio_busdev = gpiobus_attach_bus(sc->dev);
551ef2ee5d0SMichal Meloun 	if (sc->gpio_busdev == NULL)
552ef2ee5d0SMichal Meloun 		return (ENXIO);
553ef2ee5d0SMichal Meloun 	for (i = 0; i < sc->gpio_npins; i++) {
554ef2ee5d0SMichal Meloun 		sc->gpio_pins[i] = malloc(sizeof(struct as3722_gpio_pin),
555ef2ee5d0SMichal Meloun 		    M_AS3722_GPIO, M_WAITOK | M_ZERO);
556ef2ee5d0SMichal Meloun 		pin = sc->gpio_pins[i];
557ef2ee5d0SMichal Meloun 		sprintf(pin->pin_name, "gpio%d", i);
558ef2ee5d0SMichal Meloun 		pin->pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT  |
559ef2ee5d0SMichal Meloun 		    GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE |
560ef2ee5d0SMichal Meloun 		    GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_PIN_INVIN |
561ef2ee5d0SMichal Meloun 		    GPIO_PIN_INVOUT;
562ef2ee5d0SMichal Meloun 		rv = RD1(sc, AS3722_GPIO0_CONTROL + i, &pin->pin_ctrl_reg);
563ef2ee5d0SMichal Meloun 		if (rv != 0) {
564ef2ee5d0SMichal Meloun 			device_printf(sc->dev,
565ef2ee5d0SMichal Meloun 			    "Cannot read configuration for pin %s\n",
566ef2ee5d0SMichal Meloun 			    sc->gpio_pins[i]->pin_name);
567ef2ee5d0SMichal Meloun 		}
568ef2ee5d0SMichal Meloun 	}
569ef2ee5d0SMichal Meloun 	return (0);
570ef2ee5d0SMichal Meloun }
571