fdt_pinctrl.c revision 1.8
1/* $NetBSD: fdt_pinctrl.c,v 1.8 2019/02/27 16:56:00 jakllsch 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.8 2019/02/27 16:56:00 jakllsch 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 -1; 127 128 return fdtbus_pinctrl_set_config_index(phandle, index); 129} 130 131static void 132fdtbus_pinctrl_configure_node(int phandle) 133{ 134 char buf[256]; 135 int child, error; 136 137 for (child = OF_child(phandle); child; child = OF_peer(child)) { 138 if (!fdtbus_status_okay(child)) 139 continue; 140 141 /* Configure child nodes */ 142 fdtbus_pinctrl_configure_node(child); 143 144 /* 145 * Set configuration 0 for this node. This may fail if the 146 * pinctrl provider is missing; that's OK, we will re-configure 147 * when that provider attaches. 148 */ 149 fdtbus_get_path(child, buf, sizeof(buf)); 150 error = fdtbus_pinctrl_set_config_index(child, 0); 151 if (error == 0) 152 aprint_debug("pinctrl: set config pinctrl-0 for %s\n", buf); 153 else if (error != ENOENT) 154 aprint_debug("pinctrl: failed to set config pinctrl-0 for %s: %d\n", buf, error); 155 } 156} 157 158void 159fdtbus_pinctrl_configure(void) 160{ 161 fdtbus_pinctrl_configure_node(OF_finddevice("/")); 162} 163 164/* 165 * Helper routines for parsing put properties related to pinctrl bindings. 166 */ 167 168/* 169 * Pin mux settings apply to sets of pins specified by one of 3 170 * sets of properties: 171 * 172 * - "pins" + "function" 173 * - "groups" + "function" 174 * - "pinmux" 175 * 176 * Eactly one of those 3 combinations must be specified. 177 */ 178 179const char * 180fdtbus_pinctrl_parse_function(int phandle) 181{ 182 return fdtbus_get_string(phandle, "function"); 183} 184 185const void * 186fdtbus_pinctrl_parse_pins(int phandle, int *pins_len) 187{ 188 int len; 189 190 /* 191 * The pinctrl bindings specify that entries in "pins" 192 * may be integers or strings; this is determined by 193 * the hardware-specific binding. 194 */ 195 196 len = OF_getproplen(phandle, "pins"); 197 if (len > 0) { 198 return fdtbus_get_prop(phandle, "pins", pins_len); 199 } 200 201 return NULL; 202} 203 204const char * 205fdtbus_pinctrl_parse_groups(int phandle, int *groups_len) 206{ 207 int len; 208 209 len = OF_getproplen(phandle, "groups"); 210 if (len > 0) { 211 *groups_len = len; 212 return fdtbus_get_string(phandle, "groups"); 213 } 214 215 return NULL; 216} 217 218const u_int * 219fdtbus_pinctrl_parse_pinmux(int phandle, int *pinmux_len) 220{ 221 int len; 222 223 len = OF_getproplen(phandle, "pinmux"); 224 if (len > 0) { 225 return fdtbus_get_prop(phandle, "pinmux", pinmux_len); 226 } 227 228 return NULL; 229} 230 231int 232fdtbus_pinctrl_parse_bias(int phandle, int *pull_strength) 233{ 234 const char *bias_prop = NULL; 235 int bias = -1; 236 237 /* 238 * bias-pull-{up,down,pin-default} properties have an optional 239 * argument: the pull strength in Ohms. (In practice, this is 240 * sometimes a hardware-specific constant.) 241 * 242 * XXXJRT How to represent bias-pull-pin-default? 243 */ 244 245 if (of_hasprop(phandle, "bias-disable")) { 246 bias = 0; 247 } else if (of_hasprop(phandle, "bias-pull-up")) { 248 bias_prop = "bias-pull-up"; 249 bias = GPIO_PIN_PULLUP; 250 } else if (of_hasprop(phandle, "bias-pull-down")) { 251 bias_prop = "bias-pull-down"; 252 bias = GPIO_PIN_PULLDOWN; 253 } 254 255 if (pull_strength) { 256 *pull_strength = -1; 257 if (bias_prop) { 258 uint32_t val; 259 if (of_getprop_uint32(phandle, bias_prop, &val) == 0) { 260 *pull_strength = (int)val; 261 } 262 } 263 } 264 265 return bias; 266} 267 268int 269fdtbus_pinctrl_parse_drive(int phandle) 270{ 271 int drive = -1; 272 273 if (of_hasprop(phandle, "drive-push-pull")) 274 drive = GPIO_PIN_PUSHPULL; 275 else if (of_hasprop(phandle, "drive-open-drain")) 276 drive = GPIO_PIN_OPENDRAIN; 277 else if (of_hasprop(phandle, "drive-open-source")) 278 drive = 0; 279 280 return drive; 281} 282 283int 284fdtbus_pinctrl_parse_drive_strength(int phandle) 285{ 286 int val; 287 288 /* 289 * drive-strength has as an argument the target strength 290 * in mA. 291 */ 292 293 if (of_getprop_uint32(phandle, "drive-strength", &val) == 0) 294 return val; 295 296 return -1; 297} 298int fdtbus_pinctrl_parse_input_output(int phandle, int *output_value) 299{ 300 int direction = -1; 301 int pinval = -1; 302 303 if (of_hasprop(phandle, "input-enable")) { 304 direction = GPIO_PIN_INPUT; 305 } else if (of_hasprop(phandle, "input-disable")) { 306 /* 307 * XXXJRT How to represent this? This is more than 308 * just "don't set the direction" - it's an active 309 * command that might involve disabling an input 310 * buffer on the pin. 311 */ 312 } 313 314 if (of_hasprop(phandle, "output-enable")) { 315 if (direction == -1) 316 direction = 0; 317 direction |= GPIO_PIN_OUTPUT; 318 } else if (of_hasprop(phandle, "output-disable")) { 319 if (direction == -1) 320 direction = 0; 321 direction |= GPIO_PIN_TRISTATE; 322 } 323 324 if (of_hasprop(phandle, "output-low")) { 325 if (direction == -1) 326 direction = 0; 327 direction |= GPIO_PIN_OUTPUT; 328 pinval = GPIO_PIN_LOW; 329 } else if (of_hasprop(phandle, "output-high")) { 330 if (direction == -1) 331 direction = 0; 332 direction |= GPIO_PIN_OUTPUT; 333 pinval = GPIO_PIN_HIGH; 334 } 335 336 if (output_value) 337 *output_value = pinval; 338 339 /* 340 * XXX input-schmitt-enable 341 * XXX input-schmitt-disable 342 */ 343 344 if (direction != -1 345 && (direction & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) 346 == (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { 347 direction |= GPIO_PIN_INOUT; 348 } 349 350 return direction; 351} 352