1 /* $NetBSD: fdt_i2c.c,v 1.16 2025/09/23 00:52:14 thorpej Exp $ */ 2 3 /* 4 * Copyright (c) 2021, 2025 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 /*- 30 * Copyright (c) 2015 Jared D. McNeill <jmcneill (at) invisible.ca> 31 * All rights reserved. 32 * 33 * Redistribution and use in source and binary forms, with or without 34 * modification, are permitted provided that the following conditions 35 * are met: 36 * 1. Redistributions of source code must retain the above copyright 37 * notice, this list of conditions and the following disclaimer. 38 * 2. Redistributions in binary form must reproduce the above copyright 39 * notice, this list of conditions and the following disclaimer in the 40 * documentation and/or other materials provided with the distribution. 41 * 42 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 43 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 44 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 45 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 46 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 47 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 48 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 49 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 50 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 51 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 52 * SUCH DAMAGE. 53 */ 54 55 #include <sys/cdefs.h> 56 __KERNEL_RCSID(0, "$NetBSD: fdt_i2c.c,v 1.16 2025/09/23 00:52:14 thorpej Exp $"); 57 58 #include <sys/param.h> 59 #include <sys/bus.h> 60 #include <sys/kmem.h> 61 #include <sys/queue.h> 62 #include <sys/systm.h> 63 64 #include <libfdt.h> 65 #include <dev/fdt/fdtvar.h> 66 67 #include <dev/i2c/i2cvar.h> 68 #include <dev/i2c/i2c_calls.h> 69 #include <dev/i2c/i2c_enum.h> 70 71 struct fdtbus_i2c_controller { 72 i2c_tag_t i2c_tag; 73 int i2c_phandle; 74 75 LIST_ENTRY(fdtbus_i2c_controller) i2c_next; 76 }; 77 78 static LIST_HEAD(, fdtbus_i2c_controller) fdtbus_i2c_controllers = 79 LIST_HEAD_INITIALIZER(fdtbus_i2c_controllers); 80 81 void 82 fdtbus_register_i2c_controller(device_t dev, i2c_tag_t tag) 83 { 84 int phandle = devhandle_to_of(device_handle(dev)); 85 struct fdtbus_i2c_controller *i2c; 86 87 i2c = kmem_alloc(sizeof(*i2c), KM_SLEEP); 88 i2c->i2c_tag = tag; 89 i2c->i2c_phandle = phandle; 90 91 LIST_INSERT_HEAD(&fdtbus_i2c_controllers, i2c, i2c_next); 92 } 93 94 static struct fdtbus_i2c_controller * 95 fdtbus_get_i2c_controller(int phandle) 96 { 97 struct fdtbus_i2c_controller *i2c; 98 99 LIST_FOREACH(i2c, &fdtbus_i2c_controllers, i2c_next) { 100 if (i2c->i2c_phandle == phandle) 101 return i2c; 102 } 103 104 return NULL; 105 } 106 107 i2c_tag_t 108 fdtbus_i2c_get_tag(int phandle) 109 { 110 struct fdtbus_i2c_controller *i2c; 111 112 i2c = fdtbus_get_i2c_controller(phandle); 113 if (i2c == NULL) 114 return NULL; 115 116 return i2c->i2c_tag; 117 } 118 119 i2c_tag_t 120 fdtbus_i2c_acquire(int phandle, const char *prop) 121 { 122 int i2c_phandle; 123 124 i2c_phandle = fdtbus_get_phandle(phandle, prop); 125 if (i2c_phandle == -1) 126 return NULL; 127 128 return fdtbus_i2c_get_tag(i2c_phandle); 129 } 130 131 static int 132 fdtbus_i2c_enumerate_devices(device_t dev, devhandle_t call_handle, void *v) 133 { 134 struct i2c_enumerate_devices_args *args = v; 135 int i2c_node, node; 136 char name[32], compat_buf[32]; 137 bus_addr_t addr; 138 char *clist; 139 int clist_size; 140 bool cbrv; 141 142 i2c_node = devhandle_to_of(call_handle); 143 144 /* 145 * The Device Tree bindings state that if a controller has a 146 * child node named "i2c-bus", then that is the node beneath 147 * which the child devices are populated. 148 */ 149 for (node = OF_child(i2c_node); node != 0; node = OF_peer(node)) { 150 if (OF_getprop(node, "name", name, sizeof(name)) <= 0) { 151 continue; 152 } 153 if (strcmp(name, "i2c-bus") == 0) { 154 i2c_node = node; 155 break; 156 } 157 } 158 159 for (node = OF_child(i2c_node); node != 0; node = OF_peer(node)) { 160 if (OF_getprop(node, "name", name, sizeof(name)) <= 0) { 161 continue; 162 } 163 if (fdtbus_get_reg(node, 0, &addr, NULL) != 0) { 164 continue; 165 } 166 167 clist_size = OF_getproplen(node, "compatible"); 168 if (clist_size <= 0) { 169 continue; 170 } 171 clist = kmem_tmpbuf_alloc(clist_size, 172 compat_buf, sizeof(compat_buf), KM_SLEEP); 173 if (OF_getprop(node, "compatible", clist, clist_size) 174 < clist_size) { 175 kmem_tmpbuf_free(clist, clist_size, compat_buf); 176 continue; 177 } 178 179 cbrv = i2c_enumerate_device(dev, args, name, clist, 180 clist_size, (i2c_addr_t)addr, 181 devhandle_from_of(call_handle, node)); 182 183 kmem_tmpbuf_free(clist, clist_size, compat_buf); 184 185 if (!cbrv) { 186 break; 187 } 188 } 189 190 return 0; 191 } 192 OF_DEVICE_CALL_REGISTER(I2C_ENUMERATE_DEVICES_STR, 193 fdtbus_i2c_enumerate_devices); 194