Home | History | Annotate | Line # | Download | only in rockchip
rk3399_iomux.c revision 1.1.2.3
      1 /* $NetBSD: rk3399_iomux.c,v 1.1.2.3 2019/01/26 22:00:01 pgoyette Exp $ */
      2 
      3 /*-
      4  * Copyright (c) 2018 Jared McNeill <jmcneill (at) 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.1.2.3 2019/01/26 22:00:01 pgoyette 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 for GRF and PMU registers */
     51 #define	  GRF_GPIO_P_CTL_Z		0
     52 #define	  GRF_GPIO_P_CTL_PULLDOWN	1
     53 #define	  GRF_GPIO_P_CTL_Z_ALT		2
     54 #define	  GRF_GPIO_P_CTL_PULLUP		3
     55 #define	  PMU_GPIO_P_CTL_Z		0
     56 #define	  PMU_GPIO_P_CTL_PULLUP		1
     57 #define	  PMU_GPIO_P_CTL_PULLDOWN	2
     58 #define	  PMU_GPIO_P_CTL_RESERVED	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 
     71 enum 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 
     79 static 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 
     87 struct rk3399_iomux {
     88 	enum rk3399_drv_type	drv_type;
     89 };
     90 
     91 struct rk3399_iomux_bank {
     92 	struct rk3399_iomux	iomux[5];
     93 	u_int			regs;
     94 #define	RK_IOMUX_REGS_GRF	0
     95 #define	RK_IOMUX_REGS_PMU	1
     96 };
     97 
     98 static const struct rk3399_iomux_bank rk3399_iomux_banks[] = {
     99 	[0] = {
    100 		.regs = RK_IOMUX_REGS_PMU,
    101 		.iomux = {
    102 			[0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8 },
    103 			[1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8 },
    104 			[2] = { .drv_type = RK3399_DRV_TYPE_IO_DEFAULT },
    105 			[3] = { .drv_type = RK3399_DRV_TYPE_IO_DEFAULT },
    106 		},
    107 	},
    108 	[1] = {
    109 		.regs = RK_IOMUX_REGS_PMU,
    110 		.iomux = {
    111 			[0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
    112 			[1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
    113 			[2] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
    114 			[3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
    115 		}
    116 	},
    117 	[2] = {
    118 		.regs = RK_IOMUX_REGS_GRF,
    119 		.iomux = {
    120 			[0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
    121 			[1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
    122 			[2] = { .drv_type = RK3399_DRV_TYPE_IO_1V8 },
    123 			[3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8 },
    124 		},
    125 	},
    126 	[3] = {
    127 		.regs = RK_IOMUX_REGS_GRF,
    128 		.iomux = {
    129 			[0] = { .drv_type = RK3399_DRV_TYPE_IO_3V3 },
    130 			[1] = { .drv_type = RK3399_DRV_TYPE_IO_3V3 },
    131 			[2] = { .drv_type = RK3399_DRV_TYPE_IO_3V3 },
    132 			[3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
    133 		},
    134 	},
    135 	[4] = {
    136 		.regs = RK_IOMUX_REGS_GRF,
    137 		.iomux = {
    138 			[0] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
    139 			[1] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0_AUTO },
    140 			[2] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
    141 			[3] = { .drv_type = RK3399_DRV_TYPE_IO_1V8_3V0 },
    142 		},
    143 	},
    144 };
    145 
    146 #define	RK3399_IOMUX_BANK_IS_PMU(_bank)	(rk3399_iomux_banks[(_bank)].regs == RK_IOMUX_REGS_PMU)
    147 
    148 struct rk3399_iomux_conf {
    149 	const struct rk3399_iomux_bank *banks;
    150 	u_int nbanks;
    151 };
    152 
    153 static const struct rk3399_iomux_conf rk3399_iomux_conf = {
    154 	.banks = rk3399_iomux_banks,
    155 	.nbanks = __arraycount(rk3399_iomux_banks),
    156 };
    157 
    158 static const struct of_compat_data compat_data[] = {
    159 	{ "rockchip,rk3399-pinctrl",	(uintptr_t)&rk3399_iomux_conf },
    160 	{ NULL }
    161 };
    162 
    163 struct rk3399_iomux_softc {
    164 	device_t sc_dev;
    165 	struct syscon *sc_syscon[2];
    166 
    167 	const struct rk3399_iomux_conf *sc_conf;
    168 };
    169 
    170 #define	LOCK(syscon) 		\
    171 	syscon_lock(syscon)
    172 #define	UNLOCK(syscon)		\
    173 	syscon_unlock(syscon)
    174 #define	RD4(syscon, reg) 	\
    175 	syscon_read_4(syscon, (reg))
    176 #define	WR4(syscon, reg, val) 	\
    177 	syscon_write_4(syscon, (reg), (val))
    178 
    179 static int	rk3399_iomux_match(device_t, cfdata_t, void *);
    180 static void	rk3399_iomux_attach(device_t, device_t, void *);
    181 
    182 CFATTACH_DECL_NEW(rk3399_iomux, sizeof(struct rk3399_iomux_softc),
    183 	rk3399_iomux_match, rk3399_iomux_attach, NULL, NULL);
    184 
    185 static void
    186 rk3399_iomux_set_bias(struct rk3399_iomux_softc *sc, u_int bank, u_int idx, int flags)
    187 {
    188 	const struct rk3399_iomux_bank *banks = sc->sc_conf->banks;
    189 	bus_size_t reg;
    190 	u_int bias;
    191 
    192 	KASSERT(bank < sc->sc_conf->nbanks);
    193 
    194 	struct syscon * const syscon = sc->sc_syscon[banks[bank].regs];
    195 	if (RK3399_IOMUX_BANK_IS_PMU(bank)) {
    196 		reg = 0x00040 + (0x10 * bank);
    197 	} else {
    198 		reg = 0x0e040 + (0x10 * (bank - 2));
    199 	}
    200 	reg += 0x4 * (idx / 8);
    201 
    202 	if (flags == GPIO_PIN_PULLUP) {
    203 		bias = RK3399_IOMUX_BANK_IS_PMU(bank) ? PMU_GPIO_P_CTL_PULLUP : GRF_GPIO_P_CTL_PULLUP;
    204 	} else if (flags == GPIO_PIN_PULLDOWN) {
    205 		bias = RK3399_IOMUX_BANK_IS_PMU(bank) ? PMU_GPIO_P_CTL_PULLDOWN : GRF_GPIO_P_CTL_PULLDOWN;
    206 	} else {
    207 		bias = RK3399_IOMUX_BANK_IS_PMU(bank) ? PMU_GPIO_P_CTL_Z : GRF_GPIO_P_CTL_Z;
    208 	}
    209 
    210 	const uint32_t bias_val = __SHIFTIN(bias, GRF_GPIO_P_CTL(idx));
    211 	const uint32_t bias_mask = GRF_GPIO_P_WRITE_EN(idx);
    212 
    213 #ifdef RK3399_IOMUX_DEBUG
    214 	printf("%s: bank %d idx %d flags %#x: %08x -> ", __func__, bank, idx, flags, RD4(syscon, reg));
    215 #endif
    216 	WR4(syscon, reg, bias_val | bias_mask);
    217 #ifdef RK3399_IOMUX_DEBUG
    218 	printf("%08x (reg %#lx)\n", RD4(syscon, reg), reg);
    219 #endif
    220 }
    221 
    222 static int
    223 rk3399_iomux_map_drive_strength(struct rk3399_iomux_softc *sc, enum rk3399_drv_type drv_type, u_int val)
    224 {
    225 	for (int n = 0; rk3399_drv_strength[drv_type][n] != -1; n++)
    226 		if (rk3399_drv_strength[drv_type][n] == val)
    227 			return n;
    228 	return -1;
    229 }
    230 
    231 static int
    232 rk3399_iomux_set_drive_strength(struct rk3399_iomux_softc *sc, u_int bank, u_int idx, u_int val)
    233 {
    234 	const struct rk3399_iomux_bank *banks = sc->sc_conf->banks;
    235 	uint32_t drv_mask, drv_val;
    236 	bus_size_t reg;
    237 
    238 	KASSERT(bank < sc->sc_conf->nbanks);
    239 
    240 	if (idx >= 32)
    241 		return EINVAL;
    242 
    243 	const int drv = rk3399_iomux_map_drive_strength(sc, banks[bank].iomux[idx / 8].drv_type, val);
    244 	if (drv == -1)
    245 		return EINVAL;
    246 
    247 	struct syscon * const syscon = sc->sc_syscon[banks[bank].regs];
    248 	switch (bank) {
    249 	case 0:
    250 	case 1:
    251 		reg = 0x00040 + (0x10 * bank) + 0x4 * (idx / 4);
    252 		drv_mask = 0x3 << ((idx & 7) * 2);
    253 		break;
    254 	case 2:
    255 		reg = 0x0e100 + 0x4 * (idx / 4);
    256 		drv_mask = 0x3 << ((idx & 7) * 2);
    257 		break;
    258 	case 3:
    259 		switch (idx / 8) {
    260 		case 0:
    261 		case 1:
    262 		case 2:
    263 			reg = 0x0e110 + 0x8 * (idx / 4);
    264 			drv_mask = 0x7 << ((idx & 7) * 3);
    265 			break;
    266 		case 3:
    267 			reg = 0x0e128;
    268 			drv_mask = 0x3 << ((idx & 7) * 2);
    269 			break;
    270 		default:
    271 			return EINVAL;
    272 		}
    273 		break;
    274 	case 4:
    275 		switch (idx / 8) {
    276 		case 0:
    277 			reg = 0x0e12c;
    278 			drv_mask = 0x3 << ((idx & 7) * 2);
    279 			break;
    280 		case 1:
    281 			reg = 0x0e130;
    282 			drv_mask = 0x7 << ((idx & 7) * 3);
    283 			break;
    284 		case 2:
    285 			reg = 0x0e138;
    286 			drv_mask = 0x3 << ((idx & 7) * 2);
    287 			break;
    288 		case 3:
    289 			reg = 0x0e13c;
    290 			drv_mask = 0x3 << ((idx & 7) * 2);
    291 			break;
    292 		default:
    293 			return EINVAL;
    294 		}
    295 		break;
    296 	default:
    297 		return EINVAL;
    298 	}
    299 	drv_val = __SHIFTIN(val, drv_mask);
    300 
    301 	while (drv_mask) {
    302 		const uint32_t write_val = drv_val & 0xffff;
    303 		const uint32_t write_mask = (drv_mask & 0xffff) << 16;
    304 		if (write_mask) {
    305 #ifdef RK3399_IOMUX_DEBUG
    306 			printf("%s: bank %d idx %d val %d: %08x -> ", __func__, bank, idx, val, RD4(syscon, reg));
    307 #endif
    308 			WR4(syscon, reg, write_val | write_mask);
    309 #ifdef RK3399_IOMUX_DEBUG
    310 			printf("%08x (reg %#lx)\n", RD4(syscon, reg), reg);
    311 #endif
    312 		}
    313 		reg += 0x4;
    314 		drv_val >>= 16;
    315 		drv_mask >>= 16;
    316 	}
    317 
    318 	return 0;
    319 }
    320 
    321 static void
    322 rk3399_iomux_set_mux(struct rk3399_iomux_softc *sc, u_int bank, u_int idx, u_int mux)
    323 {
    324 	const struct rk3399_iomux_bank *banks = sc->sc_conf->banks;
    325 	bus_size_t reg;
    326 	uint32_t mask;
    327 
    328 	KASSERT(bank < sc->sc_conf->nbanks);
    329 
    330 	struct syscon * const syscon = sc->sc_syscon[banks[bank].regs];
    331 	if (RK3399_IOMUX_BANK_IS_PMU(bank)) {
    332 		reg = 0x00000 + (0x10 * bank);
    333 	} else {
    334 		reg = 0x0e000 + (0x10 * (bank - 2));
    335 	}
    336 	reg += 0x4 * (idx / 4);
    337 	mask = 3 << ((idx & 7) * 2);
    338 
    339 #ifdef RK3399_IOMUX_DEBUG
    340 	printf("%s: bank %d idx %d mux %#x: %08x -> ", __func__, bank, idx, mux, RD4(syscon, reg));
    341 #endif
    342 	WR4(syscon, reg, (mask << 16) | __SHIFTIN(mux, mask));
    343 #ifdef RK3399_IOMUX_DEBUG
    344 	printf("%08x (reg %#lx)\n", RD4(syscon, reg), reg);
    345 #endif
    346 }
    347 
    348 static int
    349 rk3399_iomux_config(struct rk3399_iomux_softc *sc, const int phandle, u_int bank, u_int idx, u_int mux)
    350 {
    351 
    352 	const int bias = fdtbus_pinctrl_parse_bias(phandle, NULL);
    353 	if (bias != -1)
    354 		rk3399_iomux_set_bias(sc, bank, idx, bias);
    355 
    356 	const int drv = fdtbus_pinctrl_parse_drive_strength(phandle);
    357 	if (drv != -1 &&
    358 	    rk3399_iomux_set_drive_strength(sc, bank, idx, drv) != 0)
    359 		return EINVAL;
    360 
    361 #if notyet
    362 	int output_value;
    363 	const int direction =
    364 	    fdtbus_pinctrl_parse_input_output(phandle, &output_value);
    365 	if (direction != -1) {
    366 		rk3399_iomux_set_direction(sc, bank, idx, direction,
    367 		    output_value);
    368 	}
    369 #endif
    370 
    371 	rk3399_iomux_set_mux(sc, bank, idx, mux);
    372 
    373 	return 0;
    374 }
    375 
    376 static int
    377 rk3399_iomux_pinctrl_set_config(device_t dev, const void *data, size_t len)
    378 {
    379 	struct rk3399_iomux_softc * const sc = device_private(dev);
    380 	const struct rk3399_iomux_bank *banks = sc->sc_conf->banks;
    381 	int pins_len;
    382 
    383 	if (len != 4)
    384 		return -1;
    385 
    386 	const int phandle = fdtbus_get_phandle_from_native(be32dec(data));
    387 	const u_int *pins = fdtbus_get_prop(phandle, "rockchip,pins", &pins_len);
    388 
    389 	while (pins_len >= 16) {
    390 		const u_int bank = be32toh(pins[0]);
    391 		const u_int idx = be32toh(pins[1]);
    392 		const u_int mux = be32toh(pins[2]);
    393 		const int cfg = fdtbus_get_phandle_from_native(be32toh(pins[3]));
    394 
    395 		struct syscon * const syscon = sc->sc_syscon[banks[bank].regs];
    396 		LOCK(syscon);
    397 		rk3399_iomux_config(sc, cfg, bank, idx, mux);
    398 		UNLOCK(syscon);
    399 
    400 		pins_len -= 16;
    401 		pins += 4;
    402 	}
    403 
    404 	return 0;
    405 }
    406 
    407 static struct fdtbus_pinctrl_controller_func rk3399_iomux_pinctrl_funcs = {
    408 	.set_config = rk3399_iomux_pinctrl_set_config,
    409 };
    410 
    411 static int
    412 rk3399_iomux_match(device_t parent, cfdata_t cf, void *aux)
    413 {
    414 	struct fdt_attach_args * const faa = aux;
    415 
    416 	return of_match_compat_data(faa->faa_phandle, compat_data);
    417 }
    418 
    419 static void
    420 rk3399_iomux_attach(device_t parent, device_t self, void *aux)
    421 {
    422 	struct rk3399_iomux_softc * const sc = device_private(self);
    423 	struct fdt_attach_args * const faa = aux;
    424 	const int phandle = faa->faa_phandle;
    425 	int child, sub;
    426 
    427 	sc->sc_dev = self;
    428 	sc->sc_syscon[RK_IOMUX_REGS_GRF] = fdtbus_syscon_acquire(phandle, "rockchip,grf");
    429 	if (sc->sc_syscon[RK_IOMUX_REGS_GRF] == NULL) {
    430 		aprint_error(": couldn't acquire grf syscon\n");
    431 		return;
    432 	}
    433 	sc->sc_syscon[RK_IOMUX_REGS_PMU] = fdtbus_syscon_acquire(phandle, "rockchip,pmu");
    434 	if (sc->sc_syscon[RK_IOMUX_REGS_PMU] == NULL) {
    435 		aprint_error(": couldn't acquire pmu syscon\n");
    436 		return;
    437 	}
    438 	sc->sc_conf = (void *)of_search_compatible(phandle, compat_data)->data;
    439 
    440 	aprint_naive("\n");
    441 	aprint_normal(": RK3399 IOMUX control\n");
    442 
    443 	for (child = OF_child(phandle); child; child = OF_peer(child)) {
    444 		for (sub = OF_child(child); sub; sub = OF_peer(sub)) {
    445 			if (!of_hasprop(sub, "rockchip,pins"))
    446 				continue;
    447 			fdtbus_register_pinctrl_config(self, sub, &rk3399_iomux_pinctrl_funcs);
    448 		}
    449 	}
    450 
    451 	fdtbus_pinctrl_configure();
    452 
    453 	for (child = OF_child(phandle); child; child = OF_peer(child)) {
    454 		struct fdt_attach_args cfaa = *faa;
    455 		cfaa.faa_phandle = child;
    456 		cfaa.faa_name = fdtbus_get_string(child, "name");
    457 		cfaa.faa_quiet = false;
    458 
    459 		config_found(self, &cfaa, NULL);
    460 	}
    461 }
    462