i2cmux.c revision 1.1.2.2 1 /* $NetBSD: i2cmux.c,v 1.1.2.2 2021/01/03 16:34:58 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 #include <sys/cdefs.h>
33 __KERNEL_RCSID(0, "$NetBSD: i2cmux.c,v 1.1.2.2 2021/01/03 16:34:58 thorpej Exp $");
34
35 #include <sys/types.h>
36 #include <sys/device.h>
37 #include <sys/kernel.h>
38 #include <sys/kmem.h>
39
40 #include <dev/fdt/fdtvar.h>
41 #include <dev/i2c/i2cvar.h>
42 #include <dev/i2c/i2cmuxvar.h>
43
44 /*
45 * i2c mux
46 *
47 * This works by interposing a set of virtual controllers behind the real
48 * i2c controller. We provide our own acquire and release functions that
49 * perform the following tasks:
50 *
51 * acquire -> acquire parent controller, program mux
52 *
53 * release -> idle mux, release parent controller
54 *
55 * All of the actual I/O operations are transparently passed through.
56 *
57 * N.B. the locking order; the generic I2C layer has already acquired
58 * our virtual controller's mutex before calling our acquire function,
59 * and we will then acquire the real controller's mutex when we acquire
60 * the bus, so the order is:
61 *
62 * mux virtual controller -> parent controller
63 *
64 * These are common routines used by various i2c mux controller
65 * implementations (gpio, pin mux, i2c device, etc.).
66 */
67
68 /*****************************************************************************/
69
70 static int
71 iicmux_acquire_bus(void * const v, int const flags)
72 {
73 struct iicmux_bus * const bus = v;
74 struct iicmux_softc * const sc = bus->mux;
75 int error;
76
77 error = iic_acquire_bus(sc->sc_i2c_parent, flags);
78 if (error) {
79 return error;
80 }
81
82 error = sc->sc_config->acquire_bus(bus, flags);
83 if (error) {
84 iic_release_bus(sc->sc_i2c_parent, flags);
85 }
86
87 return error;
88 }
89
90 static void
91 iicmux_release_bus(void * const v, int const flags)
92 {
93 struct iicmux_bus * const bus = v;
94 struct iicmux_softc * const sc = bus->mux;
95
96 sc->sc_config->release_bus(bus, flags);
97 iic_release_bus(sc->sc_i2c_parent, flags);
98 }
99
100 static int
101 iicmux_exec(void * const v, i2c_op_t const op, i2c_addr_t const addr,
102 const void * const cmdbuf, size_t const cmdlen, void * const databuf,
103 size_t const datalen, int const flags)
104 {
105 struct iicmux_bus * const bus = v;
106 struct iicmux_softc * const sc = bus->mux;
107
108 return iic_exec(sc->sc_i2c_parent, op, addr, cmdbuf, cmdlen,
109 databuf, datalen, flags);
110 }
111
112 /*****************************************************************************/
113
114 static int
115 iicmux_count_children(struct iicmux_softc * const sc)
116 {
117 char name[32];
118 int child, count;
119
120 restart:
121 for (child = OF_child(sc->sc_i2c_mux_phandle), count = 0; child;
122 child = OF_peer(child)) {
123 if (OF_getprop(child, "name", name, sizeof(name)) <= 0) {
124 continue;
125 }
126 if (strcmp(name, "i2c-mux") == 0) {
127 /*
128 * The node we encountered is the acutal parent
129 * of the i2c bus children. Stash its phandle
130 * and restart the enumeration.
131 */
132 sc->sc_i2c_mux_phandle = child;
133 goto restart;
134 }
135 count++;
136 }
137
138 return count;
139 }
140
141 /* XXX iicbus_print() should be able to do this. */
142 static int
143 iicmux_print(void * const aux, const char * const pnp)
144 {
145 i2c_tag_t const tag = aux;
146 struct iicmux_bus * const bus = tag->ic_cookie;
147 int rv;
148
149 rv = iicbus_print(aux, pnp);
150 aprint_normal(" bus %d", bus->busidx);
151
152 return rv;
153 }
154
155 static void
156 iicmux_attach_bus(struct iicmux_softc * const sc,
157 int const phandle, int const busidx)
158 {
159 struct iicmux_bus * const bus = &sc->sc_busses[busidx];
160
161 bus->mux = sc;
162 bus->busidx = busidx;
163 bus->phandle = phandle;
164
165 bus->bus_data = sc->sc_config->get_bus_info(bus);
166 if (bus->bus_data == NULL) {
167 aprint_error_dev(sc->sc_dev,
168 "unable to get info for bus %d\n", busidx);
169 return;
170 }
171
172 iic_tag_init(&bus->controller);
173 bus->controller.ic_cookie = bus;
174 bus->controller.ic_acquire_bus = iicmux_acquire_bus;
175 bus->controller.ic_release_bus = iicmux_release_bus;
176 bus->controller.ic_exec = iicmux_exec;
177
178 fdtbus_register_i2c_controller(&bus->controller, bus->phandle);
179
180 fdtbus_attach_i2cbus(sc->sc_dev, bus->phandle, &bus->controller,
181 iicmux_print);
182 }
183
184 void
185 iicmux_attach(struct iicmux_softc * const sc)
186 {
187
188 /*
189 * We expect sc->sc_phandle, sc->sc_config, and sc->sc_i2c_parent
190 * to be initialized by the front-end.
191 */
192 KASSERT(sc->sc_phandle > 0);
193 KASSERT(sc->sc_config != NULL);
194 KASSERT(sc->sc_i2c_parent != NULL);
195
196 /*
197 * We start out assuming that the i2c bus nodes are children of
198 * our own node. We'll adjust later if we encounter an "i2c-mux"
199 * node when counting our children. If we encounter such a node,
200 * then it's that node that is the parent of the i2c bus children.
201 */
202 sc->sc_i2c_mux_phandle = sc->sc_phandle;
203
204 /*
205 * Gather up all of the various bits of information needed
206 * for this particular type of i2c mux.
207 */
208 sc->sc_mux_data = sc->sc_config->get_mux_info(sc);
209 if (sc->sc_mux_data == NULL) {
210 aprint_error_dev(sc->sc_dev, "unable to get info for mux\n");
211 return;
212 }
213
214 sc->sc_nbusses = iicmux_count_children(sc);
215 if (sc->sc_nbusses == 0) {
216 return;
217 }
218
219 sc->sc_busses = kmem_zalloc(sizeof(*sc->sc_busses) * sc->sc_nbusses,
220 KM_SLEEP);
221
222 int child, idx;
223 for (child = OF_child(sc->sc_i2c_mux_phandle), idx = 0; child;
224 child = OF_peer(child), idx++) {
225 KASSERT(idx < sc->sc_nbusses);
226 iicmux_attach_bus(sc, child, idx);
227 }
228 }
229