/* $NetBSD: fdt_pinctrl.c,v 1.3.8.1 2017/07/18 19:13:09 snj Exp $ */ /*- * 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.3.8.1 2017/07/18 19:13:09 snj Exp $"); #include #include #include #include #include struct fdtbus_pinctrl_controller { device_t pc_dev; int pc_phandle; const struct fdtbus_pinctrl_controller_func *pc_funcs; struct fdtbus_pinctrl_controller *pc_next; }; static struct fdtbus_pinctrl_controller *fdtbus_pc = NULL; 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; pc->pc_next = fdtbus_pc; fdtbus_pc = pc; return 0; } static struct fdtbus_pinctrl_controller * fdtbus_pinctrl_lookup(int phandle) { struct fdtbus_pinctrl_controller *pc; for (pc = fdtbus_pc; pc; pc = pc->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) { const char *pinctrl_names, *name; int len, index; if ((len = OF_getproplen(phandle, "pinctrl-names")) < 0) return -1; pinctrl_names = fdtbus_get_string(phandle, "pinctrl-names"); for (name = pinctrl_names, index = 0; len > 0; name += strlen(name) + 1, index++) { if (strcmp(name, cfgname) == 0) return fdtbus_pinctrl_set_config_index(phandle, index); } /* Not found */ return -1; } static void fdtbus_pinctrl_configure_node(int phandle) { char buf[256]; int child, error; for (child = OF_child(phandle); child; child = OF_peer(child)) { if (!fdtbus_status_okay(child)) continue; /* Configure child nodes */ fdtbus_pinctrl_configure_node(child); /* * Set configuration 0 for this node. This may fail if the * pinctrl provider is missing; that's OK, we will re-configure * when that provider attaches. */ fdtbus_get_path(child, buf, sizeof(buf)); error = fdtbus_pinctrl_set_config_index(child, 0); if (error == 0) aprint_debug("pinctrl: set config pinctrl-0 for %s\n", buf); else if (error != ENOENT) aprint_debug("pinctrl: failed to set config pinctrl-0 for %s: %d\n", buf, error); } } void fdtbus_pinctrl_configure(void) { fdtbus_pinctrl_configure_node(OF_finddevice("/")); }