rk3399_iomux.c revision 1.7
1/* $NetBSD: rk3399_iomux.c,v 1.7 2021/01/18 02:35:49 thorpej Exp $ */ 2 3/*- 4 * Copyright (c) 2018 Jared 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//#define RK3399_IOMUX_DEBUG 30 31#include <sys/cdefs.h> 32__KERNEL_RCSID(0, "$NetBSD: rk3399_iomux.c,v 1.7 2021/01/18 02:35:49 thorpej Exp $"); 33 34#include <sys/param.h> 35#include <sys/bus.h> 36#include <sys/device.h> 37#include <sys/intr.h> 38#include <sys/systm.h> 39#include <sys/mutex.h> 40#include <sys/kmem.h> 41#include <sys/gpio.h> 42#include <sys/lwp.h> 43 44#include <dev/fdt/fdtvar.h> 45#include <dev/fdt/syscon.h> 46 47/* PU/PD control */ 48#define GRF_GPIO_P_CTL(_idx) (0x3 << (((_idx) & 7) * 2)) 49#define GRF_GPIO_P_WRITE_EN(_idx) (0x3 << (((_idx) & 7) * 2 + 16)) 50/* Different bias value mapping based on pull type of pin */ 51#define IO_DEF_GPIO_P_CTL_Z 0 52#define IO_DEF_GPIO_P_CTL_PULLUP 1 53#define IO_DEF_GPIO_P_CTL_PULLDOWN 2 54#define IO_DEF_GPIO_P_CTL_RESERVED 3 55#define IO_1V8_GPIO_P_CTL_Z 0 56#define IO_1V8_GPIO_P_CTL_PULLDOWN 1 57#define IO_1V8_GPIO_P_CTL_Z_ALT 2 58#define IO_1V8_GPIO_P_CTL_PULLUP 3 59 60/* Drive strength control */ 61/* Different drive strength value mapping for GRF and PMU registers */ 62#define GRF_GPIO_E_CTL_2MA 0 63#define GRF_GPIO_E_CTL_4MA 1 64#define GRF_GPIO_E_CTL_8MA 2 65#define GRF_GPIO_E_CTL_12MA 3 66#define PMU_GPIO_E_CTL_5MA 0 67#define PMU_GPIO_E_CTL_10MA 1 68#define PMU_GPIO_E_CTL_15MA 2 69#define PMU_GPIO_E_CTL_20MA 3 70 71enum rk3399_drv_type { 72 RK3399_DRV_TYPE_IO_DEFAULT, 73 RK3399_DRV_TYPE_IO_1V8_3V0, 74 RK3399_DRV_TYPE_IO_1V8, 75 RK3399_DRV_TYPE_IO_1V8_3V0_AUTO, 76 RK3399_DRV_TYPE_IO_3V3, 77}; 78 79static int rk3399_drv_strength[5][9] = { 80 [RK3399_DRV_TYPE_IO_DEFAULT] = { 2, 4, 8, 12, -1 }, 81 [RK3399_DRV_TYPE_IO_1V8_3V0] = { 3, 6, 9, 12, -1 }, 82 [RK3399_DRV_TYPE_IO_1V8] = { 5, 10, 15, 20, -1 }, 83 [RK3399_DRV_TYPE_IO_1V8_3V0_AUTO] = { 4, 6, 8, 10, 12, 14, 16, 18, -1 }, 84 [RK3399_DRV_TYPE_IO_3V3] = { 4, 7, 10, 13, 16, 19, 22, 26, -1 }, 85}; 86 87enum rk3399_pull_type { 88 RK3399_PULL_TYPE_IO_DEFAULT, 89 RK3399_PULL_TYPE_IO_1V8_ONLY, 90}; 91 92struct rk3399_iomux { 93 enum rk3399_drv_type drv_type; 94 enum rk3399_pull_type pull_type; 95}; 96 97struct rk3399_iomux_bank { 98 struct rk3399_iomux iomux[5]; 99 u_int regs; 100#define RK_IOMUX_REGS_GRF 0 101#define RK_IOMUX_REGS_PMU 1 102}; 103 104static const struct rk3399_iomux_bank rk3399_iomux_banks[] = { 105 [0] = { 106 .regs = RK_IOMUX_REGS_PMU, 107 .iomux = { 108 [0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8, 109 .pull_type = RK3399_PULL_TYPE_IO_1V8_ONLY }, 110 [1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8, 111 .pull_type = RK3399_PULL_TYPE_IO_1V8_ONLY }, 112 [2] = { .drv_type = RK3399_DRV_TYPE_IO_DEFAULT, 113 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 114 [3] = { .drv_type = RK3399_DRV_TYPE_IO_DEFAULT, 115 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 116 }, 117 }, 118 [1] = { 119 .regs = RK_IOMUX_REGS_PMU, 120 .iomux = { 121 [0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0, 122 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 123 [1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0, 124 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 125 [2] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0, 126 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 127 [3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0, 128 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 129 } 130 }, 131 [2] = { 132 .regs = RK_IOMUX_REGS_GRF, 133 .iomux = { 134 [0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0, 135 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 136 [1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0, 137 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 138 [2] = { .drv_type = RK3399_DRV_TYPE_IO_1V8, 139 .pull_type = RK3399_PULL_TYPE_IO_1V8_ONLY }, 140 [3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8, 141 .pull_type = RK3399_PULL_TYPE_IO_1V8_ONLY }, 142 }, 143 }, 144 [3] = { 145 .regs = RK_IOMUX_REGS_GRF, 146 .iomux = { 147 [0] = { .drv_type = RK3399_DRV_TYPE_IO_3V3, 148 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 149 [1] = { .drv_type = RK3399_DRV_TYPE_IO_3V3, 150 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 151 [2] = { .drv_type = RK3399_DRV_TYPE_IO_3V3, 152 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 153 [3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0, 154 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 155 }, 156 }, 157 [4] = { 158 .regs = RK_IOMUX_REGS_GRF, 159 .iomux = { 160 [0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0, 161 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 162 [1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0_AUTO, 163 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 164 [2] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0, 165 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 166 [3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0, 167 .pull_type = RK3399_PULL_TYPE_IO_DEFAULT }, 168 }, 169 }, 170}; 171 172#define RK3399_IOMUX_BANK_IS_PMU(_bank) (rk3399_iomux_banks[(_bank)].regs == RK_IOMUX_REGS_PMU) 173 174struct rk3399_iomux_conf { 175 const struct rk3399_iomux_bank *banks; 176 u_int nbanks; 177}; 178 179static const struct rk3399_iomux_conf rk3399_iomux_conf = { 180 .banks = rk3399_iomux_banks, 181 .nbanks = __arraycount(rk3399_iomux_banks), 182}; 183 184static const struct device_compatible_entry compat_data[] = { 185 { .compat = "rockchip,rk3399-pinctrl", .data = &rk3399_iomux_conf }, 186 { NULL } 187}; 188 189struct rk3399_iomux_softc { 190 device_t sc_dev; 191 struct syscon *sc_syscon[2]; 192 193 const struct rk3399_iomux_conf *sc_conf; 194}; 195 196#define LOCK(syscon) \ 197 syscon_lock(syscon) 198#define UNLOCK(syscon) \ 199 syscon_unlock(syscon) 200#define RD4(syscon, reg) \ 201 syscon_read_4(syscon, (reg)) 202#define WR4(syscon, reg, val) \ 203 syscon_write_4(syscon, (reg), (val)) 204 205static int rk3399_iomux_match(device_t, cfdata_t, void *); 206static void rk3399_iomux_attach(device_t, device_t, void *); 207 208CFATTACH_DECL_NEW(rk3399_iomux, sizeof(struct rk3399_iomux_softc), 209 rk3399_iomux_match, rk3399_iomux_attach, NULL, NULL); 210 211static void 212rk3399_iomux_set_bias(struct rk3399_iomux_softc *sc, u_int bank, u_int idx, int flags) 213{ 214 const struct rk3399_iomux_bank *banks = sc->sc_conf->banks; 215 bus_size_t reg; 216 u_int bias; 217 218 KASSERT(bank < sc->sc_conf->nbanks); 219 220 struct syscon * const syscon = sc->sc_syscon[banks[bank].regs]; 221 if (RK3399_IOMUX_BANK_IS_PMU(bank)) { 222 reg = 0x00040 + (0x10 * bank); 223 } else { 224 reg = 0x0e040 + (0x10 * (bank - 2)); 225 } 226 reg += 0x4 * (idx / 8); 227 228 const int pull_type = banks[bank].iomux[idx / 8].pull_type; 229 230 if (flags == GPIO_PIN_PULLUP) { 231 bias = pull_type == RK3399_PULL_TYPE_IO_DEFAULT ? 232 IO_DEF_GPIO_P_CTL_PULLUP : 233 IO_1V8_GPIO_P_CTL_PULLUP; 234 } else if (flags == GPIO_PIN_PULLDOWN) { 235 bias = pull_type == RK3399_PULL_TYPE_IO_DEFAULT ? 236 IO_DEF_GPIO_P_CTL_PULLDOWN : 237 IO_1V8_GPIO_P_CTL_PULLDOWN; 238 } else { 239 bias = pull_type == RK3399_PULL_TYPE_IO_DEFAULT ? 240 IO_DEF_GPIO_P_CTL_Z : 241 IO_1V8_GPIO_P_CTL_Z; 242 } 243 244 const uint32_t bias_val = __SHIFTIN(bias, GRF_GPIO_P_CTL(idx)); 245 const uint32_t bias_mask = GRF_GPIO_P_WRITE_EN(idx); 246 247#ifdef RK3399_IOMUX_DEBUG 248 printf("%s: bank %d idx %d flags %#x: %08x -> ", __func__, bank, idx, flags, RD4(syscon, reg)); 249#endif 250 WR4(syscon, reg, bias_val | bias_mask); 251#ifdef RK3399_IOMUX_DEBUG 252 printf("%08x (reg %#lx)\n", RD4(syscon, reg), reg); 253#endif 254} 255 256static int 257rk3399_iomux_map_drive_strength(struct rk3399_iomux_softc *sc, enum rk3399_drv_type drv_type, u_int val) 258{ 259 for (int n = 0; rk3399_drv_strength[drv_type][n] != -1; n++) 260 if (rk3399_drv_strength[drv_type][n] == val) 261 return n; 262 return -1; 263} 264 265static int 266rk3399_iomux_set_drive_strength(struct rk3399_iomux_softc *sc, u_int bank, u_int idx, u_int val) 267{ 268 const struct rk3399_iomux_bank *banks = sc->sc_conf->banks; 269 uint32_t drv_mask, drv_val; 270 bus_size_t reg; 271 272 KASSERT(bank < sc->sc_conf->nbanks); 273 274 if (idx >= 32) 275 return EINVAL; 276 277 const int drv = rk3399_iomux_map_drive_strength(sc, banks[bank].iomux[idx / 8].drv_type, val); 278 if (drv == -1) 279 return EINVAL; 280 281 struct syscon * const syscon = sc->sc_syscon[banks[bank].regs]; 282 switch (bank) { 283 case 0: 284 case 1: 285 reg = 0x00040 + (0x10 * bank) + 0x4 * (idx / 4); 286 drv_mask = 0x3 << ((idx & 7) * 2); 287 break; 288 case 2: 289 reg = 0x0e100 + 0x4 * (idx / 4); 290 drv_mask = 0x3 << ((idx & 7) * 2); 291 break; 292 case 3: 293 switch (idx / 8) { 294 case 0: 295 case 1: 296 case 2: 297 reg = 0x0e110 + 0x8 * (idx / 4); 298 drv_mask = 0x7 << ((idx & 7) * 3); 299 break; 300 case 3: 301 reg = 0x0e128; 302 drv_mask = 0x3 << ((idx & 7) * 2); 303 break; 304 default: 305 return EINVAL; 306 } 307 break; 308 case 4: 309 switch (idx / 8) { 310 case 0: 311 reg = 0x0e12c; 312 drv_mask = 0x3 << ((idx & 7) * 2); 313 break; 314 case 1: 315 reg = 0x0e130; 316 drv_mask = 0x7 << ((idx & 7) * 3); 317 break; 318 case 2: 319 reg = 0x0e138; 320 drv_mask = 0x3 << ((idx & 7) * 2); 321 break; 322 case 3: 323 reg = 0x0e13c; 324 drv_mask = 0x3 << ((idx & 7) * 2); 325 break; 326 default: 327 return EINVAL; 328 } 329 break; 330 default: 331 return EINVAL; 332 } 333 drv_val = __SHIFTIN(val, drv_mask); 334 335 while (drv_mask) { 336 const uint32_t write_val = drv_val & 0xffff; 337 const uint32_t write_mask = (drv_mask & 0xffff) << 16; 338 if (write_mask) { 339#ifdef RK3399_IOMUX_DEBUG 340 printf("%s: bank %d idx %d val %d: %08x -> ", __func__, bank, idx, val, RD4(syscon, reg)); 341#endif 342 WR4(syscon, reg, write_val | write_mask); 343#ifdef RK3399_IOMUX_DEBUG 344 printf("%08x (reg %#lx)\n", RD4(syscon, reg), reg); 345#endif 346 } 347 reg += 0x4; 348 drv_val >>= 16; 349 drv_mask >>= 16; 350 } 351 352 return 0; 353} 354 355static void 356rk3399_iomux_set_mux(struct rk3399_iomux_softc *sc, u_int bank, u_int idx, u_int mux) 357{ 358 const struct rk3399_iomux_bank *banks = sc->sc_conf->banks; 359 bus_size_t reg; 360 uint32_t mask; 361 362 KASSERT(bank < sc->sc_conf->nbanks); 363 364 struct syscon * const syscon = sc->sc_syscon[banks[bank].regs]; 365 if (RK3399_IOMUX_BANK_IS_PMU(bank)) { 366 reg = 0x00000 + (0x10 * bank); 367 } else { 368 reg = 0x0e000 + (0x10 * (bank - 2)); 369 } 370 reg += 0x4 * (idx / 8); 371 mask = 3 << ((idx & 7) * 2); 372 373#ifdef RK3399_IOMUX_DEBUG 374 printf("%s: bank %d idx %d mux %#x: %08x -> ", __func__, bank, idx, mux, RD4(syscon, reg)); 375#endif 376 WR4(syscon, reg, (mask << 16) | __SHIFTIN(mux, mask)); 377#ifdef RK3399_IOMUX_DEBUG 378 printf("%08x (reg %#lx)\n", RD4(syscon, reg), reg); 379#endif 380} 381 382static int 383rk3399_iomux_config(struct rk3399_iomux_softc *sc, const int phandle, u_int bank, u_int idx, u_int mux) 384{ 385 386 const int bias = fdtbus_pinctrl_parse_bias(phandle, NULL); 387 if (bias != -1) 388 rk3399_iomux_set_bias(sc, bank, idx, bias); 389 390 const int drv = fdtbus_pinctrl_parse_drive_strength(phandle); 391 if (drv != -1 && 392 rk3399_iomux_set_drive_strength(sc, bank, idx, drv) != 0) 393 return EINVAL; 394 395#if notyet 396 int output_value; 397 const int direction = 398 fdtbus_pinctrl_parse_input_output(phandle, &output_value); 399 if (direction != -1) { 400 rk3399_iomux_set_direction(sc, bank, idx, direction, 401 output_value); 402 } 403#endif 404 405 rk3399_iomux_set_mux(sc, bank, idx, mux); 406 407 return 0; 408} 409 410static int 411rk3399_iomux_pinctrl_set_config(device_t dev, const void *data, size_t len) 412{ 413 struct rk3399_iomux_softc * const sc = device_private(dev); 414 const struct rk3399_iomux_bank *banks = sc->sc_conf->banks; 415 int pins_len; 416 417 if (len != 4) 418 return -1; 419 420 const int phandle = fdtbus_get_phandle_from_native(be32dec(data)); 421 const u_int *pins = fdtbus_get_prop(phandle, "rockchip,pins", &pins_len); 422 423 while (pins_len >= 16) { 424 const u_int bank = be32toh(pins[0]); 425 const u_int idx = be32toh(pins[1]); 426 const u_int mux = be32toh(pins[2]); 427 const int cfg = fdtbus_get_phandle_from_native(be32toh(pins[3])); 428 429 struct syscon * const syscon = sc->sc_syscon[banks[bank].regs]; 430 LOCK(syscon); 431 rk3399_iomux_config(sc, cfg, bank, idx, mux); 432 UNLOCK(syscon); 433 434 pins_len -= 16; 435 pins += 4; 436 } 437 438 return 0; 439} 440 441static struct fdtbus_pinctrl_controller_func rk3399_iomux_pinctrl_funcs = { 442 .set_config = rk3399_iomux_pinctrl_set_config, 443}; 444 445static int 446rk3399_iomux_match(device_t parent, cfdata_t cf, void *aux) 447{ 448 struct fdt_attach_args * const faa = aux; 449 450 return of_match_compat_data(faa->faa_phandle, compat_data); 451} 452 453#ifdef RK3399_IOMUX_FORCE_ENABLE_SWJ_DP 454/* 455 * This enables the SWJ-DP (Serial Wire JTAG Debug Port). 456 * If you enable this you must also disable sdhc due to pin conflicts. 457 */ 458static void 459rk3399_iomux_force_enable_swj_dp(struct rk3399_iomux_softc * const sc) 460{ 461 struct syscon * const syscon = sc->sc_syscon[RK_IOMUX_REGS_GRF]; 462 uint32_t val; 463 464 aprint_normal_dev(sc->sc_dev, "enabling on-chip debugging\n"); 465#define GRF_GPIO4B_IOMUX 0xe024 466#define GRF_GPIO4B_IOMUX_TCK __BITS(5,4) 467#define GRF_GPIO4B_IOMUX_TMS __BITS(7,6) 468#define GRF_SOC_CON7 0xe21c 469#define GRF_SOC_CON7_FORCE_JTAG __BIT(12) 470 LOCK(syscon); 471 val = RD4(syscon, GRF_GPIO4B_IOMUX); 472 val &= ~(GRF_GPIO4B_IOMUX_TCK | GRF_GPIO4B_IOMUX_TMS); 473 val |= __SHIFTIN(0x2, GRF_GPIO4B_IOMUX_TCK); 474 val |= __SHIFTIN(0x2, GRF_GPIO4B_IOMUX_TMS); 475 WR4(syscon, GRF_GPIO4B_IOMUX, val); 476 val = RD4(syscon, GRF_SOC_CON7); 477 val |= GRF_SOC_CON7_FORCE_JTAG; 478 WR4(syscon, GRF_SOC_CON7, val); 479 UNLOCK(syscon); 480} 481#endif 482 483static void 484rk3399_iomux_attach(device_t parent, device_t self, void *aux) 485{ 486 struct rk3399_iomux_softc * const sc = device_private(self); 487 struct fdt_attach_args * const faa = aux; 488 const int phandle = faa->faa_phandle; 489 int child, sub; 490 491 sc->sc_dev = self; 492 sc->sc_syscon[RK_IOMUX_REGS_GRF] = fdtbus_syscon_acquire(phandle, "rockchip,grf"); 493 if (sc->sc_syscon[RK_IOMUX_REGS_GRF] == NULL) { 494 aprint_error(": couldn't acquire grf syscon\n"); 495 return; 496 } 497 sc->sc_syscon[RK_IOMUX_REGS_PMU] = fdtbus_syscon_acquire(phandle, "rockchip,pmu"); 498 if (sc->sc_syscon[RK_IOMUX_REGS_PMU] == NULL) { 499 aprint_error(": couldn't acquire pmu syscon\n"); 500 return; 501 } 502 sc->sc_conf = of_search_compatible(phandle, compat_data)->data; 503 504 aprint_naive("\n"); 505 aprint_normal(": RK3399 IOMUX control\n"); 506 507 for (child = OF_child(phandle); child; child = OF_peer(child)) { 508 for (sub = OF_child(child); sub; sub = OF_peer(sub)) { 509 if (!of_hasprop(sub, "rockchip,pins")) 510 continue; 511 fdtbus_register_pinctrl_config(self, sub, &rk3399_iomux_pinctrl_funcs); 512 } 513 } 514 515 for (child = OF_child(phandle); child; child = OF_peer(child)) { 516 struct fdt_attach_args cfaa = *faa; 517 cfaa.faa_phandle = child; 518 cfaa.faa_name = fdtbus_get_string(child, "name"); 519 cfaa.faa_quiet = false; 520 521 config_found(self, &cfaa, NULL); 522 } 523 524#ifdef RK3399_IOMUX_FORCE_ENABLE_SWJ_DP 525 rk3399_iomux_force_enable_swj_dp(sc); 526#endif 527} 528