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$"); 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 45 MALLOC_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 84 static 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 95 static 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 115 struct as3722_pincfg { 116 char *function; 117 int flags; 118 }; 119 120 struct 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 */ 132 static int 133 as3722_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 146 static int 147 as3722_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 209 static int 210 as3722_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", (void **)pins); 216 if (*lpins <= 0) 217 return (ENOENT); 218 219 /* Read function (mux) settings. */ 220 rv = OF_getprop_alloc(node, "function", (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 232 static int 233 as3722_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 OF_prop_free(pins); 258 if (cfg.function != NULL) 259 OF_prop_free(cfg.function); 260 261 return (rv); 262 } 263 264 int 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 (!ofw_bus_node_status_okay(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 */ 288 device_t 289 as3722_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 297 int 298 as3722_gpio_pin_max(device_t dev, int *maxpin) 299 { 300 301 *maxpin = NGPIO - 1; 302 return (0); 303 } 304 305 int 306 as3722_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 319 int 320 as3722_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 333 int 334 as3722_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 385 static int 386 as3722_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 429 int 430 as3722_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 460 int 461 as3722_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 481 int 482 as3722_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 510 int 511 as3722_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 533 int 534 as3722_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 545 int 546 as3722_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