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