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