1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org>
5 * Copyright (c) 2010 Luiz Otavio O Souza
6 * Copyright (c) 2019 Ian Lepore <ian@freebsd.org>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 *    notice, this list of conditions and the following disclaimer in the
15 *    documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30#include <sys/cdefs.h>
31__FBSDID("$FreeBSD$");
32
33#include "opt_platform.h"
34
35#include <sys/param.h>
36#include <sys/systm.h>
37#include <sys/bus.h>
38#include <sys/gpio.h>
39#include <sys/kernel.h>
40#include <sys/module.h>
41
42#include <dev/gpio/gpiobusvar.h>
43#include <dev/iicbus/iiconf.h>
44
45#include "gpiobus_if.h"
46#include "iicbb_if.h"
47
48#define	GPIOIIC_SCL_DFLT	0
49#define	GPIOIIC_SDA_DFLT	1
50#define	GPIOIIC_MIN_PINS	2
51
52struct gpioiic_softc
53{
54	device_t	dev;
55	gpio_pin_t	sclpin;
56	gpio_pin_t	sdapin;
57};
58
59#ifdef FDT
60
61#include <dev/ofw/ofw_bus.h>
62
63static struct ofw_compat_data compat_data[] = {
64	{"i2c-gpio",  true}, /* Standard devicetree compat string */
65	{"gpioiic",   true}, /* Deprecated old freebsd compat string */
66	{NULL,        false}
67};
68OFWBUS_PNP_INFO(compat_data);
69SIMPLEBUS_PNP_INFO(compat_data);
70
71static phandle_t
72gpioiic_get_node(device_t bus, device_t dev)
73{
74
75	/* Share our fdt node with iicbus so it can find its child nodes. */
76	return (ofw_bus_get_node(bus));
77}
78
79static int
80gpioiic_setup_fdt_pins(struct gpioiic_softc *sc)
81{
82	phandle_t node;
83	int err;
84
85	node = ofw_bus_get_node(sc->dev);
86
87	/*
88	 * Historically, we used the first two array elements of the gpios
89	 * property.  The modern bindings specify separate scl-gpios and
90	 * sda-gpios properties.  We cope with whichever is present.
91	 */
92	if (OF_hasprop(node, "gpios")) {
93		if ((err = gpio_pin_get_by_ofw_idx(sc->dev, node,
94		    GPIOIIC_SCL_DFLT, &sc->sclpin)) != 0) {
95			device_printf(sc->dev, "invalid gpios property\n");
96			return (err);
97		}
98		if ((err = gpio_pin_get_by_ofw_idx(sc->dev, node,
99		    GPIOIIC_SDA_DFLT, &sc->sdapin)) != 0) {
100			device_printf(sc->dev, "ivalid gpios property\n");
101			return (err);
102		}
103	} else {
104		if ((err = gpio_pin_get_by_ofw_property(sc->dev, node,
105		    "scl-gpios", &sc->sclpin)) != 0) {
106			device_printf(sc->dev, "missing scl-gpios property\n");
107			return (err);
108		}
109		if ((err = gpio_pin_get_by_ofw_property(sc->dev, node,
110		    "sda-gpios", &sc->sdapin)) != 0) {
111			device_printf(sc->dev, "missing sda-gpios property\n");
112			return (err);
113		}
114	}
115	return (0);
116}
117#endif /* FDT */
118
119static int
120gpioiic_setup_hinted_pins(struct gpioiic_softc *sc)
121{
122	device_t busdev;
123	const char *busname, *devname;
124	int err, numpins, sclnum, sdanum, unit;
125
126	devname = device_get_name(sc->dev);
127	unit = device_get_unit(sc->dev);
128	busdev = device_get_parent(sc->dev);
129
130	/*
131	 * If there is not an "at" hint naming our actual parent, then we
132	 * weren't instantiated as a child of gpiobus via hints, and we thus
133	 * can't access ivars that only exist for such children.
134	 */
135	if (resource_string_value(devname, unit, "at", &busname) != 0 ||
136	    (strcmp(busname, device_get_nameunit(busdev)) != 0 &&
137	     strcmp(busname, device_get_name(busdev)) != 0)) {
138		return (ENOENT);
139	}
140
141	/* Make sure there were hints for at least two pins. */
142	numpins = gpiobus_get_npins(sc->dev);
143	if (numpins < GPIOIIC_MIN_PINS) {
144#ifdef FDT
145		/*
146		 * Be silent when there are no hints on FDT systems; the FDT
147		 * data will provide the pin config (we'll whine if it doesn't).
148		 */
149		if (numpins == 0) {
150			return (ENOENT);
151		}
152#endif
153		device_printf(sc->dev,
154		    "invalid pins hint; it must contain at least %d pins\n",
155		    GPIOIIC_MIN_PINS);
156		return (EINVAL);
157	}
158
159	/*
160	 * Our parent bus has already parsed the pins hint and it will use that
161	 * info when we call gpio_pin_get_by_child_index().  But we have to
162	 * handle the scl/sda index hints that tell us which of the two pins is
163	 * the clock and which is the data.  They're optional, but if present
164	 * they must be a valid index (0 <= index < numpins).
165	 */
166	if ((err = resource_int_value(devname, unit, "scl", &sclnum)) != 0)
167		sclnum = GPIOIIC_SCL_DFLT;
168	else if (sclnum < 0 || sclnum >= numpins) {
169		device_printf(sc->dev, "invalid scl hint %d\n", sclnum);
170		return (EINVAL);
171	}
172	if ((err = resource_int_value(devname, unit, "sda", &sdanum)) != 0)
173		sdanum = GPIOIIC_SDA_DFLT;
174	else if (sdanum < 0 || sdanum >= numpins) {
175		device_printf(sc->dev, "invalid sda hint %d\n", sdanum);
176		return (EINVAL);
177	}
178
179	/* Allocate gpiobus_pin structs for the pins we found above. */
180	if ((err = gpio_pin_get_by_child_index(sc->dev, sclnum,
181	    &sc->sclpin)) != 0)
182		return (err);
183	if ((err = gpio_pin_get_by_child_index(sc->dev, sdanum,
184	    &sc->sdapin)) != 0)
185		return (err);
186
187	return (0);
188}
189
190static void
191gpioiic_setsda(device_t dev, int val)
192{
193	struct gpioiic_softc *sc = device_get_softc(dev);
194
195	if (val) {
196		gpio_pin_setflags(sc->sdapin, GPIO_PIN_INPUT);
197	} else {
198		gpio_pin_setflags(sc->sdapin,
199		    GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN);
200		gpio_pin_set_active(sc->sdapin, 0);
201	}
202}
203
204static void
205gpioiic_setscl(device_t dev, int val)
206{
207	struct gpioiic_softc *sc = device_get_softc(dev);
208
209	if (val) {
210		gpio_pin_setflags(sc->sclpin, GPIO_PIN_INPUT);
211	} else {
212		gpio_pin_setflags(sc->sclpin,
213		    GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN);
214		gpio_pin_set_active(sc->sclpin, 0);
215	}
216}
217
218static int
219gpioiic_getscl(device_t dev)
220{
221	struct gpioiic_softc *sc = device_get_softc(dev);
222	bool val;
223
224	gpio_pin_setflags(sc->sclpin, GPIO_PIN_INPUT);
225	gpio_pin_is_active(sc->sclpin, &val);
226	return (val);
227}
228
229static int
230gpioiic_getsda(device_t dev)
231{
232	struct gpioiic_softc *sc = device_get_softc(dev);
233	bool val;
234
235	gpio_pin_setflags(sc->sdapin, GPIO_PIN_INPUT);
236	gpio_pin_is_active(sc->sdapin, &val);
237	return (val);
238}
239
240static int
241gpioiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
242{
243	struct gpioiic_softc *sc = device_get_softc(dev);
244
245	/* Stop driving the bus pins. */
246	gpio_pin_setflags(sc->sdapin, GPIO_PIN_INPUT);
247	gpio_pin_setflags(sc->sclpin, GPIO_PIN_INPUT);
248
249	/* Indicate that we have no slave address (master mode). */
250	return (IIC_ENOADDR);
251}
252
253static void
254gpioiic_cleanup(struct gpioiic_softc *sc)
255{
256
257	device_delete_children(sc->dev);
258
259	if (sc->sclpin != NULL)
260		gpio_pin_release(sc->sclpin);
261
262	if (sc->sdapin != NULL)
263		gpio_pin_release(sc->sdapin);
264}
265
266static int
267gpioiic_probe(device_t dev)
268{
269	int rv;
270
271	/*
272	 * By default we only bid to attach if specifically added by our parent
273	 * (usually via hint.gpioiic.#.at=busname).  On FDT systems we bid as
274	 * the default driver based on being configured in the FDT data.
275	 */
276	rv = BUS_PROBE_NOWILDCARD;
277
278#ifdef FDT
279	if (ofw_bus_status_okay(dev) &&
280	    ofw_bus_search_compatible(dev, compat_data)->ocd_data)
281                rv = BUS_PROBE_DEFAULT;
282#endif
283
284	device_set_desc(dev, "GPIO I2C");
285
286	return (rv);
287}
288
289static int
290gpioiic_attach(device_t dev)
291{
292	struct gpioiic_softc *sc = device_get_softc(dev);
293	int err;
294
295	sc->dev = dev;
296
297	/* Acquire our gpio pins. */
298	err = gpioiic_setup_hinted_pins(sc);
299#ifdef FDT
300	if (err != 0)
301		err = gpioiic_setup_fdt_pins(sc);
302#endif
303	if (err != 0) {
304		device_printf(sc->dev, "no pins configured\n");
305		gpioiic_cleanup(sc);
306		return (ENXIO);
307	}
308
309	/*
310	 * Say what we came up with for pin config.
311	 * NB: in the !FDT case the controller driver might not be set up enough
312	 * for GPIO_GET_BUS() to work.  Also, our parent is the only gpiobus
313	 * that can provide our pins.
314	 */
315	device_printf(dev, "SCL pin: %s:%d, SDA pin: %s:%d\n",
316#ifdef FDT
317	    device_get_nameunit(GPIO_GET_BUS(sc->sclpin->dev)), sc->sclpin->pin,
318	    device_get_nameunit(GPIO_GET_BUS(sc->sdapin->dev)), sc->sdapin->pin);
319#else
320	    device_get_nameunit(device_get_parent(dev)), sc->sclpin->pin,
321	    device_get_nameunit(device_get_parent(dev)), sc->sdapin->pin);
322#endif
323
324	/* Add the bitbang driver as our only child; it will add iicbus. */
325	device_add_child(sc->dev, "iicbb", -1);
326	return (bus_generic_attach(dev));
327}
328
329static int
330gpioiic_detach(device_t dev)
331{
332	struct gpioiic_softc *sc = device_get_softc(dev);
333	int err;
334
335	if ((err = bus_generic_detach(dev)) != 0)
336		return (err);
337
338	gpioiic_cleanup(sc);
339
340	return (0);
341}
342
343static devclass_t gpioiic_devclass;
344
345static device_method_t gpioiic_methods[] = {
346	/* Device interface */
347	DEVMETHOD(device_probe,		gpioiic_probe),
348	DEVMETHOD(device_attach,	gpioiic_attach),
349	DEVMETHOD(device_detach,	gpioiic_detach),
350
351	/* iicbb interface */
352	DEVMETHOD(iicbb_setsda,		gpioiic_setsda),
353	DEVMETHOD(iicbb_setscl,		gpioiic_setscl),
354	DEVMETHOD(iicbb_getsda,		gpioiic_getsda),
355	DEVMETHOD(iicbb_getscl,		gpioiic_getscl),
356	DEVMETHOD(iicbb_reset,		gpioiic_reset),
357
358#ifdef FDT
359	/* OFW bus interface */
360	DEVMETHOD(ofw_bus_get_node,	gpioiic_get_node),
361#endif
362
363	DEVMETHOD_END
364};
365
366static driver_t gpioiic_driver = {
367	"gpioiic",
368	gpioiic_methods,
369	sizeof(struct gpioiic_softc),
370};
371
372DRIVER_MODULE(gpioiic, gpiobus, gpioiic_driver, gpioiic_devclass, 0, 0);
373DRIVER_MODULE(gpioiic, simplebus, gpioiic_driver, gpioiic_devclass, 0, 0);
374DRIVER_MODULE(iicbb, gpioiic, iicbb_driver, iicbb_devclass, 0, 0);
375MODULE_DEPEND(gpioiic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
376MODULE_DEPEND(gpioiic, gpiobus, 1, 1, 1);
377