1 /* $NetBSD: fdt_pinctrl.c,v 1.5 2018/06/30 20:34:43 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.5 2018/06/30 20:34:43 jmcneill Exp $"); 32 33 #include <sys/param.h> 34 #include <sys/bus.h> 35 #include <sys/kmem.h> 36 #include <sys/queue.h> 37 38 #include <libfdt.h> 39 #include <dev/fdt/fdtvar.h> 40 41 struct fdtbus_pinctrl_controller { 42 device_t pc_dev; 43 int pc_phandle; 44 const struct fdtbus_pinctrl_controller_func *pc_funcs; 45 46 LIST_ENTRY(fdtbus_pinctrl_controller) pc_next; 47 }; 48 49 static LIST_HEAD(, fdtbus_pinctrl_controller) fdtbus_pinctrl_controllers = 50 LIST_HEAD_INITIALIZER(fdtbus_pinctrl_controllers); 51 52 int 53 fdtbus_register_pinctrl_config(device_t dev, int phandle, 54 const struct fdtbus_pinctrl_controller_func *funcs) 55 { 56 struct fdtbus_pinctrl_controller *pc; 57 58 pc = kmem_alloc(sizeof(*pc), KM_SLEEP); 59 pc->pc_dev = dev; 60 pc->pc_phandle = phandle; 61 pc->pc_funcs = funcs; 62 63 LIST_INSERT_HEAD(&fdtbus_pinctrl_controllers, pc, pc_next); 64 65 return 0; 66 } 67 68 static struct fdtbus_pinctrl_controller * 69 fdtbus_pinctrl_lookup(int phandle) 70 { 71 struct fdtbus_pinctrl_controller *pc; 72 73 LIST_FOREACH(pc, &fdtbus_pinctrl_controllers, pc_next) { 74 if (pc->pc_phandle == phandle) 75 return pc; 76 } 77 78 return NULL; 79 } 80 81 int 82 fdtbus_pinctrl_set_config_index(int phandle, u_int index) 83 { 84 struct fdtbus_pinctrl_controller *pc; 85 const u_int *pinctrl_data; 86 char buf[16]; 87 u_int xref, pinctrl_cells; 88 int len, error; 89 90 snprintf(buf, sizeof(buf), "pinctrl-%u", index); 91 92 pinctrl_data = fdtbus_get_prop(phandle, buf, &len); 93 if (pinctrl_data == NULL) 94 return ENOENT; 95 96 while (len > 0) { 97 xref = fdtbus_get_phandle_from_native(be32toh(pinctrl_data[0])); 98 pc = fdtbus_pinctrl_lookup(xref); 99 if (pc == NULL) 100 return ENXIO; 101 102 if (of_getprop_uint32(OF_parent(xref), "#pinctrl-cells", &pinctrl_cells) != 0) 103 pinctrl_cells = 1; 104 105 error = pc->pc_funcs->set_config(pc->pc_dev, pinctrl_data, pinctrl_cells * 4); 106 if (error != 0) 107 return error; 108 109 pinctrl_data += pinctrl_cells; 110 len -= (pinctrl_cells * 4); 111 } 112 113 return 0; 114 } 115 116 int 117 fdtbus_pinctrl_set_config(int phandle, const char *cfgname) 118 { 119 const char *pinctrl_names, *name; 120 int len, index; 121 122 if ((len = OF_getproplen(phandle, "pinctrl-names")) < 0) 123 return -1; 124 125 pinctrl_names = fdtbus_get_string(phandle, "pinctrl-names"); 126 127 for (name = pinctrl_names, index = 0; len > 0; 128 name += strlen(name) + 1, index++) { 129 if (strcmp(name, cfgname) == 0) 130 return fdtbus_pinctrl_set_config_index(phandle, index); 131 } 132 133 /* Not found */ 134 return -1; 135 } 136 137 static void 138 fdtbus_pinctrl_configure_node(int phandle) 139 { 140 char buf[256]; 141 int child, error; 142 143 for (child = OF_child(phandle); child; child = OF_peer(child)) { 144 if (!fdtbus_status_okay(child)) 145 continue; 146 147 /* Configure child nodes */ 148 fdtbus_pinctrl_configure_node(child); 149 150 /* 151 * Set configuration 0 for this node. This may fail if the 152 * pinctrl provider is missing; that's OK, we will re-configure 153 * when that provider attaches. 154 */ 155 fdtbus_get_path(child, buf, sizeof(buf)); 156 error = fdtbus_pinctrl_set_config_index(child, 0); 157 if (error == 0) 158 aprint_debug("pinctrl: set config pinctrl-0 for %s\n", buf); 159 else if (error != ENOENT) 160 aprint_debug("pinctrl: failed to set config pinctrl-0 for %s: %d\n", buf, error); 161 } 162 } 163 164 void 165 fdtbus_pinctrl_configure(void) 166 { 167 fdtbus_pinctrl_configure_node(OF_finddevice("/")); 168 } 169