fdt_i2c.c revision 1.16 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