i2cmux.c revision 1.12 1 /* $NetBSD: i2cmux.c,v 1.12 2025/09/16 13:09:13 thorpej Exp $ */
2
3 /*-
4 * Copyright (c) 2020 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jason R. Thorpe.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #if defined(__i386__) || defined(__amd64__) || defined(__aarch64__)
33 #include "acpica.h"
34 #endif
35
36 #include <sys/cdefs.h>
37 __KERNEL_RCSID(0, "$NetBSD: i2cmux.c,v 1.12 2025/09/16 13:09:13 thorpej Exp $");
38
39 #include <sys/types.h>
40 #include <sys/device.h>
41 #include <sys/kernel.h>
42 #include <sys/kmem.h>
43
44 #include <dev/fdt/fdtvar.h>
45 #include <dev/i2c/i2cvar.h>
46 #include <dev/i2c/i2cmuxvar.h>
47
48 #if NACPICA > 0
49 #include <dev/acpi/acpivar.h>
50 #endif
51
52 /*
53 * i2c mux
54 *
55 * This works by interposing a set of virtual controllers behind the real
56 * i2c controller. We provide our own acquire and release functions that
57 * perform the following tasks:
58 *
59 * acquire -> acquire parent controller, program mux
60 *
61 * release -> idle mux, release parent controller
62 *
63 * All of the actual I/O operations are transparently passed through.
64 *
65 * N.B. the locking order; the generic I2C layer has already acquired
66 * our virtual controller's mutex before calling our acquire function,
67 * and we will then acquire the real controller's mutex when we acquire
68 * the bus, so the order is:
69 *
70 * mux virtual controller -> parent controller
71 *
72 * These are common routines used by various i2c mux controller
73 * implementations (gpio, pin mux, i2c device, etc.).
74 */
75
76 /*****************************************************************************/
77
78 static int
79 iicmux_acquire_bus(void * const v, int const flags)
80 {
81 struct iicmux_bus * const bus = v;
82 struct iicmux_softc * const sc = bus->mux;
83 int error;
84
85 error = iic_acquire_bus(sc->sc_i2c_parent, flags);
86 if (error) {
87 return error;
88 }
89
90 error = sc->sc_config->acquire_bus(bus, flags);
91 if (error) {
92 iic_release_bus(sc->sc_i2c_parent, flags);
93 }
94
95 return error;
96 }
97
98 static void
99 iicmux_release_bus(void * const v, int const flags)
100 {
101 struct iicmux_bus * const bus = v;
102 struct iicmux_softc * const sc = bus->mux;
103
104 sc->sc_config->release_bus(bus, flags);
105 iic_release_bus(sc->sc_i2c_parent, flags);
106 }
107
108 static int
109 iicmux_exec(void * const v, i2c_op_t const op, i2c_addr_t const addr,
110 const void * const cmdbuf, size_t const cmdlen, void * const databuf,
111 size_t const datalen, int const flags)
112 {
113 struct iicmux_bus * const bus = v;
114 struct iicmux_softc * const sc = bus->mux;
115
116 return iic_exec(sc->sc_i2c_parent, op, addr, cmdbuf, cmdlen,
117 databuf, datalen, flags);
118 }
119
120 /*****************************************************************************/
121
122 static int
123 iicmux_count_children(struct iicmux_softc * const sc)
124 {
125 char name[32];
126 int child, count;
127 int mux_phandle = devhandle_to_of(sc->sc_i2c_mux_devhandle);
128
129 restart:
130 for (child = OF_child(mux_phandle), count = 0; child;
131 child = OF_peer(child)) {
132 if (OF_getprop(child, "name", name, sizeof(name)) <= 0) {
133 continue;
134 }
135 if (strcmp(name, "i2c-mux") == 0) {
136 /*
137 * The node we encountered is the actual parent
138 * of the i2c bus children. Stash its phandle
139 * and restart the enumeration.
140 */
141 mux_phandle = child;
142 goto restart;
143 }
144 count++;
145 }
146 sc->sc_i2c_mux_devhandle =
147 devhandle_from_of(sc->sc_i2c_mux_devhandle, mux_phandle);
148
149 return count;
150 }
151
152 #if 0
153 /* XXX iicbus_print() should be able to do this. */
154 static int
155 iicmux_print(void * const aux, const char * const pnp)
156 {
157 i2c_tag_t const tag = aux;
158 struct iicmux_bus * const bus = tag->ic_cookie;
159 int rv;
160
161 rv = iicbus_print(aux, pnp);
162 aprint_normal(" bus %d", bus->busidx);
163
164 return rv;
165 }
166 #endif
167
168 static void
169 iicmux_attach_bus(struct iicmux_softc * const sc, devhandle_t bus_devhandle,
170 int const busidx)
171 {
172 struct iicmux_bus * const bus = &sc->sc_busses[busidx];
173
174 bus->mux = sc;
175 bus->busidx = busidx;
176 bus->devhandle = bus_devhandle;
177
178 bus->bus_data = sc->sc_config->get_bus_info(bus);
179 if (bus->bus_data == NULL) {
180 aprint_error_dev(sc->sc_dev,
181 "unable to get info for bus %d\n", busidx);
182 return;
183 }
184
185 iic_tag_init(&bus->controller);
186 bus->controller.ic_cookie = bus;
187 bus->controller.ic_acquire_bus = iicmux_acquire_bus;
188 bus->controller.ic_release_bus = iicmux_release_bus;
189 bus->controller.ic_exec = iicmux_exec;
190
191 iicbus_attach_with_devhandle(sc->sc_dev, &bus->controller,
192 bus->devhandle);
193 }
194
195 static void
196 iicmux_attach_fdt(struct iicmux_softc * const sc)
197 {
198
199 sc->sc_nbusses = iicmux_count_children(sc);
200 if (sc->sc_nbusses == 0) {
201 return;
202 }
203
204 sc->sc_busses = kmem_zalloc(sizeof(*sc->sc_busses) * sc->sc_nbusses,
205 KM_SLEEP);
206
207 #define BUS_DEVHANDLE(sc, c) \
208 devhandle_from_of((sc)->sc_i2c_mux_devhandle, (c))
209
210 const int mux_phandle = devhandle_to_of(sc->sc_i2c_mux_devhandle);
211 int child, idx;
212 for (child = OF_child(mux_phandle), idx = 0; child;
213 child = OF_peer(child), idx++) {
214 KASSERT(idx < sc->sc_nbusses);
215 iicmux_attach_bus(sc, BUS_DEVHANDLE(sc, child), idx);
216 }
217 #undef BUS_DEVHANDLE
218 }
219
220 #if NACPICA > 0
221 static void
222 iicmux_attach_acpi(struct iicmux_softc * const sc)
223 {
224 ACPI_HANDLE hdl = devhandle_to_acpi(sc->sc_i2c_mux_devhandle);
225 struct acpi_devnode *devnode, *ad;
226 int idx;
227
228 devnode = acpi_match_node(hdl);
229 KASSERT(devnode != NULL);
230
231 /* Count child busses */
232 sc->sc_nbusses = 0;
233 SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
234 if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE ||
235 !acpi_device_present(ad->ad_handle)) {
236 continue;
237 }
238 sc->sc_nbusses++;
239 }
240
241 sc->sc_busses = kmem_zalloc(sizeof(*sc->sc_busses) * sc->sc_nbusses,
242 KM_SLEEP);
243
244 #define BUS_DEVHANDLE(sc, c) \
245 devhandle_from_acpi((sc)->sc_i2c_mux_devhandle, (c))
246
247 /* Attach child busses */
248 idx = 0;
249 SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
250 if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE ||
251 !acpi_device_present(ad->ad_handle)) {
252 continue;
253 }
254 iicmux_attach_bus(sc, BUS_DEVHANDLE(sc, ad->ad_handle), idx);
255 idx++;
256 }
257 #undef BUS_DEVHANDLE
258 }
259 #endif /* NACPICA > 0 */
260
261 void
262 iicmux_attach(struct iicmux_softc * const sc)
263 {
264 /*
265 * We expect sc->sc_config and sc->sc_i2c_parent to be initialized
266 * by the front-end.
267 */
268 KASSERT(sc->sc_config != NULL);
269 KASSERT(sc->sc_i2c_parent != NULL);
270
271 /*
272 * We start out assuming that the i2c bus nodes are children of
273 * our own node. We'll adjust later if we encounter an "i2c-mux"
274 * node when counting our children. If we encounter such a node,
275 * then it's that node that is the parent of the i2c bus children.
276 */
277 sc->sc_i2c_mux_devhandle = device_handle(sc->sc_dev);
278
279 /*
280 * Gather up all of the various bits of information needed
281 * for this particular type of i2c mux.
282 */
283 sc->sc_mux_data = sc->sc_config->get_mux_info(sc);
284 if (sc->sc_mux_data == NULL) {
285 aprint_error_dev(sc->sc_dev, "unable to get info for mux\n");
286 return;
287 }
288
289 /*
290 * Do configuration method (OF, ACPI) specific setup.
291 *
292 * XXX We can eliminate 99% of the platform device tree
293 * XXX specific stuff here, but to do that we need a generic
294 * XXX way to enumerate children of an arbitrary devhandle.
295 */
296 switch (devhandle_type(sc->sc_i2c_mux_devhandle)) {
297 case DEVHANDLE_TYPE_OF:
298 iicmux_attach_fdt(sc);
299 break;
300 #if NACPICA > 0
301 case DEVHANDLE_TYPE_ACPI:
302 iicmux_attach_acpi(sc);
303 break;
304 #endif
305 default:
306 aprint_error_dev(sc->sc_dev, "could not configure mux: "
307 "handle type 0x%08x not supported\n",
308 devhandle_type(sc->sc_i2c_mux_devhandle));
309 break;
310 }
311 }
312