1 /* $NetBSD: fdt_pinctrl.c,v 1.4 2017/07/02 15:27:58 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 5 * Copyright (c) 2015 Martin Fouts 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. Redistributions in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in the 15 * documentation and/or other materials provided with the distribution. 16 * 17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <sys/cdefs.h> 31 __KERNEL_RCSID(0, "$NetBSD: fdt_pinctrl.c,v 1.4 2017/07/02 15:27:58 jmcneill Exp $"); 32 33 #include <sys/param.h> 34 #include <sys/bus.h> 35 #include <sys/kmem.h> 36 37 #include <libfdt.h> 38 #include <dev/fdt/fdtvar.h> 39 40 struct fdtbus_pinctrl_controller { 41 device_t pc_dev; 42 int pc_phandle; 43 const struct fdtbus_pinctrl_controller_func *pc_funcs; 44 45 struct fdtbus_pinctrl_controller *pc_next; 46 }; 47 48 static struct fdtbus_pinctrl_controller *fdtbus_pc = NULL; 49 50 int 51 fdtbus_register_pinctrl_config(device_t dev, int phandle, 52 const struct fdtbus_pinctrl_controller_func *funcs) 53 { 54 struct fdtbus_pinctrl_controller *pc; 55 56 pc = kmem_alloc(sizeof(*pc), KM_SLEEP); 57 pc->pc_dev = dev; 58 pc->pc_phandle = phandle; 59 pc->pc_funcs = funcs; 60 61 pc->pc_next = fdtbus_pc; 62 fdtbus_pc = pc; 63 64 return 0; 65 } 66 67 static struct fdtbus_pinctrl_controller * 68 fdtbus_pinctrl_lookup(int phandle) 69 { 70 struct fdtbus_pinctrl_controller *pc; 71 72 for (pc = fdtbus_pc; pc; pc = pc->pc_next) 73 if (pc->pc_phandle == phandle) 74 return pc; 75 76 return NULL; 77 } 78 79 int 80 fdtbus_pinctrl_set_config_index(int phandle, u_int index) 81 { 82 struct fdtbus_pinctrl_controller *pc; 83 const u_int *pinctrl_data; 84 char buf[16]; 85 u_int xref, pinctrl_cells; 86 int len, error; 87 88 snprintf(buf, sizeof(buf), "pinctrl-%u", index); 89 90 pinctrl_data = fdtbus_get_prop(phandle, buf, &len); 91 if (pinctrl_data == NULL) 92 return ENOENT; 93 94 while (len > 0) { 95 xref = fdtbus_get_phandle_from_native(be32toh(pinctrl_data[0])); 96 pc = fdtbus_pinctrl_lookup(xref); 97 if (pc == NULL) 98 return ENXIO; 99 100 if (of_getprop_uint32(OF_parent(xref), "#pinctrl-cells", &pinctrl_cells) != 0) 101 pinctrl_cells = 1; 102 103 error = pc->pc_funcs->set_config(pc->pc_dev, pinctrl_data, pinctrl_cells * 4); 104 if (error != 0) 105 return error; 106 107 pinctrl_data += pinctrl_cells; 108 len -= (pinctrl_cells * 4); 109 } 110 111 return 0; 112 } 113 114 int 115 fdtbus_pinctrl_set_config(int phandle, const char *cfgname) 116 { 117 const char *pinctrl_names, *name; 118 int len, index; 119 120 if ((len = OF_getproplen(phandle, "pinctrl-names")) < 0) 121 return -1; 122 123 pinctrl_names = fdtbus_get_string(phandle, "pinctrl-names"); 124 125 for (name = pinctrl_names, index = 0; len > 0; 126 name += strlen(name) + 1, index++) { 127 if (strcmp(name, cfgname) == 0) 128 return fdtbus_pinctrl_set_config_index(phandle, index); 129 } 130 131 /* Not found */ 132 return -1; 133 } 134 135 static void 136 fdtbus_pinctrl_configure_node(int phandle) 137 { 138 char buf[256]; 139 int child, error; 140 141 for (child = OF_child(phandle); child; child = OF_peer(child)) { 142 if (!fdtbus_status_okay(child)) 143 continue; 144 145 /* Configure child nodes */ 146 fdtbus_pinctrl_configure_node(child); 147 148 /* 149 * Set configuration 0 for this node. This may fail if the 150 * pinctrl provider is missing; that's OK, we will re-configure 151 * when that provider attaches. 152 */ 153 fdtbus_get_path(child, buf, sizeof(buf)); 154 error = fdtbus_pinctrl_set_config_index(child, 0); 155 if (error == 0) 156 aprint_debug("pinctrl: set config pinctrl-0 for %s\n", buf); 157 else if (error != ENOENT) 158 aprint_debug("pinctrl: failed to set config pinctrl-0 for %s: %d\n", buf, error); 159 } 160 } 161 162 void 163 fdtbus_pinctrl_configure(void) 164 { 165 fdtbus_pinctrl_configure_node(OF_finddevice("/")); 166 } 167