1/*	$NetBSD: i2cmux.c,v 1.7 2021/11/10 15:39:03 msaitoh 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.7 2021/11/10 15:39:03 msaitoh 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#include <dev/acpi/acpi_i2c.h>
51#endif
52
53/*
54 * i2c mux
55 *
56 * This works by interposing a set of virtual controllers behind the real
57 * i2c controller.  We provide our own acquire and release functions that
58 * perform the following tasks:
59 *
60 *	acquire -> acquire parent controller, program mux
61 *
62 *	release -> idle mux, release parent controller
63 *
64 * All of the actual I/O operations are transparently passed through.
65 *
66 * N.B. the locking order; the generic I2C layer has already acquired
67 * our virtual controller's mutex before calling our acquire function,
68 * and we will then acquire the real controller's mutex when we acquire
69 * the bus, so the order is:
70 *
71 *	mux virtual controller -> parent controller
72 *
73 * These are common routines used by various i2c mux controller
74 * implementations (gpio, pin mux, i2c device, etc.).
75 */
76
77/*****************************************************************************/
78
79static int
80iicmux_acquire_bus(void * const v, int const flags)
81{
82	struct iicmux_bus * const bus = v;
83	struct iicmux_softc * const sc = bus->mux;
84	int error;
85
86	error = iic_acquire_bus(sc->sc_i2c_parent, flags);
87	if (error) {
88		return error;
89	}
90
91	error = sc->sc_config->acquire_bus(bus, flags);
92	if (error) {
93		iic_release_bus(sc->sc_i2c_parent, flags);
94	}
95
96	return error;
97}
98
99static void
100iicmux_release_bus(void * const v, int const flags)
101{
102	struct iicmux_bus * const bus = v;
103	struct iicmux_softc * const sc = bus->mux;
104
105	sc->sc_config->release_bus(bus, flags);
106	iic_release_bus(sc->sc_i2c_parent, flags);
107}
108
109static int
110iicmux_exec(void * const v, i2c_op_t const op, i2c_addr_t const addr,
111    const void * const cmdbuf, size_t const cmdlen, void * const databuf,
112    size_t const datalen, int const flags)
113{
114	struct iicmux_bus * const bus = v;
115	struct iicmux_softc * const sc = bus->mux;
116
117	return iic_exec(sc->sc_i2c_parent, op, addr, cmdbuf, cmdlen,
118			databuf, datalen, flags);
119}
120
121/*****************************************************************************/
122
123static int
124iicmux_count_children(struct iicmux_softc * const sc)
125{
126	char name[32];
127	int child, count;
128
129 restart:
130	for (child = OF_child(sc->sc_i2c_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			sc->sc_i2c_mux_phandle = child;
142			goto restart;
143		}
144		count++;
145	}
146
147	return count;
148}
149
150/* XXX iicbus_print() should be able to do this. */
151static int
152iicmux_print(void * const aux, const char * const pnp)
153{
154	i2c_tag_t const tag = aux;
155	struct iicmux_bus * const bus = tag->ic_cookie;
156	int rv;
157
158	rv = iicbus_print(aux, pnp);
159	aprint_normal(" bus %d", bus->busidx);
160
161	return rv;
162}
163
164static void
165iicmux_attach_bus(struct iicmux_softc * const sc,
166    uintptr_t const handle, enum i2c_cookie_type handletype, int const busidx)
167{
168	struct iicmux_bus * const bus = &sc->sc_busses[busidx];
169
170	bus->mux = sc;
171	bus->busidx = busidx;
172	bus->handle = handle;
173	bus->handletype = handletype;
174
175	bus->bus_data = sc->sc_config->get_bus_info(bus);
176	if (bus->bus_data == NULL) {
177		aprint_error_dev(sc->sc_dev,
178		    "unable to get info for bus %d\n", busidx);
179		return;
180	}
181
182	iic_tag_init(&bus->controller);
183	bus->controller.ic_cookie = bus;
184	bus->controller.ic_acquire_bus = iicmux_acquire_bus;
185	bus->controller.ic_release_bus = iicmux_release_bus;
186	bus->controller.ic_exec = iicmux_exec;
187
188	switch (handletype) {
189	case I2C_COOKIE_OF:
190		fdtbus_register_i2c_controller(&bus->controller,
191		    (int)bus->handle);
192
193		fdtbus_attach_i2cbus(sc->sc_dev, (int)bus->handle,
194		    &bus->controller, iicmux_print);
195		break;
196#if NACPICA > 0
197	case I2C_COOKIE_ACPI: {
198		struct acpi_devnode *ad = acpi_match_node((ACPI_HANDLE)handle);
199		KASSERT(ad != NULL);
200		struct i2cbus_attach_args iba = {
201			.iba_tag = &bus->controller,
202			.iba_child_devices = acpi_enter_i2c_devs(NULL, ad)
203		};
204		config_found(sc->sc_dev, &iba, iicbus_print, CFARGS_NONE);
205	}	break;
206#endif
207	default:
208		aprint_error_dev(sc->sc_dev, "unknown handle type\n");
209		break;
210	}
211}
212
213static void
214iicmux_attach_fdt(struct iicmux_softc * const sc)
215{
216	/*
217	 * We start out assuming that the i2c bus nodes are children of
218	 * our own node.  We'll adjust later if we encounter an "i2c-mux"
219	 * node when counting our children.  If we encounter such a node,
220	 * then it's that node that is the parent of the i2c bus children.
221	 */
222	sc->sc_i2c_mux_phandle = (int)sc->sc_handle;
223
224	sc->sc_nbusses = iicmux_count_children(sc);
225	if (sc->sc_nbusses == 0) {
226		return;
227	}
228
229	sc->sc_busses = kmem_zalloc(sizeof(*sc->sc_busses) * sc->sc_nbusses,
230	    KM_SLEEP);
231
232	int child, idx;
233	for (child = OF_child(sc->sc_i2c_mux_phandle), idx = 0; child;
234	     child = OF_peer(child), idx++) {
235		KASSERT(idx < sc->sc_nbusses);
236		iicmux_attach_bus(sc, child, I2C_COOKIE_OF, idx);
237	}
238}
239
240#if NACPICA > 0
241static void
242iicmux_attach_acpi(struct iicmux_softc * const sc)
243{
244	ACPI_HANDLE hdl = (ACPI_HANDLE)sc->sc_handle;
245	struct acpi_devnode *devnode, *ad;
246	int idx;
247
248	devnode = acpi_match_node(hdl);
249	KASSERT(devnode != NULL);
250
251	/* Count child busses */
252	sc->sc_nbusses = 0;
253	SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
254		if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE ||
255		    !acpi_device_present(ad->ad_handle)) {
256			continue;
257		}
258		sc->sc_nbusses++;
259	}
260
261	sc->sc_busses = kmem_zalloc(sizeof(*sc->sc_busses) * sc->sc_nbusses,
262	    KM_SLEEP);
263
264	/* Attach child busses */
265	idx = 0;
266	SIMPLEQ_FOREACH(ad, &devnode->ad_child_head, ad_child_list) {
267		if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE ||
268		    !acpi_device_present(ad->ad_handle)) {
269			continue;
270		}
271		iicmux_attach_bus(sc, (uintptr_t)ad->ad_handle,
272		    I2C_COOKIE_ACPI, idx);
273		idx++;
274	}
275}
276#endif
277
278void
279iicmux_attach(struct iicmux_softc * const sc)
280{
281	/*
282	 * We expect sc->sc_handle, sc->sc_config, and sc->sc_i2c_parent
283	 * to be initialized by the front-end.
284	 */
285	KASSERT(sc->sc_handle > 0);
286	KASSERT(sc->sc_config != NULL);
287	KASSERT(sc->sc_i2c_parent != NULL);
288
289	/*
290	 * Gather up all of the various bits of information needed
291	 * for this particular type of i2c mux.
292	 */
293	sc->sc_mux_data = sc->sc_config->get_mux_info(sc);
294	if (sc->sc_mux_data == NULL) {
295		aprint_error_dev(sc->sc_dev, "unable to get info for mux\n");
296		return;
297	}
298
299	/*
300	 * Do configuration method (OF, ACPI) specific setup.
301	 */
302	switch (sc->sc_handletype) {
303	case I2C_COOKIE_OF:
304		iicmux_attach_fdt(sc);
305		break;
306#if NACPICA > 0
307	case I2C_COOKIE_ACPI:
308		iicmux_attach_acpi(sc);
309		break;
310#endif
311	default:
312		aprint_error_dev(sc->sc_dev, "could not configure mux: "
313		    "handle type %u not supported\n", sc->sc_handletype);
314		break;
315	}
316}
317