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