Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/nvidia/as3722_gpio.c
39483 views
1
/*-
2
* Copyright (c) 2016 Michal Meloun <[email protected]>
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
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
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
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
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
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
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
291
as3722_gpio_pin_max(device_t dev, int *maxpin)
292
{
293
294
*maxpin = NGPIO - 1;
295
return (0);
296
}
297
298
int
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
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
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
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
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
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
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
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
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
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_add_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
569