1 /* $NetBSD: meson_pinctrl.c,v 1.12 2021/04/24 23:36:26 thorpej Exp $ */ 2 3 /*- 4 * Copyright (c) 2019 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include "opt_soc.h" 30 31 #include <sys/cdefs.h> 32 __KERNEL_RCSID(0, "$NetBSD: meson_pinctrl.c,v 1.12 2021/04/24 23:36:26 thorpej Exp $"); 33 34 #include <sys/param.h> 35 #include <sys/bus.h> 36 #include <sys/device.h> 37 #include <sys/systm.h> 38 #include <sys/kernel.h> 39 #include <sys/mutex.h> 40 #include <sys/kmem.h> 41 #include <sys/gpio.h> 42 43 #include <dev/gpio/gpiovar.h> 44 45 #include <dev/fdt/fdtvar.h> 46 47 #include <arm/amlogic/meson_pinctrl.h> 48 49 struct meson_pinctrl_softc { 50 device_t sc_dev; 51 bus_space_tag_t sc_bst; 52 bus_space_handle_t sc_bsh_mux; 53 bus_space_handle_t sc_bsh_pull; 54 bus_space_handle_t sc_bsh_pull_enable; 55 bus_space_handle_t sc_bsh_gpio; 56 int sc_phandle; 57 int sc_phandle_gpio; 58 59 kmutex_t sc_lock; 60 61 const struct meson_pinctrl_config *sc_conf; 62 63 struct gpio_chipset_tag sc_gp; 64 gpio_pin_t *sc_pins; 65 }; 66 67 struct meson_pinctrl_gpio_pin { 68 struct meson_pinctrl_softc *pin_sc; 69 const struct meson_pinctrl_gpio *pin_def; 70 int pin_flags; 71 bool pin_actlo; 72 }; 73 74 static const struct device_compatible_entry compat_data[] = { 75 #ifdef SOC_MESON8B 76 { .compat = "amlogic,meson8b-aobus-pinctrl", 77 .data = &meson8b_aobus_pinctrl_config }, 78 { .compat = "amlogic,meson8b-cbus-pinctrl", 79 .data = &meson8b_cbus_pinctrl_config }, 80 #endif 81 #ifdef SOC_MESONGXBB 82 { .compat = "amlogic,meson-gxbb-aobus-pinctrl", 83 .data = &mesongxbb_aobus_pinctrl_config }, 84 { .compat = "amlogic,meson-gxbb-periphs-pinctrl", 85 .data = &mesongxbb_periphs_pinctrl_config }, 86 #endif 87 #ifdef SOC_MESONGXL 88 { .compat = "amlogic,meson-gxl-aobus-pinctrl", 89 .data = &mesongxl_aobus_pinctrl_config }, 90 { .compat = "amlogic,meson-gxl-periphs-pinctrl", 91 .data = &mesongxl_periphs_pinctrl_config }, 92 #endif 93 #ifdef SOC_MESONG12 94 { .compat = "amlogic,meson-g12a-aobus-pinctrl", 95 .data = &mesong12a_aobus_pinctrl_config }, 96 { .compat = "amlogic,meson-g12a-periphs-pinctrl", 97 .data = &mesong12a_periphs_pinctrl_config }, 98 #endif 99 DEVICE_COMPAT_EOL 100 }; 101 102 #define MUX_READ(sc, reg) \ 103 bus_space_read_4((sc)->sc_bst, (sc)->sc_bsh_mux, (reg)) 104 #define MUX_WRITE(sc, reg, val) \ 105 bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh_mux, (reg), (val)) 106 107 static const struct meson_pinctrl_group * 108 meson_pinctrl_find_group(struct meson_pinctrl_softc *sc, 109 const char *name) 110 { 111 const struct meson_pinctrl_group *group; 112 u_int n; 113 114 for (n = 0; n < sc->sc_conf->ngroups; n++) { 115 group = &sc->sc_conf->groups[n]; 116 if (strcmp(group->name, name) == 0) 117 return group; 118 } 119 120 return NULL; 121 } 122 123 static bool 124 meson_pinctrl_group_in_bank(struct meson_pinctrl_softc *sc, 125 const struct meson_pinctrl_group *group, u_int bankno) 126 { 127 u_int n; 128 129 for (n = 0; n < group->nbank; n++) { 130 if (group->bank[n] == bankno) 131 return true; 132 } 133 134 return false; 135 } 136 137 static void 138 meson_pinctrl_set_group(struct meson_pinctrl_softc *sc, 139 const struct meson_pinctrl_group *group, bool enable) 140 { 141 uint32_t val; 142 143 val = MUX_READ(sc, group->reg); 144 if (group->mask == 0) { 145 if (enable) 146 val |= __BIT(group->bit); 147 else 148 val &= ~__BIT(group->bit); 149 } else { 150 val &= ~group->mask; 151 if (enable) 152 val |= __SHIFTIN(group->func, group->mask); 153 } 154 MUX_WRITE(sc, group->reg, val); 155 } 156 157 static void 158 meson_pinctrl_setfunc(struct meson_pinctrl_softc *sc, const char *name) 159 { 160 const struct meson_pinctrl_group *group, *target_group; 161 u_int n, bank; 162 163 target_group = meson_pinctrl_find_group(sc, name); 164 if (target_group == NULL) { 165 aprint_error_dev(sc->sc_dev, "function '%s' not supported\n", name); 166 return; 167 } 168 169 /* Disable conflicting groups */ 170 for (n = 0; n < sc->sc_conf->ngroups; n++) { 171 group = &sc->sc_conf->groups[n]; 172 if (target_group == group) 173 continue; 174 for (bank = 0; bank < target_group->nbank; bank++) { 175 if (meson_pinctrl_group_in_bank(sc, group, target_group->bank[bank])) 176 meson_pinctrl_set_group(sc, group, false); 177 } 178 } 179 180 /* Enable target group */ 181 meson_pinctrl_set_group(sc, target_group, true); 182 } 183 184 static int 185 meson_pinctrl_set_config(device_t dev, const void *data, size_t len) 186 { 187 struct meson_pinctrl_softc * const sc = device_private(dev); 188 const char *groups; 189 int groups_len; 190 191 if (len != 4) 192 return -1; 193 194 const int phandle = fdtbus_get_phandle_from_native(be32dec(data)); 195 const int mux = of_find_firstchild_byname(phandle, "mux"); 196 if (mux == -1) 197 return -1; 198 199 groups = fdtbus_pinctrl_parse_groups(mux, &groups_len); 200 if (groups == NULL) 201 return -1; 202 203 for (; groups_len > 0; 204 groups_len -= strlen(groups) + 1, groups += strlen(groups) + 1) { 205 meson_pinctrl_setfunc(sc, groups); 206 } 207 208 return 0; 209 } 210 211 static struct fdtbus_pinctrl_controller_func meson_pinctrl_funcs = { 212 .set_config = meson_pinctrl_set_config, 213 }; 214 215 static bus_space_handle_t 216 meson_pinctrl_gpio_handle(struct meson_pinctrl_softc *sc, 217 const struct meson_pinctrl_gpioreg *gpioreg) 218 { 219 switch (gpioreg->type) { 220 case MESON_PINCTRL_REGTYPE_PULL: 221 return sc->sc_bsh_pull; 222 case MESON_PINCTRL_REGTYPE_PULL_ENABLE: 223 return sc->sc_bsh_pull_enable; 224 case MESON_PINCTRL_REGTYPE_GPIO: 225 return sc->sc_bsh_gpio; 226 default: 227 panic("unsupported GPIO regtype %d", gpioreg->type); 228 } 229 } 230 231 static int 232 meson_pinctrl_pin_read(void *priv, int pin) 233 { 234 struct meson_pinctrl_softc * const sc = priv; 235 const struct meson_pinctrl_gpio *pin_def = &sc->sc_conf->gpios[pin]; 236 const struct meson_pinctrl_gpioreg *gpio_reg = &pin_def->in; 237 bus_space_handle_t bsh; 238 uint32_t data; 239 int val; 240 241 KASSERT(pin < sc->sc_conf->ngpios); 242 243 bsh = meson_pinctrl_gpio_handle(sc, gpio_reg); 244 data = bus_space_read_4(sc->sc_bst, bsh, gpio_reg->reg); 245 val = __SHIFTOUT(data, gpio_reg->mask); 246 247 return val; 248 } 249 250 static void 251 meson_pinctrl_pin_write(void *priv, int pin, int val) 252 { 253 struct meson_pinctrl_softc * const sc = priv; 254 const struct meson_pinctrl_gpio *pin_def = &sc->sc_conf->gpios[pin]; 255 const struct meson_pinctrl_gpioreg *gpio_reg = &pin_def->out; 256 bus_space_handle_t bsh; 257 uint32_t data; 258 259 KASSERT(pin < sc->sc_conf->ngpios); 260 261 bsh = meson_pinctrl_gpio_handle(sc, gpio_reg); 262 263 mutex_enter(&sc->sc_lock); 264 data = bus_space_read_4(sc->sc_bst, bsh, gpio_reg->reg); 265 if (val) 266 data |= gpio_reg->mask; 267 else 268 data &= ~gpio_reg->mask; 269 bus_space_write_4(sc->sc_bst, bsh, gpio_reg->reg, data); 270 mutex_exit(&sc->sc_lock); 271 } 272 273 static void 274 meson_pinctrl_pin_dir(struct meson_pinctrl_softc *sc, 275 const struct meson_pinctrl_gpio *pin_def, int flags) 276 { 277 bus_space_handle_t bsh; 278 uint32_t data; 279 280 KASSERT(mutex_owned(&sc->sc_lock)); 281 282 bsh = meson_pinctrl_gpio_handle(sc, &pin_def->oen); 283 data = bus_space_read_4(sc->sc_bst, bsh, pin_def->oen.reg); 284 if ((flags & GPIO_PIN_INPUT) != 0) 285 data |= pin_def->oen.mask; 286 else 287 data &= ~pin_def->oen.mask; 288 bus_space_write_4(sc->sc_bst, bsh, pin_def->oen.reg, data); 289 } 290 291 static void 292 meson_pinctrl_pin_ctl(void *priv, int pin, int flags) 293 { 294 struct meson_pinctrl_softc * const sc = priv; 295 const struct meson_pinctrl_gpio *pin_def = &sc->sc_conf->gpios[pin]; 296 bus_space_handle_t bsh; 297 uint32_t data; 298 299 KASSERT(pin < sc->sc_conf->ngpios); 300 301 mutex_enter(&sc->sc_lock); 302 303 if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) != 0) 304 meson_pinctrl_pin_dir(sc, pin_def, flags); 305 306 if ((flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) != 0) { 307 bsh = meson_pinctrl_gpio_handle(sc, &pin_def->pupd); 308 data = bus_space_read_4(sc->sc_bst, bsh, pin_def->pupd.reg); 309 if ((flags & GPIO_PIN_PULLUP) != 0) 310 data |= pin_def->pupd.mask; 311 else 312 data &= ~pin_def->pupd.mask; 313 bus_space_write_4(sc->sc_bst, bsh, pin_def->pupd.reg, data); 314 315 bsh = meson_pinctrl_gpio_handle(sc, &pin_def->pupden); 316 data = bus_space_read_4(sc->sc_bst, bsh, pin_def->pupden.reg); 317 data |= pin_def->pupden.mask; 318 bus_space_write_4(sc->sc_bst, bsh, pin_def->pupden.reg, data); 319 } else { 320 bsh = meson_pinctrl_gpio_handle(sc, &pin_def->pupden); 321 data = bus_space_read_4(sc->sc_bst, bsh, pin_def->pupden.reg); 322 data &= ~pin_def->pupden.mask; 323 bus_space_write_4(sc->sc_bst, bsh, pin_def->pupden.reg, data); 324 } 325 326 mutex_exit(&sc->sc_lock); 327 } 328 329 static const struct meson_pinctrl_gpio * 330 meson_pinctrl_gpio_lookup(struct meson_pinctrl_softc *sc, u_int id) 331 { 332 if (id >= sc->sc_conf->ngpios) 333 return NULL; 334 335 if (sc->sc_conf->gpios[id].name == NULL) 336 return NULL; 337 338 return &sc->sc_conf->gpios[id]; 339 } 340 341 static void * 342 meson_pinctrl_gpio_acquire(device_t dev, const void *data, size_t len, int flags) 343 { 344 struct meson_pinctrl_softc * const sc = device_private(dev); 345 const struct meson_pinctrl_gpio *pin_def; 346 const struct meson_pinctrl_group *group; 347 struct meson_pinctrl_gpio_pin *gpin; 348 const u_int *gpio = data; 349 u_int n, bank; 350 351 if (len != 12) 352 return NULL; 353 354 const u_int id = be32toh(gpio[1]); 355 const bool actlo = be32toh(gpio[2]) & 1; 356 357 pin_def = meson_pinctrl_gpio_lookup(sc, id); 358 if (pin_def == NULL) 359 return NULL; 360 361 /* Disable conflicting groups */ 362 for (n = 0; n < sc->sc_conf->ngroups; n++) { 363 group = &sc->sc_conf->groups[n]; 364 for (bank = 0; bank < group->nbank; bank++) { 365 if (group->bank[bank] == pin_def->id) 366 meson_pinctrl_set_group(sc, group, false); 367 } 368 } 369 370 mutex_enter(&sc->sc_lock); 371 meson_pinctrl_pin_dir(sc, pin_def, flags); 372 mutex_exit(&sc->sc_lock); 373 374 gpin = kmem_zalloc(sizeof(*gpin), KM_SLEEP); 375 gpin->pin_sc = sc; 376 gpin->pin_def = pin_def; 377 gpin->pin_flags = flags; 378 gpin->pin_actlo = actlo; 379 380 return gpin; 381 } 382 383 static void 384 meson_pinctrl_gpio_release(device_t dev, void *priv) 385 { 386 struct meson_pinctrl_softc * const sc = device_private(dev); 387 struct meson_pinctrl_gpio_pin *gpin = priv; 388 const struct meson_pinctrl_gpio *pin_def = gpin->pin_def; 389 390 KASSERT(sc == gpin->pin_sc); 391 392 mutex_enter(&sc->sc_lock); 393 meson_pinctrl_pin_dir(sc, pin_def, GPIO_PIN_INPUT); 394 mutex_exit(&sc->sc_lock); 395 396 kmem_free(gpin, sizeof(*gpin)); 397 } 398 399 static int 400 meson_pinctrl_gpio_read(device_t dev, void *priv, bool raw) 401 { 402 struct meson_pinctrl_softc * const sc = device_private(dev); 403 struct meson_pinctrl_gpio_pin *gpin = priv; 404 const struct meson_pinctrl_gpio *pin_def = gpin->pin_def; 405 int val; 406 407 val = meson_pinctrl_pin_read(sc, pin_def->id); 408 if (!raw && gpin->pin_actlo) 409 val = !val; 410 411 return val; 412 } 413 414 static void 415 meson_pinctrl_gpio_write(device_t dev, void *priv, int val, bool raw) 416 { 417 struct meson_pinctrl_softc * const sc = device_private(dev); 418 struct meson_pinctrl_gpio_pin *gpin = priv; 419 const struct meson_pinctrl_gpio *pin_def = gpin->pin_def; 420 421 if (!raw && gpin->pin_actlo) 422 val = !val; 423 424 meson_pinctrl_pin_write(sc, pin_def->id, val); 425 } 426 427 static struct fdtbus_gpio_controller_func meson_pinctrl_gpio_funcs = { 428 .acquire = meson_pinctrl_gpio_acquire, 429 .release = meson_pinctrl_gpio_release, 430 .read = meson_pinctrl_gpio_read, 431 .write = meson_pinctrl_gpio_write, 432 }; 433 434 static int 435 meson_pinctrl_initres(struct meson_pinctrl_softc *sc) 436 { 437 bool gpio_found = false; 438 bus_addr_t addr; 439 bus_size_t size; 440 int child; 441 442 for (child = OF_child(sc->sc_phandle); child; child = OF_peer(child)) { 443 if (of_hasprop(child, "gpio-controller")) { 444 if (gpio_found) 445 continue; 446 gpio_found = true; 447 448 if (fdtbus_get_reg_byname(child, "mux", &addr, &size) != 0 || 449 bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_mux) != 0) { 450 aprint_error(": couldn't map mux registers\n"); 451 return ENXIO; 452 } 453 if (fdtbus_get_reg_byname(child, "gpio", &addr, &size) != 0 || 454 bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_gpio) != 0) { 455 aprint_error(": couldn't map gpio registers\n"); 456 return ENXIO; 457 } 458 459 /* pull register is optional */ 460 if (fdtbus_get_reg_byname(child, "pull", &addr, &size) == 0) { 461 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_pull) != 0) { 462 aprint_error(": couldn't map pull registers\n"); 463 return ENXIO; 464 } 465 } 466 /* pull-enable register is optional */ 467 if (fdtbus_get_reg_byname(child, "pull-enable", &addr, &size) == 0) { 468 if (bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh_pull_enable) != 0) { 469 aprint_error(": couldn't map pull-enable registers\n"); 470 return ENXIO; 471 } 472 } 473 474 sc->sc_phandle_gpio = child; 475 } else if (of_find_firstchild_byname(child, "mux") != -1) { 476 fdtbus_register_pinctrl_config(sc->sc_dev, child, &meson_pinctrl_funcs); 477 } 478 } 479 480 if (!gpio_found) { 481 aprint_error(": couldn't find gpio controller\n"); 482 return ENOENT; 483 } 484 485 return 0; 486 } 487 488 static void 489 meson_pinctrl_initgpio(struct meson_pinctrl_softc *sc) 490 { 491 const struct meson_pinctrl_gpio *pin_def; 492 struct gpio_chipset_tag *gp; 493 struct gpiobus_attach_args gba; 494 int child, len, val; 495 u_int pin; 496 497 fdtbus_register_gpio_controller(sc->sc_dev, sc->sc_phandle_gpio, &meson_pinctrl_gpio_funcs); 498 499 for (child = OF_child(sc->sc_phandle_gpio); child; child = OF_peer(child)) { 500 if (!of_hasprop(child, "gpio-hog")) 501 continue; 502 503 const char *line_name = fdtbus_get_string(child, "line-name"); 504 if (line_name == NULL) 505 line_name = fdtbus_get_string(child, "name"); 506 507 const bool input = of_hasprop(child, "input"); 508 const bool output_low = of_hasprop(child, "output-low"); 509 const bool output_high = of_hasprop(child, "output-high"); 510 511 if (!input && !output_low && !output_high) { 512 aprint_error_dev(sc->sc_dev, "no configuration for line %s\n", line_name); 513 continue; 514 } 515 516 const u_int *gpio = fdtbus_get_prop(child, "gpios", &len); 517 while (len >= 8) { 518 const u_int id = be32toh(gpio[0]); 519 const bool actlo = be32toh(gpio[1]) & 1; 520 521 pin_def = meson_pinctrl_gpio_lookup(sc, id); 522 if (pin_def != NULL) { 523 if (input) { 524 device_printf(sc->sc_dev, "%s %s set to input\n", 525 line_name, pin_def->name); 526 meson_pinctrl_pin_ctl(sc, pin_def->id, GPIO_PIN_INPUT); 527 } else { 528 val = output_high; 529 if (actlo) 530 val = !val; 531 device_printf(sc->sc_dev, "%s %s set to output (%s)\n", 532 line_name, pin_def->name, val ? "high" : "low"); 533 meson_pinctrl_pin_write(sc, pin_def->id, val); 534 meson_pinctrl_pin_ctl(sc, pin_def->id, GPIO_PIN_OUTPUT); 535 } 536 } else { 537 aprint_error_dev(sc->sc_dev, "%s: unsupported pin %d\n", line_name, id); 538 } 539 540 len -= 8; 541 gpio += 8; 542 } 543 } 544 545 const u_int npins = sc->sc_conf->ngpios; 546 sc->sc_pins = kmem_zalloc(sizeof(*sc->sc_pins) * npins, KM_SLEEP); 547 for (pin = 0; pin < npins; pin++) { 548 pin_def = &sc->sc_conf->gpios[pin]; 549 sc->sc_pins[pin].pin_num = pin; 550 if (pin_def->name == NULL) 551 continue; 552 sc->sc_pins[pin].pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | 553 GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN; 554 sc->sc_pins[pin].pin_state = meson_pinctrl_pin_read(sc, pin); 555 strlcpy(sc->sc_pins[pin].pin_defname, pin_def->name, 556 sizeof(sc->sc_pins[pin].pin_defname)); 557 } 558 559 gp = &sc->sc_gp; 560 gp->gp_cookie = sc; 561 gp->gp_pin_read = meson_pinctrl_pin_read; 562 gp->gp_pin_write = meson_pinctrl_pin_write; 563 gp->gp_pin_ctl = meson_pinctrl_pin_ctl; 564 565 memset(&gba, 0, sizeof(gba)); 566 gba.gba_gc = gp; 567 gba.gba_pins = sc->sc_pins; 568 gba.gba_npins = npins; 569 config_found(sc->sc_dev, &gba, NULL, CFARG_EOL); 570 } 571 572 static int 573 meson_pinctrl_match(device_t parent, cfdata_t cf, void *aux) 574 { 575 struct fdt_attach_args * const faa = aux; 576 577 return of_compatible_match(faa->faa_phandle, compat_data); 578 } 579 580 static void 581 meson_pinctrl_attach(device_t parent, device_t self, void *aux) 582 { 583 struct meson_pinctrl_softc * const sc = device_private(self); 584 struct fdt_attach_args * const faa = aux; 585 586 sc->sc_dev = self; 587 sc->sc_phandle = faa->faa_phandle; 588 sc->sc_bst = faa->faa_bst; 589 sc->sc_conf = of_compatible_lookup(sc->sc_phandle, compat_data)->data; 590 mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_VM); 591 592 if (meson_pinctrl_initres(sc) != 0) 593 return; 594 595 aprint_naive("\n"); 596 aprint_normal(": %s\n", sc->sc_conf->name); 597 598 meson_pinctrl_initgpio(sc); 599 } 600 601 CFATTACH_DECL_NEW(meson_pinctrl, sizeof(struct meson_pinctrl_softc), 602 meson_pinctrl_match, meson_pinctrl_attach, NULL, NULL); 603