1/* $NetBSD: fdt_pinctrl.c,v 1.10 2019/10/01 23:32:52 jmcneill Exp $ */ 2 3/*- 4 * Copyright (c) 2019 Jason R. Thorpe 5 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 6 * Copyright (c) 2015 Martin Fouts 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 23 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 25 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 */ 30 31#include <sys/cdefs.h> 32__KERNEL_RCSID(0, "$NetBSD: fdt_pinctrl.c,v 1.10 2019/10/01 23:32:52 jmcneill Exp $"); 33 34#include <sys/param.h> 35#include <sys/bus.h> 36#include <sys/gpio.h> 37#include <sys/kmem.h> 38#include <sys/queue.h> 39 40#include <libfdt.h> 41#include <dev/fdt/fdtvar.h> 42 43struct fdtbus_pinctrl_controller { 44 device_t pc_dev; 45 int pc_phandle; 46 const struct fdtbus_pinctrl_controller_func *pc_funcs; 47 48 LIST_ENTRY(fdtbus_pinctrl_controller) pc_next; 49}; 50 51static LIST_HEAD(, fdtbus_pinctrl_controller) fdtbus_pinctrl_controllers = 52 LIST_HEAD_INITIALIZER(fdtbus_pinctrl_controllers); 53 54int 55fdtbus_register_pinctrl_config(device_t dev, int phandle, 56 const struct fdtbus_pinctrl_controller_func *funcs) 57{ 58 struct fdtbus_pinctrl_controller *pc; 59 60 pc = kmem_alloc(sizeof(*pc), KM_SLEEP); 61 pc->pc_dev = dev; 62 pc->pc_phandle = phandle; 63 pc->pc_funcs = funcs; 64 65 LIST_INSERT_HEAD(&fdtbus_pinctrl_controllers, pc, pc_next); 66 67 return 0; 68} 69 70static struct fdtbus_pinctrl_controller * 71fdtbus_pinctrl_lookup(int phandle) 72{ 73 struct fdtbus_pinctrl_controller *pc; 74 75 LIST_FOREACH(pc, &fdtbus_pinctrl_controllers, pc_next) { 76 if (pc->pc_phandle == phandle) 77 return pc; 78 } 79 80 return NULL; 81} 82 83int 84fdtbus_pinctrl_set_config_index(int phandle, u_int index) 85{ 86 struct fdtbus_pinctrl_controller *pc; 87 const u_int *pinctrl_data; 88 char buf[16]; 89 u_int xref, pinctrl_cells; 90 int len, error; 91 92 snprintf(buf, sizeof(buf), "pinctrl-%u", index); 93 94 pinctrl_data = fdtbus_get_prop(phandle, buf, &len); 95 if (pinctrl_data == NULL) 96 return ENOENT; 97 98 while (len > 0) { 99 xref = fdtbus_get_phandle_from_native(be32toh(pinctrl_data[0])); 100 pc = fdtbus_pinctrl_lookup(xref); 101 if (pc == NULL) 102 return ENXIO; 103 104 if (of_getprop_uint32(OF_parent(xref), "#pinctrl-cells", &pinctrl_cells) != 0) 105 pinctrl_cells = 1; 106 107 error = pc->pc_funcs->set_config(pc->pc_dev, pinctrl_data, pinctrl_cells * 4); 108 if (error != 0) 109 return error; 110 111 pinctrl_data += pinctrl_cells; 112 len -= (pinctrl_cells * 4); 113 } 114 115 return 0; 116} 117 118int 119fdtbus_pinctrl_set_config(int phandle, const char *cfgname) 120{ 121 u_int index; 122 int err; 123 124 err = fdtbus_get_index(phandle, "pinctrl-names", cfgname, &index); 125 if (err != 0) 126 return ENOENT; 127 128 return fdtbus_pinctrl_set_config_index(phandle, index); 129} 130 131bool 132fdtbus_pinctrl_has_config(int phandle, const char *cfgname) 133{ 134 u_int index; 135 136 return fdtbus_get_index(phandle, "pinctrl-names", cfgname, &index) == 0; 137} 138 139/* 140 * Helper routines for parsing put properties related to pinctrl bindings. 141 */ 142 143/* 144 * Pin mux settings apply to sets of pins specified by one of 3 145 * sets of properties: 146 * 147 * - "pins" + "function" 148 * - "groups" + "function" 149 * - "pinmux" 150 * 151 * Eactly one of those 3 combinations must be specified. 152 */ 153 154const char * 155fdtbus_pinctrl_parse_function(int phandle) 156{ 157 return fdtbus_get_string(phandle, "function"); 158} 159 160const void * 161fdtbus_pinctrl_parse_pins(int phandle, int *pins_len) 162{ 163 int len; 164 165 /* 166 * The pinctrl bindings specify that entries in "pins" 167 * may be integers or strings; this is determined by 168 * the hardware-specific binding. 169 */ 170 171 len = OF_getproplen(phandle, "pins"); 172 if (len > 0) { 173 return fdtbus_get_prop(phandle, "pins", pins_len); 174 } 175 176 return NULL; 177} 178 179const char * 180fdtbus_pinctrl_parse_groups(int phandle, int *groups_len) 181{ 182 int len; 183 184 len = OF_getproplen(phandle, "groups"); 185 if (len > 0) { 186 *groups_len = len; 187 return fdtbus_get_string(phandle, "groups"); 188 } 189 190 return NULL; 191} 192 193const u_int * 194fdtbus_pinctrl_parse_pinmux(int phandle, int *pinmux_len) 195{ 196 int len; 197 198 len = OF_getproplen(phandle, "pinmux"); 199 if (len > 0) { 200 return fdtbus_get_prop(phandle, "pinmux", pinmux_len); 201 } 202 203 return NULL; 204} 205 206int 207fdtbus_pinctrl_parse_bias(int phandle, int *pull_strength) 208{ 209 const char *bias_prop = NULL; 210 int bias = -1; 211 212 /* 213 * bias-pull-{up,down,pin-default} properties have an optional 214 * argument: the pull strength in Ohms. (In practice, this is 215 * sometimes a hardware-specific constant.) 216 * 217 * XXXJRT How to represent bias-pull-pin-default? 218 */ 219 220 if (of_hasprop(phandle, "bias-disable")) { 221 bias = 0; 222 } else if (of_hasprop(phandle, "bias-pull-up")) { 223 bias_prop = "bias-pull-up"; 224 bias = GPIO_PIN_PULLUP; 225 } else if (of_hasprop(phandle, "bias-pull-down")) { 226 bias_prop = "bias-pull-down"; 227 bias = GPIO_PIN_PULLDOWN; 228 } 229 230 if (pull_strength) { 231 *pull_strength = -1; 232 if (bias_prop) { 233 uint32_t val; 234 if (of_getprop_uint32(phandle, bias_prop, &val) == 0) { 235 *pull_strength = (int)val; 236 } 237 } 238 } 239 240 return bias; 241} 242 243int 244fdtbus_pinctrl_parse_drive(int phandle) 245{ 246 int drive = -1; 247 248 if (of_hasprop(phandle, "drive-push-pull")) 249 drive = GPIO_PIN_PUSHPULL; 250 else if (of_hasprop(phandle, "drive-open-drain")) 251 drive = GPIO_PIN_OPENDRAIN; 252 else if (of_hasprop(phandle, "drive-open-source")) 253 drive = 0; 254 255 return drive; 256} 257 258int 259fdtbus_pinctrl_parse_drive_strength(int phandle) 260{ 261 int val; 262 263 /* 264 * drive-strength has as an argument the target strength 265 * in mA. 266 */ 267 268 if (of_getprop_uint32(phandle, "drive-strength", &val) == 0) 269 return val; 270 271 return -1; 272} 273int fdtbus_pinctrl_parse_input_output(int phandle, int *output_value) 274{ 275 int direction = -1; 276 int pinval = -1; 277 278 if (of_hasprop(phandle, "input-enable")) { 279 direction = GPIO_PIN_INPUT; 280 } else if (of_hasprop(phandle, "input-disable")) { 281 /* 282 * XXXJRT How to represent this? This is more than 283 * just "don't set the direction" - it's an active 284 * command that might involve disabling an input 285 * buffer on the pin. 286 */ 287 } 288 289 if (of_hasprop(phandle, "output-enable")) { 290 if (direction == -1) 291 direction = 0; 292 direction |= GPIO_PIN_OUTPUT; 293 } else if (of_hasprop(phandle, "output-disable")) { 294 if (direction == -1) 295 direction = 0; 296 direction |= GPIO_PIN_TRISTATE; 297 } 298 299 if (of_hasprop(phandle, "output-low")) { 300 if (direction == -1) 301 direction = 0; 302 direction |= GPIO_PIN_OUTPUT; 303 pinval = GPIO_PIN_LOW; 304 } else if (of_hasprop(phandle, "output-high")) { 305 if (direction == -1) 306 direction = 0; 307 direction |= GPIO_PIN_OUTPUT; 308 pinval = GPIO_PIN_HIGH; 309 } 310 311 if (output_value) 312 *output_value = pinval; 313 314 /* 315 * XXX input-schmitt-enable 316 * XXX input-schmitt-disable 317 */ 318 319 if (direction != -1 320 && (direction & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) 321 == (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { 322 direction |= GPIO_PIN_INOUT; 323 } 324 325 return direction; 326} 327