xref: /freebsd/sys/dev/gpio/gpioiic.c (revision 4133f23624058951a3b66e3ad735de980a485f36)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org>
5  * Copyright (c) 2010 Luiz Otavio O Souza
6  * All rights reserved.
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 #ifdef FDT
43 #include <dev/fdt/fdt_common.h>
44 #include <dev/ofw/ofw_bus.h>
45 #endif
46 
47 #include <dev/gpio/gpiobusvar.h>
48 #include <dev/iicbus/iiconf.h>
49 #include <dev/iicbus/iicbus.h>
50 
51 #include "gpiobus_if.h"
52 #include "iicbb_if.h"
53 
54 #define	GPIOIIC_SCL_DFLT	0
55 #define	GPIOIIC_SDA_DFLT	1
56 #define	GPIOIIC_MIN_PINS	2
57 
58 struct gpioiic_softc
59 {
60 	device_t	sc_dev;
61 	device_t	sc_busdev;
62 	int		scl_pin;
63 	int		sda_pin;
64 };
65 
66 static int gpioiic_probe(device_t);
67 static int gpioiic_attach(device_t);
68 
69 /* iicbb interface */
70 static void gpioiic_reset_bus(device_t);
71 static void gpioiic_setsda(device_t, int);
72 static void gpioiic_setscl(device_t, int);
73 static int gpioiic_getsda(device_t);
74 static int gpioiic_getscl(device_t);
75 static int gpioiic_reset(device_t, u_char, u_char, u_char *);
76 
77 static int
78 gpioiic_probe(device_t dev)
79 {
80 	struct gpiobus_ivar *devi;
81 
82 #ifdef FDT
83 	if (!ofw_bus_status_okay(dev))
84 		return (ENXIO);
85 	if (!ofw_bus_is_compatible(dev, "gpioiic"))
86 		return (ENXIO);
87 #endif
88 	devi = GPIOBUS_IVAR(dev);
89 	if (devi->npins < GPIOIIC_MIN_PINS) {
90 		device_printf(dev,
91 		    "gpioiic needs at least %d GPIO pins (only %d given).\n",
92 		    GPIOIIC_MIN_PINS, devi->npins);
93 		return (ENXIO);
94 	}
95 	device_set_desc(dev, "GPIO I2C bit-banging driver");
96 
97 	return (BUS_PROBE_DEFAULT);
98 }
99 
100 static int
101 gpioiic_attach(device_t dev)
102 {
103 	device_t		bitbang;
104 #ifdef FDT
105 	phandle_t		node;
106 	pcell_t			pin;
107 #endif
108 	struct gpiobus_ivar	*devi;
109 	struct gpioiic_softc	*sc;
110 
111 	sc = device_get_softc(dev);
112 	sc->sc_dev = dev;
113 	sc->sc_busdev = device_get_parent(dev);
114 	if (resource_int_value(device_get_name(dev),
115 		device_get_unit(dev), "scl", &sc->scl_pin))
116 		sc->scl_pin = GPIOIIC_SCL_DFLT;
117 	if (resource_int_value(device_get_name(dev),
118 		device_get_unit(dev), "sda", &sc->sda_pin))
119 		sc->sda_pin = GPIOIIC_SDA_DFLT;
120 
121 #ifdef FDT
122 	if ((node = ofw_bus_get_node(dev)) == -1)
123 		return (ENXIO);
124 	if (OF_getencprop(node, "scl", &pin, sizeof(pin)) > 0)
125 		sc->scl_pin = (int)pin;
126 	if (OF_getencprop(node, "sda", &pin, sizeof(pin)) > 0)
127 		sc->sda_pin = (int)pin;
128 #endif
129 
130 	if (sc->scl_pin < 0 || sc->scl_pin > 1)
131 		sc->scl_pin = GPIOIIC_SCL_DFLT;
132 	if (sc->sda_pin < 0 || sc->sda_pin > 1)
133 		sc->sda_pin = GPIOIIC_SDA_DFLT;
134 
135 	devi = GPIOBUS_IVAR(dev);
136 	device_printf(dev, "SCL pin: %d, SDA pin: %d\n",
137 	    devi->pins[sc->scl_pin], devi->pins[sc->sda_pin]);
138 
139 	/* add generic bit-banging code */
140 	bitbang = device_add_child(dev, "iicbb", -1);
141 	device_probe_and_attach(bitbang);
142 
143 	return (0);
144 }
145 
146 static int
147 gpioiic_detach(device_t dev)
148 {
149 
150 	bus_generic_detach(dev);
151 	device_delete_children(dev);
152 	return (0);
153 }
154 
155 /*
156  * Reset bus by setting SDA first and then SCL.
157  * Must always be called with gpio bus locked.
158  */
159 static void
160 gpioiic_reset_bus(device_t dev)
161 {
162 	struct gpioiic_softc		*sc = device_get_softc(dev);
163 
164 	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
165 	    GPIO_PIN_INPUT);
166 	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
167 	    GPIO_PIN_INPUT);
168 }
169 
170 static void
171 gpioiic_setpin(struct gpioiic_softc *sc, int pin, int val)
172 {
173 	int				err;
174 
175 	if (val == 0) {
176 		err = GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, pin, 0);
177 		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, pin,
178 		    GPIO_PIN_OUTPUT);
179 
180 		/*
181 		 * Some controllers cannot set output value while a pin is in
182 		 * input mode.
183 		 */
184 		if (err != 0)
185 			GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, pin, 0);
186 	} else {
187 		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, pin,
188 		    GPIO_PIN_INPUT);
189 	}
190 }
191 
192 static void
193 gpioiic_setsda(device_t dev, int val)
194 {
195 	struct gpioiic_softc		*sc = device_get_softc(dev);
196 
197 	gpioiic_setpin(sc, sc->sda_pin, val);
198 }
199 
200 static void
201 gpioiic_setscl(device_t dev, int val)
202 {
203 	struct gpioiic_softc		*sc = device_get_softc(dev);
204 
205 	gpioiic_setpin(sc, sc->scl_pin, val);
206 }
207 
208 static int
209 gpioiic_getpin(struct gpioiic_softc *sc, int pin)
210 {
211 	unsigned int			val;
212 
213 	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, pin, GPIO_PIN_INPUT);
214 	GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, pin, &val);
215 	return ((int)val);
216 }
217 
218 static int
219 gpioiic_getscl(device_t dev)
220 {
221 	struct gpioiic_softc		*sc = device_get_softc(dev);
222 
223 	return (gpioiic_getpin(sc, sc->scl_pin));
224 }
225 
226 static int
227 gpioiic_getsda(device_t dev)
228 {
229 	struct gpioiic_softc		*sc = device_get_softc(dev);
230 
231 	return (gpioiic_getpin(sc, sc->sda_pin));
232 }
233 
234 static int
235 gpioiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
236 {
237 	struct gpioiic_softc		*sc;
238 
239 	sc = device_get_softc(dev);
240 	gpioiic_reset_bus(sc->sc_dev);
241 
242 	return (IIC_ENOADDR);
243 }
244 
245 #ifdef FDT
246 static phandle_t
247 gpioiic_get_node(device_t bus, device_t dev)
248 {
249 
250 	/* We only have one child, the iicbb, which needs our own node. */
251 	return (ofw_bus_get_node(bus));
252 }
253 #endif
254 
255 static devclass_t gpioiic_devclass;
256 
257 static device_method_t gpioiic_methods[] = {
258 	/* Device interface */
259 	DEVMETHOD(device_probe,		gpioiic_probe),
260 	DEVMETHOD(device_attach,	gpioiic_attach),
261 	DEVMETHOD(device_detach,	gpioiic_detach),
262 
263 	/* iicbb interface */
264 	DEVMETHOD(iicbb_setsda,		gpioiic_setsda),
265 	DEVMETHOD(iicbb_setscl,		gpioiic_setscl),
266 	DEVMETHOD(iicbb_getsda,		gpioiic_getsda),
267 	DEVMETHOD(iicbb_getscl,		gpioiic_getscl),
268 	DEVMETHOD(iicbb_reset,		gpioiic_reset),
269 
270 #ifdef FDT
271 	/* OFW bus interface */
272 	DEVMETHOD(ofw_bus_get_node,	gpioiic_get_node),
273 #endif
274 
275 	DEVMETHOD_END
276 };
277 
278 static driver_t gpioiic_driver = {
279 	"gpioiic",
280 	gpioiic_methods,
281 	sizeof(struct gpioiic_softc),
282 };
283 
284 DRIVER_MODULE(gpioiic, gpiobus, gpioiic_driver, gpioiic_devclass, 0, 0);
285 DRIVER_MODULE(iicbb, gpioiic, iicbb_driver, iicbb_devclass, 0, 0);
286 MODULE_DEPEND(gpioiic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
287 MODULE_DEPEND(gpioiic, gpiobus, 1, 1, 1);
288