rk3399_iomux.c revision 1.3
1/* $NetBSD: rk3399_iomux.c,v 1.3 2019/04/30 22:24:27 jmcneill 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.3 2019/04/30 22:24:27 jmcneill 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 of_compat_data compat_data[] = { 185 { "rockchip,rk3399-pinctrl", (uintptr_t)&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 / 4); 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 453static void 454rk3399_iomux_attach(device_t parent, device_t self, void *aux) 455{ 456 struct rk3399_iomux_softc * const sc = device_private(self); 457 struct fdt_attach_args * const faa = aux; 458 const int phandle = faa->faa_phandle; 459 int child, sub; 460 461 sc->sc_dev = self; 462 sc->sc_syscon[RK_IOMUX_REGS_GRF] = fdtbus_syscon_acquire(phandle, "rockchip,grf"); 463 if (sc->sc_syscon[RK_IOMUX_REGS_GRF] == NULL) { 464 aprint_error(": couldn't acquire grf syscon\n"); 465 return; 466 } 467 sc->sc_syscon[RK_IOMUX_REGS_PMU] = fdtbus_syscon_acquire(phandle, "rockchip,pmu"); 468 if (sc->sc_syscon[RK_IOMUX_REGS_PMU] == NULL) { 469 aprint_error(": couldn't acquire pmu syscon\n"); 470 return; 471 } 472 sc->sc_conf = (void *)of_search_compatible(phandle, compat_data)->data; 473 474 aprint_naive("\n"); 475 aprint_normal(": RK3399 IOMUX control\n"); 476 477 for (child = OF_child(phandle); child; child = OF_peer(child)) { 478 for (sub = OF_child(child); sub; sub = OF_peer(sub)) { 479 if (!of_hasprop(sub, "rockchip,pins")) 480 continue; 481 fdtbus_register_pinctrl_config(self, sub, &rk3399_iomux_pinctrl_funcs); 482 } 483 } 484 485 fdtbus_pinctrl_configure(); 486 487 for (child = OF_child(phandle); child; child = OF_peer(child)) { 488 struct fdt_attach_args cfaa = *faa; 489 cfaa.faa_phandle = child; 490 cfaa.faa_name = fdtbus_get_string(child, "name"); 491 cfaa.faa_quiet = false; 492 493 config_found(self, &cfaa, NULL); 494 } 495} 496