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