i2cmux.c revision 1.1.2.2 1 1.1.2.2 thorpej /* $NetBSD: i2cmux.c,v 1.1.2.2 2021/01/03 16:34:58 thorpej Exp $ */
2 1.1.2.2 thorpej
3 1.1.2.2 thorpej /*-
4 1.1.2.2 thorpej * Copyright (c) 2020 The NetBSD Foundation, Inc.
5 1.1.2.2 thorpej * All rights reserved.
6 1.1.2.2 thorpej *
7 1.1.2.2 thorpej * This code is derived from software contributed to The NetBSD Foundation
8 1.1.2.2 thorpej * by Jason R. Thorpe.
9 1.1.2.2 thorpej *
10 1.1.2.2 thorpej * Redistribution and use in source and binary forms, with or without
11 1.1.2.2 thorpej * modification, are permitted provided that the following conditions
12 1.1.2.2 thorpej * are met:
13 1.1.2.2 thorpej * 1. Redistributions of source code must retain the above copyright
14 1.1.2.2 thorpej * notice, this list of conditions and the following disclaimer.
15 1.1.2.2 thorpej * 2. Redistributions in binary form must reproduce the above copyright
16 1.1.2.2 thorpej * notice, this list of conditions and the following disclaimer in the
17 1.1.2.2 thorpej * documentation and/or other materials provided with the distribution.
18 1.1.2.2 thorpej *
19 1.1.2.2 thorpej * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 1.1.2.2 thorpej * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 1.1.2.2 thorpej * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 1.1.2.2 thorpej * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 1.1.2.2 thorpej * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 1.1.2.2 thorpej * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 1.1.2.2 thorpej * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 1.1.2.2 thorpej * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 1.1.2.2 thorpej * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 1.1.2.2 thorpej * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 1.1.2.2 thorpej * POSSIBILITY OF SUCH DAMAGE.
30 1.1.2.2 thorpej */
31 1.1.2.2 thorpej
32 1.1.2.2 thorpej #include <sys/cdefs.h>
33 1.1.2.2 thorpej __KERNEL_RCSID(0, "$NetBSD: i2cmux.c,v 1.1.2.2 2021/01/03 16:34:58 thorpej Exp $");
34 1.1.2.2 thorpej
35 1.1.2.2 thorpej #include <sys/types.h>
36 1.1.2.2 thorpej #include <sys/device.h>
37 1.1.2.2 thorpej #include <sys/kernel.h>
38 1.1.2.2 thorpej #include <sys/kmem.h>
39 1.1.2.2 thorpej
40 1.1.2.2 thorpej #include <dev/fdt/fdtvar.h>
41 1.1.2.2 thorpej #include <dev/i2c/i2cvar.h>
42 1.1.2.2 thorpej #include <dev/i2c/i2cmuxvar.h>
43 1.1.2.2 thorpej
44 1.1.2.2 thorpej /*
45 1.1.2.2 thorpej * i2c mux
46 1.1.2.2 thorpej *
47 1.1.2.2 thorpej * This works by interposing a set of virtual controllers behind the real
48 1.1.2.2 thorpej * i2c controller. We provide our own acquire and release functions that
49 1.1.2.2 thorpej * perform the following tasks:
50 1.1.2.2 thorpej *
51 1.1.2.2 thorpej * acquire -> acquire parent controller, program mux
52 1.1.2.2 thorpej *
53 1.1.2.2 thorpej * release -> idle mux, release parent controller
54 1.1.2.2 thorpej *
55 1.1.2.2 thorpej * All of the actual I/O operations are transparently passed through.
56 1.1.2.2 thorpej *
57 1.1.2.2 thorpej * N.B. the locking order; the generic I2C layer has already acquired
58 1.1.2.2 thorpej * our virtual controller's mutex before calling our acquire function,
59 1.1.2.2 thorpej * and we will then acquire the real controller's mutex when we acquire
60 1.1.2.2 thorpej * the bus, so the order is:
61 1.1.2.2 thorpej *
62 1.1.2.2 thorpej * mux virtual controller -> parent controller
63 1.1.2.2 thorpej *
64 1.1.2.2 thorpej * These are common routines used by various i2c mux controller
65 1.1.2.2 thorpej * implementations (gpio, pin mux, i2c device, etc.).
66 1.1.2.2 thorpej */
67 1.1.2.2 thorpej
68 1.1.2.2 thorpej /*****************************************************************************/
69 1.1.2.2 thorpej
70 1.1.2.2 thorpej static int
71 1.1.2.2 thorpej iicmux_acquire_bus(void * const v, int const flags)
72 1.1.2.2 thorpej {
73 1.1.2.2 thorpej struct iicmux_bus * const bus = v;
74 1.1.2.2 thorpej struct iicmux_softc * const sc = bus->mux;
75 1.1.2.2 thorpej int error;
76 1.1.2.2 thorpej
77 1.1.2.2 thorpej error = iic_acquire_bus(sc->sc_i2c_parent, flags);
78 1.1.2.2 thorpej if (error) {
79 1.1.2.2 thorpej return error;
80 1.1.2.2 thorpej }
81 1.1.2.2 thorpej
82 1.1.2.2 thorpej error = sc->sc_config->acquire_bus(bus, flags);
83 1.1.2.2 thorpej if (error) {
84 1.1.2.2 thorpej iic_release_bus(sc->sc_i2c_parent, flags);
85 1.1.2.2 thorpej }
86 1.1.2.2 thorpej
87 1.1.2.2 thorpej return error;
88 1.1.2.2 thorpej }
89 1.1.2.2 thorpej
90 1.1.2.2 thorpej static void
91 1.1.2.2 thorpej iicmux_release_bus(void * const v, int const flags)
92 1.1.2.2 thorpej {
93 1.1.2.2 thorpej struct iicmux_bus * const bus = v;
94 1.1.2.2 thorpej struct iicmux_softc * const sc = bus->mux;
95 1.1.2.2 thorpej
96 1.1.2.2 thorpej sc->sc_config->release_bus(bus, flags);
97 1.1.2.2 thorpej iic_release_bus(sc->sc_i2c_parent, flags);
98 1.1.2.2 thorpej }
99 1.1.2.2 thorpej
100 1.1.2.2 thorpej static int
101 1.1.2.2 thorpej iicmux_exec(void * const v, i2c_op_t const op, i2c_addr_t const addr,
102 1.1.2.2 thorpej const void * const cmdbuf, size_t const cmdlen, void * const databuf,
103 1.1.2.2 thorpej size_t const datalen, int const flags)
104 1.1.2.2 thorpej {
105 1.1.2.2 thorpej struct iicmux_bus * const bus = v;
106 1.1.2.2 thorpej struct iicmux_softc * const sc = bus->mux;
107 1.1.2.2 thorpej
108 1.1.2.2 thorpej return iic_exec(sc->sc_i2c_parent, op, addr, cmdbuf, cmdlen,
109 1.1.2.2 thorpej databuf, datalen, flags);
110 1.1.2.2 thorpej }
111 1.1.2.2 thorpej
112 1.1.2.2 thorpej /*****************************************************************************/
113 1.1.2.2 thorpej
114 1.1.2.2 thorpej static int
115 1.1.2.2 thorpej iicmux_count_children(struct iicmux_softc * const sc)
116 1.1.2.2 thorpej {
117 1.1.2.2 thorpej char name[32];
118 1.1.2.2 thorpej int child, count;
119 1.1.2.2 thorpej
120 1.1.2.2 thorpej restart:
121 1.1.2.2 thorpej for (child = OF_child(sc->sc_i2c_mux_phandle), count = 0; child;
122 1.1.2.2 thorpej child = OF_peer(child)) {
123 1.1.2.2 thorpej if (OF_getprop(child, "name", name, sizeof(name)) <= 0) {
124 1.1.2.2 thorpej continue;
125 1.1.2.2 thorpej }
126 1.1.2.2 thorpej if (strcmp(name, "i2c-mux") == 0) {
127 1.1.2.2 thorpej /*
128 1.1.2.2 thorpej * The node we encountered is the acutal parent
129 1.1.2.2 thorpej * of the i2c bus children. Stash its phandle
130 1.1.2.2 thorpej * and restart the enumeration.
131 1.1.2.2 thorpej */
132 1.1.2.2 thorpej sc->sc_i2c_mux_phandle = child;
133 1.1.2.2 thorpej goto restart;
134 1.1.2.2 thorpej }
135 1.1.2.2 thorpej count++;
136 1.1.2.2 thorpej }
137 1.1.2.2 thorpej
138 1.1.2.2 thorpej return count;
139 1.1.2.2 thorpej }
140 1.1.2.2 thorpej
141 1.1.2.2 thorpej /* XXX iicbus_print() should be able to do this. */
142 1.1.2.2 thorpej static int
143 1.1.2.2 thorpej iicmux_print(void * const aux, const char * const pnp)
144 1.1.2.2 thorpej {
145 1.1.2.2 thorpej i2c_tag_t const tag = aux;
146 1.1.2.2 thorpej struct iicmux_bus * const bus = tag->ic_cookie;
147 1.1.2.2 thorpej int rv;
148 1.1.2.2 thorpej
149 1.1.2.2 thorpej rv = iicbus_print(aux, pnp);
150 1.1.2.2 thorpej aprint_normal(" bus %d", bus->busidx);
151 1.1.2.2 thorpej
152 1.1.2.2 thorpej return rv;
153 1.1.2.2 thorpej }
154 1.1.2.2 thorpej
155 1.1.2.2 thorpej static void
156 1.1.2.2 thorpej iicmux_attach_bus(struct iicmux_softc * const sc,
157 1.1.2.2 thorpej int const phandle, int const busidx)
158 1.1.2.2 thorpej {
159 1.1.2.2 thorpej struct iicmux_bus * const bus = &sc->sc_busses[busidx];
160 1.1.2.2 thorpej
161 1.1.2.2 thorpej bus->mux = sc;
162 1.1.2.2 thorpej bus->busidx = busidx;
163 1.1.2.2 thorpej bus->phandle = phandle;
164 1.1.2.2 thorpej
165 1.1.2.2 thorpej bus->bus_data = sc->sc_config->get_bus_info(bus);
166 1.1.2.2 thorpej if (bus->bus_data == NULL) {
167 1.1.2.2 thorpej aprint_error_dev(sc->sc_dev,
168 1.1.2.2 thorpej "unable to get info for bus %d\n", busidx);
169 1.1.2.2 thorpej return;
170 1.1.2.2 thorpej }
171 1.1.2.2 thorpej
172 1.1.2.2 thorpej iic_tag_init(&bus->controller);
173 1.1.2.2 thorpej bus->controller.ic_cookie = bus;
174 1.1.2.2 thorpej bus->controller.ic_acquire_bus = iicmux_acquire_bus;
175 1.1.2.2 thorpej bus->controller.ic_release_bus = iicmux_release_bus;
176 1.1.2.2 thorpej bus->controller.ic_exec = iicmux_exec;
177 1.1.2.2 thorpej
178 1.1.2.2 thorpej fdtbus_register_i2c_controller(&bus->controller, bus->phandle);
179 1.1.2.2 thorpej
180 1.1.2.2 thorpej fdtbus_attach_i2cbus(sc->sc_dev, bus->phandle, &bus->controller,
181 1.1.2.2 thorpej iicmux_print);
182 1.1.2.2 thorpej }
183 1.1.2.2 thorpej
184 1.1.2.2 thorpej void
185 1.1.2.2 thorpej iicmux_attach(struct iicmux_softc * const sc)
186 1.1.2.2 thorpej {
187 1.1.2.2 thorpej
188 1.1.2.2 thorpej /*
189 1.1.2.2 thorpej * We expect sc->sc_phandle, sc->sc_config, and sc->sc_i2c_parent
190 1.1.2.2 thorpej * to be initialized by the front-end.
191 1.1.2.2 thorpej */
192 1.1.2.2 thorpej KASSERT(sc->sc_phandle > 0);
193 1.1.2.2 thorpej KASSERT(sc->sc_config != NULL);
194 1.1.2.2 thorpej KASSERT(sc->sc_i2c_parent != NULL);
195 1.1.2.2 thorpej
196 1.1.2.2 thorpej /*
197 1.1.2.2 thorpej * We start out assuming that the i2c bus nodes are children of
198 1.1.2.2 thorpej * our own node. We'll adjust later if we encounter an "i2c-mux"
199 1.1.2.2 thorpej * node when counting our children. If we encounter such a node,
200 1.1.2.2 thorpej * then it's that node that is the parent of the i2c bus children.
201 1.1.2.2 thorpej */
202 1.1.2.2 thorpej sc->sc_i2c_mux_phandle = sc->sc_phandle;
203 1.1.2.2 thorpej
204 1.1.2.2 thorpej /*
205 1.1.2.2 thorpej * Gather up all of the various bits of information needed
206 1.1.2.2 thorpej * for this particular type of i2c mux.
207 1.1.2.2 thorpej */
208 1.1.2.2 thorpej sc->sc_mux_data = sc->sc_config->get_mux_info(sc);
209 1.1.2.2 thorpej if (sc->sc_mux_data == NULL) {
210 1.1.2.2 thorpej aprint_error_dev(sc->sc_dev, "unable to get info for mux\n");
211 1.1.2.2 thorpej return;
212 1.1.2.2 thorpej }
213 1.1.2.2 thorpej
214 1.1.2.2 thorpej sc->sc_nbusses = iicmux_count_children(sc);
215 1.1.2.2 thorpej if (sc->sc_nbusses == 0) {
216 1.1.2.2 thorpej return;
217 1.1.2.2 thorpej }
218 1.1.2.2 thorpej
219 1.1.2.2 thorpej sc->sc_busses = kmem_zalloc(sizeof(*sc->sc_busses) * sc->sc_nbusses,
220 1.1.2.2 thorpej KM_SLEEP);
221 1.1.2.2 thorpej
222 1.1.2.2 thorpej int child, idx;
223 1.1.2.2 thorpej for (child = OF_child(sc->sc_i2c_mux_phandle), idx = 0; child;
224 1.1.2.2 thorpej child = OF_peer(child), idx++) {
225 1.1.2.2 thorpej KASSERT(idx < sc->sc_nbusses);
226 1.1.2.2 thorpej iicmux_attach_bus(sc, child, idx);
227 1.1.2.2 thorpej }
228 1.1.2.2 thorpej }
229