/* $NetBSD: fdt_pinctrl.c,v 1.8.4.2 2019/10/03 17:23:11 martin Exp $ */ /*- * Copyright (c) 2019 Jason R. Thorpe * Copyright (c) 2017 Jared McNeill * Copyright (c) 2015 Martin Fouts * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __KERNEL_RCSID(0, "$NetBSD: fdt_pinctrl.c,v 1.8.4.2 2019/10/03 17:23:11 martin Exp $"); #include #include #include #include #include #include #include struct fdtbus_pinctrl_controller { device_t pc_dev; int pc_phandle; const struct fdtbus_pinctrl_controller_func *pc_funcs; LIST_ENTRY(fdtbus_pinctrl_controller) pc_next; }; static LIST_HEAD(, fdtbus_pinctrl_controller) fdtbus_pinctrl_controllers = LIST_HEAD_INITIALIZER(fdtbus_pinctrl_controllers); int fdtbus_register_pinctrl_config(device_t dev, int phandle, const struct fdtbus_pinctrl_controller_func *funcs) { struct fdtbus_pinctrl_controller *pc; pc = kmem_alloc(sizeof(*pc), KM_SLEEP); pc->pc_dev = dev; pc->pc_phandle = phandle; pc->pc_funcs = funcs; LIST_INSERT_HEAD(&fdtbus_pinctrl_controllers, pc, pc_next); return 0; } static struct fdtbus_pinctrl_controller * fdtbus_pinctrl_lookup(int phandle) { struct fdtbus_pinctrl_controller *pc; LIST_FOREACH(pc, &fdtbus_pinctrl_controllers, pc_next) { if (pc->pc_phandle == phandle) return pc; } return NULL; } int fdtbus_pinctrl_set_config_index(int phandle, u_int index) { struct fdtbus_pinctrl_controller *pc; const u_int *pinctrl_data; char buf[16]; u_int xref, pinctrl_cells; int len, error; snprintf(buf, sizeof(buf), "pinctrl-%u", index); pinctrl_data = fdtbus_get_prop(phandle, buf, &len); if (pinctrl_data == NULL) return ENOENT; while (len > 0) { xref = fdtbus_get_phandle_from_native(be32toh(pinctrl_data[0])); pc = fdtbus_pinctrl_lookup(xref); if (pc == NULL) return ENXIO; if (of_getprop_uint32(OF_parent(xref), "#pinctrl-cells", &pinctrl_cells) != 0) pinctrl_cells = 1; error = pc->pc_funcs->set_config(pc->pc_dev, pinctrl_data, pinctrl_cells * 4); if (error != 0) return error; pinctrl_data += pinctrl_cells; len -= (pinctrl_cells * 4); } return 0; } int fdtbus_pinctrl_set_config(int phandle, const char *cfgname) { u_int index; int err; err = fdtbus_get_index(phandle, "pinctrl-names", cfgname, &index); if (err != 0) return ENOENT; return fdtbus_pinctrl_set_config_index(phandle, index); } bool fdtbus_pinctrl_has_config(int phandle, const char *cfgname) { u_int index; return fdtbus_get_index(phandle, "pinctrl-names", cfgname, &index) == 0; } /* * Helper routines for parsing put properties related to pinctrl bindings. */ /* * Pin mux settings apply to sets of pins specified by one of 3 * sets of properties: * * - "pins" + "function" * - "groups" + "function" * - "pinmux" * * Eactly one of those 3 combinations must be specified. */ const char * fdtbus_pinctrl_parse_function(int phandle) { return fdtbus_get_string(phandle, "function"); } const void * fdtbus_pinctrl_parse_pins(int phandle, int *pins_len) { int len; /* * The pinctrl bindings specify that entries in "pins" * may be integers or strings; this is determined by * the hardware-specific binding. */ len = OF_getproplen(phandle, "pins"); if (len > 0) { return fdtbus_get_prop(phandle, "pins", pins_len); } return NULL; } const char * fdtbus_pinctrl_parse_groups(int phandle, int *groups_len) { int len; len = OF_getproplen(phandle, "groups"); if (len > 0) { *groups_len = len; return fdtbus_get_string(phandle, "groups"); } return NULL; } const u_int * fdtbus_pinctrl_parse_pinmux(int phandle, int *pinmux_len) { int len; len = OF_getproplen(phandle, "pinmux"); if (len > 0) { return fdtbus_get_prop(phandle, "pinmux", pinmux_len); } return NULL; } int fdtbus_pinctrl_parse_bias(int phandle, int *pull_strength) { const char *bias_prop = NULL; int bias = -1; /* * bias-pull-{up,down,pin-default} properties have an optional * argument: the pull strength in Ohms. (In practice, this is * sometimes a hardware-specific constant.) * * XXXJRT How to represent bias-pull-pin-default? */ if (of_hasprop(phandle, "bias-disable")) { bias = 0; } else if (of_hasprop(phandle, "bias-pull-up")) { bias_prop = "bias-pull-up"; bias = GPIO_PIN_PULLUP; } else if (of_hasprop(phandle, "bias-pull-down")) { bias_prop = "bias-pull-down"; bias = GPIO_PIN_PULLDOWN; } if (pull_strength) { *pull_strength = -1; if (bias_prop) { uint32_t val; if (of_getprop_uint32(phandle, bias_prop, &val) == 0) { *pull_strength = (int)val; } } } return bias; } int fdtbus_pinctrl_parse_drive(int phandle) { int drive = -1; if (of_hasprop(phandle, "drive-push-pull")) drive = GPIO_PIN_PUSHPULL; else if (of_hasprop(phandle, "drive-open-drain")) drive = GPIO_PIN_OPENDRAIN; else if (of_hasprop(phandle, "drive-open-source")) drive = 0; return drive; } int fdtbus_pinctrl_parse_drive_strength(int phandle) { int val; /* * drive-strength has as an argument the target strength * in mA. */ if (of_getprop_uint32(phandle, "drive-strength", &val) == 0) return val; return -1; } int fdtbus_pinctrl_parse_input_output(int phandle, int *output_value) { int direction = -1; int pinval = -1; if (of_hasprop(phandle, "input-enable")) { direction = GPIO_PIN_INPUT; } else if (of_hasprop(phandle, "input-disable")) { /* * XXXJRT How to represent this? This is more than * just "don't set the direction" - it's an active * command that might involve disabling an input * buffer on the pin. */ } if (of_hasprop(phandle, "output-enable")) { if (direction == -1) direction = 0; direction |= GPIO_PIN_OUTPUT; } else if (of_hasprop(phandle, "output-disable")) { if (direction == -1) direction = 0; direction |= GPIO_PIN_TRISTATE; } if (of_hasprop(phandle, "output-low")) { if (direction == -1) direction = 0; direction |= GPIO_PIN_OUTPUT; pinval = GPIO_PIN_LOW; } else if (of_hasprop(phandle, "output-high")) { if (direction == -1) direction = 0; direction |= GPIO_PIN_OUTPUT; pinval = GPIO_PIN_HIGH; } if (output_value) *output_value = pinval; /* * XXX input-schmitt-enable * XXX input-schmitt-disable */ if (direction != -1 && (direction & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) == (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { direction |= GPIO_PIN_INOUT; } return direction; }