1 /* $NetBSD: exynos_pinctrl.c,v 1.22 2022/02/11 23:48:41 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2015, 2020 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Marty Fouts, and by Nick Hudson 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include "opt_exynos.h" 33 #include "opt_arm_debug.h" 34 #include "gpio.h" 35 36 #include <sys/cdefs.h> 37 __KERNEL_RCSID(1, "$NetBSD: exynos_pinctrl.c,v 1.22 2022/02/11 23:48:41 riastradh Exp $"); 38 39 #include <sys/param.h> 40 #include <sys/bus.h> 41 #include <sys/device.h> 42 #include <sys/intr.h> 43 #include <sys/systm.h> 44 #include <sys/gpio.h> 45 46 #include <dev/gpio/gpiovar.h> 47 48 #include <arm/samsung/exynos_reg.h> 49 #include <arm/samsung/exynos_var.h> 50 #include <arm/samsung/exynos_intr.h> 51 #include <arm/samsung/exynos_pinctrl.h> 52 53 #include <dev/fdt/fdtvar.h> 54 55 struct exynos_pinctrl_config { 56 int pc_phandle; 57 struct exynos_gpio_pin_cfg pc_pincfg; 58 struct exynos_pinctrl_softc *pc_sc; 59 }; 60 61 static int exynos_pinctrl_match(device_t, cfdata_t, void *); 62 static void exynos_pinctrl_attach(device_t, device_t, void *); 63 64 static int exynos_pinctrl_set_cfg(device_t, const void *, size_t); 65 static void exynos_parse_config(int, struct exynos_gpio_pin_cfg *); 66 67 static struct fdtbus_pinctrl_controller_func exynos_pinctrl_controller_func = { 68 .set_config = exynos_pinctrl_set_cfg 69 }; 70 71 CFATTACH_DECL_NEW(exynos_pinctrl, sizeof(struct exynos_pinctrl_softc), 72 exynos_pinctrl_match, exynos_pinctrl_attach, NULL, NULL); 73 74 75 static const struct device_compatible_entry compat_data[] = { 76 { .compat = "samsung,exynos5410-pinctrl", 77 .data = &exynos5410_pinctrl_banks }, 78 { .compat = "samsung,exynos5420-pinctrl", 79 .data = &exynos5420_pinctrl_banks }, 80 81 DEVICE_COMPAT_EOL 82 }; 83 84 static int 85 exynos_pinctrl_match(device_t parent, cfdata_t cf, void *aux) 86 { 87 struct fdt_attach_args * const faa = aux; 88 89 return of_compatible_match(faa->faa_phandle, compat_data); 90 } 91 92 static void 93 exynos_pinctrl_attach(device_t parent, device_t self, void *aux) 94 { 95 struct exynos_pinctrl_softc * const sc = device_private(self); 96 struct fdt_attach_args * const faa = aux; 97 bus_addr_t addr; 98 bus_size_t size; 99 int error; 100 int child; 101 102 if (fdtbus_get_reg(faa->faa_phandle, 0, &addr, &size) != 0) { 103 aprint_error(": couldn't get registers\n"); 104 return; 105 } 106 107 aprint_normal(" pinctrl @ 0x%08x ", (uint)addr); 108 sc->sc_dev = self; 109 sc->sc_bst = faa->faa_bst; 110 sc->sc_epb = of_compatible_lookup(faa->faa_phandle, compat_data)->data; 111 112 error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh); 113 if (error) { 114 aprint_error(": couldn't map %#" PRIxBUSADDR ": %d", 115 addr, error); 116 return; 117 } 118 119 aprint_naive("\n"); 120 aprint_normal("\n"); 121 122 for (child = OF_child(faa->faa_phandle); child; 123 child = OF_peer(child)) { 124 125 if (of_hasprop(child, "gpio-controller")) { 126 exynos_gpio_bank_config(sc, faa, child); 127 } 128 129 if (of_hasprop(child, "samsung,pins")) { 130 fdtbus_register_pinctrl_config(self, child, 131 &exynos_pinctrl_controller_func); 132 } 133 } 134 } 135 136 static void 137 exynos_parse_config(int phandle, struct exynos_gpio_pin_cfg *gc) 138 { 139 gc->cfg_valid = of_getprop_uint32(phandle, "samsung,pin-function", &gc->cfg) == 0; 140 gc->pud_valid = of_getprop_uint32(phandle, "samsung,pin-pud", &gc->pud) == 0; 141 gc->drv_valid = of_getprop_uint32(phandle, "samsung,pin-drv", &gc->drv) == 0; 142 gc->conpwd_valid = of_getprop_uint32(phandle, "samsung,pin-conpwd", &gc->conpwd) == 0; 143 gc->pudpwd_valid = of_getprop_uint32(phandle, "samsung,pin-pudpwd", &gc->pudpwd) == 0; 144 } 145 146 static int 147 exynos_parse_pin(const char *pinname) 148 { 149 150 const int len = strlen(pinname); 151 152 if (len == 0) 153 return -1; 154 155 if (pinname[len - 1] < '0' || pinname[len - 1] > '9') 156 return -1; 157 158 return pinname[len - 1] - '0'; 159 } 160 161 static int 162 exynos_do_config(struct exynos_pinctrl_config *pc) 163 { 164 struct exynos_gpio_pin_cfg *gc = &pc->pc_pincfg; 165 const struct exynos_pinctrl_banks *epb = pc->pc_sc->sc_epb; 166 struct exynos_gpio_bank *bank; 167 const char *pins; 168 int pin; 169 170 int pins_len = OF_getproplen(pc->pc_phandle, "samsung,pins"); 171 if (pins_len <= 0) 172 return -1; 173 174 for (pins = fdtbus_get_string(pc->pc_phandle, "samsung,pins"); 175 pins_len > 0; 176 pins_len -= strlen(pins) + 1, pins += strlen(pins) + 1) { 177 bank = exynos_gpio_bank_lookup(epb, pins); 178 pin = exynos_parse_pin(pins); 179 if (bank == NULL) { 180 aprint_error_dev(pc->pc_sc->sc_dev, 181 "unknown pin name '%s'\n", pins); 182 continue; 183 } 184 exynos_gpio_pin_ctl_write(bank, gc, pin); 185 } 186 187 return 0; 188 } 189 190 static int 191 exynos_pinctrl_set_cfg(device_t dev, const void *data, size_t len) 192 { 193 struct exynos_pinctrl_config pc; 194 195 if (len != 4) 196 return -1; 197 198 pc.pc_phandle = fdtbus_get_phandle_from_native(be32dec(data)); 199 pc.pc_sc = device_private(dev); 200 exynos_parse_config(pc.pc_phandle, &pc.pc_pincfg); 201 202 return exynos_do_config(&pc); 203 } 204