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 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 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 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 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 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 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 292 as3722_gpio_pin_max(device_t dev, int *maxpin) 293 { 294 295 *maxpin = NGPIO - 1; 296 return (0); 297 } 298 299 int 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 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 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 380 as3722_gpio_get_mode(struct as3722_softc *sc, uint32_t pin, uint32_t gpio_flags) 381 { 382 int flags; 383 384 flags = sc->gpio_pins[pin]->pin_cfg_flags; 385 386 /* Tristate mode. */ 387 if (flags & AS3722_CFG_BIAS_HIGH_IMPEDANCE || 388 gpio_flags & GPIO_PIN_TRISTATE) 389 return (AS3722_MODE_TRISTATE); 390 391 /* Open drain modes. */ 392 if (flags & AS3722_CFG_OPEN_DRAIN || gpio_flags & GPIO_PIN_OPENDRAIN) { 393 /* Only pull up have effect */ 394 if (flags & AS3722_CFG_BIAS_PULL_UP || 395 gpio_flags & GPIO_PIN_PULLUP) 396 return (AS3722_MODE_OPEN_DRAIN_LV); 397 return (AS3722_MODE_OPEN_DRAIN); 398 } 399 /* Input modes. */ 400 if (gpio_flags & GPIO_PIN_INPUT) { 401 /* Accept pull up or pull down. */ 402 if (flags & AS3722_CFG_BIAS_PULL_UP || 403 gpio_flags & GPIO_PIN_PULLUP) 404 return (AS3722_MODE_INPUT_PULL_UP_LV); 405 406 if (flags & AS3722_CFG_BIAS_PULL_DOWN || 407 gpio_flags & GPIO_PIN_PULLDOWN) 408 return (AS3722_MODE_INPUT_PULL_DOWN); 409 return (AS3722_MODE_INPUT); 410 } 411 /* 412 * Output modes. 413 * Pull down is used as indicator of low voltage output. 414 */ 415 if (flags & AS3722_CFG_BIAS_PULL_DOWN || 416 gpio_flags & GPIO_PIN_PULLDOWN) 417 return (AS3722_MODE_PUSH_PULL_LV); 418 return (AS3722_MODE_PUSH_PULL); 419 } 420 421 int 422 as3722_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 423 { 424 struct as3722_softc *sc; 425 uint8_t ctrl, mode, iosf; 426 int rv; 427 428 sc = device_get_softc(dev); 429 if (pin >= sc->gpio_npins) 430 return (EINVAL); 431 432 GPIO_LOCK(sc); 433 ctrl = sc->gpio_pins[pin]->pin_ctrl_reg; 434 iosf = (ctrl >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK; 435 /* Is pin in GPIO mode ? */ 436 if (iosf != AS3722_IOSF_GPIO) { 437 GPIO_UNLOCK(sc); 438 return (ENXIO); 439 } 440 mode = as3722_gpio_get_mode(sc, pin, flags); 441 ctrl &= ~(AS3722_GPIO_MODE_MASK << AS3722_GPIO_MODE_SHIFT); 442 ctrl |= mode << AS3722_GPIO_MODE_SHIFT; 443 rv = 0; 444 if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) { 445 rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl); 446 sc->gpio_pins[pin]->pin_ctrl_reg = ctrl; 447 } 448 GPIO_UNLOCK(sc); 449 return (rv); 450 } 451 452 int 453 as3722_gpio_pin_set(device_t dev, uint32_t pin, uint32_t val) 454 { 455 struct as3722_softc *sc; 456 uint8_t tmp; 457 int rv; 458 459 sc = device_get_softc(dev); 460 if (pin >= sc->gpio_npins) 461 return (EINVAL); 462 463 tmp = (val != 0) ? 1 : 0; 464 if (sc->gpio_pins[pin]->pin_ctrl_reg & AS3722_GPIO_INVERT) 465 tmp ^= 1; 466 467 GPIO_LOCK(sc); 468 rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), (tmp << pin)); 469 GPIO_UNLOCK(sc); 470 return (rv); 471 } 472 473 int 474 as3722_gpio_pin_get(device_t dev, uint32_t pin, uint32_t *val) 475 { 476 struct as3722_softc *sc; 477 uint8_t tmp, mode, ctrl; 478 int rv; 479 480 sc = device_get_softc(dev); 481 if (pin >= sc->gpio_npins) 482 return (EINVAL); 483 484 GPIO_LOCK(sc); 485 ctrl = sc->gpio_pins[pin]->pin_ctrl_reg; 486 mode = (ctrl >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK; 487 if ((mode == AS3722_MODE_PUSH_PULL) || 488 (mode == AS3722_MODE_PUSH_PULL_LV)) 489 rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp); 490 else 491 rv = RD1(sc, AS3722_GPIO_SIGNAL_IN, &tmp); 492 GPIO_UNLOCK(sc); 493 if (rv != 0) 494 return (rv); 495 496 *val = tmp & (1 << pin) ? 1 : 0; 497 if (ctrl & AS3722_GPIO_INVERT) 498 *val ^= 1; 499 return (0); 500 } 501 502 int 503 as3722_gpio_pin_toggle(device_t dev, uint32_t pin) 504 { 505 struct as3722_softc *sc; 506 uint8_t tmp; 507 int rv; 508 509 sc = device_get_softc(dev); 510 if (pin >= sc->gpio_npins) 511 return (EINVAL); 512 513 GPIO_LOCK(sc); 514 rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp); 515 if (rv != 0) { 516 GPIO_UNLOCK(sc); 517 return (rv); 518 } 519 tmp ^= (1 <<pin); 520 rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), tmp); 521 GPIO_UNLOCK(sc); 522 return (0); 523 } 524 525 int 526 as3722_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent, 527 int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) 528 { 529 530 if (gcells != 2) 531 return (ERANGE); 532 *pin = gpios[0]; 533 *flags= gpios[1]; 534 return (0); 535 } 536 537 int 538 as3722_gpio_attach(struct as3722_softc *sc, phandle_t node) 539 { 540 struct as3722_gpio_pin *pin; 541 int i, rv; 542 543 sx_init(&sc->gpio_lock, "AS3722 GPIO lock"); 544 sc->gpio_npins = NGPIO; 545 sc->gpio_pins = malloc(sizeof(struct as3722_gpio_pin *) * 546 sc->gpio_npins, M_AS3722_GPIO, M_WAITOK | M_ZERO); 547 548 sc->gpio_busdev = gpiobus_attach_bus(sc->dev); 549 if (sc->gpio_busdev == NULL) 550 return (ENXIO); 551 for (i = 0; i < sc->gpio_npins; i++) { 552 sc->gpio_pins[i] = malloc(sizeof(struct as3722_gpio_pin), 553 M_AS3722_GPIO, M_WAITOK | M_ZERO); 554 pin = sc->gpio_pins[i]; 555 sprintf(pin->pin_name, "gpio%d", i); 556 pin->pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | 557 GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE | 558 GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_PIN_INVIN | 559 GPIO_PIN_INVOUT; 560 rv = RD1(sc, AS3722_GPIO0_CONTROL + i, &pin->pin_ctrl_reg); 561 if (rv != 0) { 562 device_printf(sc->dev, 563 "Cannot read configuration for pin %s\n", 564 sc->gpio_pins[i]->pin_name); 565 } 566 } 567 return (0); 568 } 569