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