xref: /freebsd/sys/dev/iicbus/mux/iicmux.c (revision 61e21613)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2019 Ian Lepore <ian@freebsd.org>
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/cdefs.h>
29 #include "opt_platform.h"
30 
31 #include <sys/param.h>
32 #include <sys/bus.h>
33 #include <sys/kernel.h>
34 #include <sys/module.h>
35 #include <sys/sysctl.h>
36 
37 #ifdef FDT
38 #include <dev/ofw/ofw_bus.h>
39 #include <dev/ofw/ofw_bus_subr.h>
40 #include <dev/ofw/openfirm.h>
41 #endif
42 
43 #include <dev/iicbus/iiconf.h>
44 #include "iicbus_if.h"
45 #include "iicmux_if.h"
46 #include "iicmux.h"
47 
48 /*------------------------------------------------------------------------------
49  * iicbus methods, called by the iicbus functions in iiconf.c.
50  *
51  * All these functions return an IIC adapter-layer error code (because we are
52  * pretending to be a host bridge/i2c controller).  Standard errno values
53  * returned from these must be encoded using iic2errno().
54  *----------------------------------------------------------------------------*/
55 
56 static int
57 iicmux_callback(device_t dev, int index, caddr_t data)
58 {
59 	struct iicmux_softc *sc = device_get_softc(dev);
60 	struct iic_reqbus_data *rd;
61 	int err, i;
62 
63 	/* If it's not one of the operations we know about, bail early. */
64 	if (index != IIC_REQUEST_BUS && index != IIC_RELEASE_BUS)
65 		return (iic2errno(EOPNOTSUPP));
66 
67 	/*
68 	 * Ensure that the data passed to us includes the device_t of the child
69 	 * bus and device.  If missing, someone bypassed iicbus_request_bus()
70 	 * and called this method directly using the old calling standard.  If
71 	 * present, find the index of the child bus that called us.
72 	 */
73 	rd = (struct iic_reqbus_data *)data;
74 	if (!(rd->flags & IIC_REQBUS_DEV))
75 		return (iic2errno(EINVAL));
76 
77 	for (i = 0; i <= sc->maxbus && sc->childdevs[i] != rd->bus; ++i)
78 		continue;
79 	if (i > sc->maxbus)
80 		return (iic2errno(ENOENT));
81 
82 	/*
83 	 * If the operation is a release it "cannot fail".  Idle the downstream
84 	 * bus, then release exclusive use of the upstream bus, and we're done.
85 	 */
86 	if (index == IIC_RELEASE_BUS) {
87 		if (sc->debugmux > 0) {
88 			device_printf(dev, "idle the bus for %s on bus %s\n",
89 			    device_get_nameunit(rd->dev),
90 			    device_get_nameunit(rd->bus));
91 		}
92 		IICMUX_BUS_SELECT(dev, IICMUX_SELECT_IDLE, rd);
93 		iicbus_release_bus(sc->busdev, dev);
94 		return (IIC_NOERR);
95 	}
96 
97 	if (sc->debugmux > 0) {
98 		device_printf(dev, "select bus idx %d for %s on bus %s\n", i,
99 		    device_get_nameunit(rd->dev), device_get_nameunit(rd->bus));
100 	}
101 
102 	/*
103 	 * The operation is a request for exclusive use.  First we have to
104 	 * request exclusive use of our upstream bus.  If multiple slave devices
105 	 * from our different child buses attempt to do IO at the same time,
106 	 * this is what ensures that they don't switch the bus out from under
107 	 * each other. The first one in proceeds and others wait here (or get an
108 	 * EWOULDBLOCK return if they're using IIC_DONTWAIT).
109 	 */
110 	if ((err = iicbus_request_bus(sc->busdev, dev, rd->flags)) != 0)
111 		return (err); /* Already an IIC error code. */
112 
113 	/*
114 	 * Now that we own exclusive use of the upstream bus, connect it to the
115 	 * downstream bus where the request came from.
116 	 */
117 	if ((err = IICMUX_BUS_SELECT(dev, i, rd)) != 0)
118 		iicbus_release_bus(sc->busdev, dev);
119 
120 	return (err);
121 }
122 
123 static u_int
124 iicmux_get_frequency(device_t dev, u_char speed)
125 {
126 	struct iicmux_softc *sc = device_get_softc(dev);
127 
128 	return (IICBUS_GET_FREQUENCY(sc->busdev, speed));
129 }
130 
131 #ifdef FDT
132 static phandle_t
133 iicmux_get_node(device_t dev, device_t child)
134 {
135 	struct iicmux_softc *sc = device_get_softc(dev);
136 	int i;
137 
138 	for (i = 0; i <= sc->maxbus; ++i) {
139 		if (sc->childdevs[i] == child)
140 			return (sc->childnodes[i]);
141 	}
142 	return (0); /* null handle */
143 }
144 #endif
145 
146 static int
147 iicmux_intr(device_t dev, int event, char *buf)
148 {
149 	struct iicmux_softc *sc = device_get_softc(dev);
150 
151 	/* XXX iicbus_intr() in iiconf.c should return status. */
152 
153 	iicbus_intr(sc->busdev, event, buf);
154 	return (0);
155 }
156 
157 static int
158 iicmux_read(device_t dev, char *buf, int len, int *bytes, int last, int delay)
159 {
160 	struct iicmux_softc *sc = device_get_softc(dev);
161 
162 	return (iicbus_read(sc->busdev, buf, len, bytes, last, delay));
163 }
164 
165 static int
166 iicmux_repeated_start(device_t dev, u_char slave, int timeout)
167 {
168 	struct iicmux_softc *sc = device_get_softc(dev);
169 
170 	return (iicbus_repeated_start(sc->busdev, slave, timeout));
171 }
172 
173 static int
174 iicmux_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
175 {
176 	struct iicmux_softc *sc = device_get_softc(dev);
177 
178 	return (iicbus_reset(sc->busdev, speed, addr, oldaddr));
179 }
180 
181 static int
182 iicmux_start(device_t dev, u_char slave, int timeout)
183 {
184 	struct iicmux_softc *sc = device_get_softc(dev);
185 
186 	return (iicbus_start(sc->busdev, slave, timeout));
187 }
188 
189 static int
190 iicmux_stop(device_t dev)
191 {
192 	struct iicmux_softc *sc = device_get_softc(dev);
193 
194 	return (iicbus_stop(sc->busdev));
195 }
196 
197 static int
198 iicmux_transfer( device_t dev, struct iic_msg *msgs, uint32_t nmsgs)
199 {
200 	struct iicmux_softc *sc = device_get_softc(dev);
201 
202 	return (iicbus_transfer(sc->busdev, msgs, nmsgs));
203 }
204 
205 static int
206 iicmux_write(device_t dev, const char *buf, int len, int *bytes, int timeout)
207 {
208 	struct iicmux_softc *sc = device_get_softc(dev);
209 
210 	return (iicbus_write(sc->busdev, buf, len, bytes, timeout));
211 }
212 
213 /*------------------------------------------------------------------------------
214  * iicmux helper functions, called by hardware-specific drivers.
215  * All these functions return a standard errno value.
216  *----------------------------------------------------------------------------*/
217 
218 int
219 iicmux_add_child(device_t dev, device_t child, int busidx)
220 {
221 	struct iicmux_softc *sc = device_get_softc(dev);
222 
223 	if (busidx >= sc->numbuses) {
224 		device_printf(dev,
225 		    "iicmux_add_child: bus idx %d too big", busidx);
226 		return (EINVAL);
227 	}
228 	if (sc->childdevs[busidx] != NULL) {
229 		device_printf(dev, "iicmux_add_child: bus idx %d already added",
230 		    busidx);
231 		return (EINVAL);
232 	}
233 
234 	sc->childdevs[busidx] = child;
235 	if (sc->maxbus < busidx)
236 		sc->maxbus = busidx;
237 
238 	return (0);
239 }
240 
241 static int
242 iicmux_attach_children(struct iicmux_softc *sc)
243 {
244 	int i;
245 #ifdef FDT
246 	phandle_t child, node, parent;
247 	pcell_t idx;
248 
249 	/*
250 	 * Find our FDT node.  Child nodes within our node will become our
251 	 * iicbus children.
252 	 */
253 	if((node = ofw_bus_get_node(sc->dev)) == 0) {
254 		device_printf(sc->dev, "cannot find FDT node\n");
255 		return (ENOENT);
256 	}
257 
258 	/*
259 	 * First we have to see if there is a child node named "i2c-mux".  If it
260 	 * exists, all children of that node are buses, else all children of the
261 	 * device node are buses.
262 	 */
263 	if ((parent = ofw_bus_find_child(node, "i2c-mux")) == 0)
264 		parent = node;
265 
266 	/*
267 	 * Attach the children represented in the device tree.
268 	 */
269 	for (child = OF_child(parent); child != 0; child = OF_peer(child)) {
270 		if (OF_getencprop(child, "reg", &idx, sizeof(idx)) == -1) {
271 			device_printf(sc->dev,
272 			    "child bus missing required 'reg' property\n");
273 			continue;
274 		}
275 		if (idx >= sc->numbuses) {
276 			device_printf(sc->dev,
277 			    "child bus 'reg' property %d exceeds the number "
278 			    "of buses supported by the device (%d)\n",
279 			    idx, sc->numbuses);
280 			continue;
281 		}
282 		sc->childdevs[idx] = device_add_child(sc->dev, "iicbus", -1);
283 		sc->childnodes[idx] = child;
284 		if (sc->maxbus < (int)idx)
285 			sc->maxbus = idx;
286 	}
287 
288 	/* If we configured anything using FDT data, we're done. */
289 	if (sc->maxbus >= 0)
290 		return (0);
291 #endif /* FDT */
292 
293 	/*
294 	 * If we make it to here, we didn't add any children based on FDT data.
295 	 * Add an iicbus child for every downstream bus supported by the mux.
296 	 */
297 	for (i = 0; i < sc->numbuses; ++i) {
298 		sc->childdevs[i] = device_add_child(sc->dev, "iicbus", -1);
299 		sc->maxbus = i;
300 	}
301 
302 	return (0);
303 }
304 
305 int
306 iicmux_attach(device_t dev, device_t busdev, int numbuses)
307 {
308 	struct iicmux_softc *sc = device_get_softc(dev);
309 	int err;
310 
311 	if (numbuses >= IICMUX_MAX_BUSES) {
312 		device_printf(dev, "iicmux_attach: numbuses %d > max %d\n",
313 		    numbuses, IICMUX_MAX_BUSES);
314 		return (EINVAL);
315 	}
316 
317 	sc->dev = dev;
318 	sc->busdev = busdev;
319 	sc->maxbus = -1;
320 	sc->numbuses = numbuses;
321 
322 	if ((err = iicmux_attach_children(sc)) != 0)
323 		return (err);
324 
325 	SYSCTL_ADD_UINT(device_get_sysctl_ctx(sc->dev),
326 	    SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO,
327 	    "debugmux", CTLFLAG_RWTUN, &sc->debugmux, 0, "debug mux operations");
328 
329 	return (0);
330 }
331 
332 int
333 iicmux_detach(device_t dev)
334 {
335 	struct iicmux_softc *sc = device_get_softc(dev);
336 	int err, i;
337 
338 	/* Delete only the children we added in iicmux_add* functions. */
339 	for (i = 0; i <= sc->maxbus; ++i) {
340 		if (sc->childdevs[i] == NULL)
341 			continue;
342 		if ((err = device_delete_child(dev, sc->childdevs[i])) != 0)
343 			return (err);
344 		sc->childdevs[i] = NULL;
345 	}
346 
347 	return (0);
348 }
349 
350 static device_method_t iicmux_methods [] = {
351 	/* iicbus_if methods */
352 	DEVMETHOD(iicbus_intr,			iicmux_intr),
353 	DEVMETHOD(iicbus_callback,		iicmux_callback),
354 	DEVMETHOD(iicbus_repeated_start,	iicmux_repeated_start),
355 	DEVMETHOD(iicbus_start,			iicmux_start),
356 	DEVMETHOD(iicbus_stop,			iicmux_stop),
357 	DEVMETHOD(iicbus_read,			iicmux_read),
358 	DEVMETHOD(iicbus_write,			iicmux_write),
359 	DEVMETHOD(iicbus_reset,			iicmux_reset),
360 	DEVMETHOD(iicbus_transfer,		iicmux_transfer),
361 	DEVMETHOD(iicbus_get_frequency,		iicmux_get_frequency),
362 
363 #ifdef FDT
364 	/* ofwbus_if methods */
365 	DEVMETHOD(ofw_bus_get_node,		iicmux_get_node),
366 #endif
367 
368 	DEVMETHOD_END
369 };
370 
371 static int
372 iicmux_modevent(module_t mod, int type, void *unused)
373 {
374 	switch (type) {
375 	case MOD_LOAD:
376 		return 0;
377 	case MOD_UNLOAD:
378 		return 0;
379 	}
380 	return EINVAL;
381 }
382 
383 static moduledata_t iicmux_mod = {
384 	"iicmux",
385 	iicmux_modevent,
386 	0
387 };
388 
389 DEFINE_CLASS_0(iicmux, iicmux_driver, iicmux_methods,
390     sizeof(struct iicmux_softc));
391 
392 DECLARE_MODULE(iicmux, iicmux_mod, SI_SUB_DRIVERS, SI_ORDER_ANY);
393 MODULE_VERSION(iicmux, 1);
394 
395 MODULE_DEPEND(iicmux, iicbus, 1, 1, 1);
396