xref: /freebsd/sys/dev/gpio/gpioiic.c (revision 0957b409)
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 /*
147  * Reset bus by setting SDA first and then SCL.
148  * Must always be called with gpio bus locked.
149  */
150 static void
151 gpioiic_reset_bus(device_t dev)
152 {
153 	struct gpioiic_softc		*sc = device_get_softc(dev);
154 
155 	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
156 	    GPIO_PIN_INPUT);
157 	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
158 	    GPIO_PIN_INPUT);
159 }
160 
161 static void
162 gpioiic_setsda(device_t dev, int val)
163 {
164 	struct gpioiic_softc		*sc = device_get_softc(dev);
165 
166 	if (val == 0) {
167 		GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sda_pin, 0);
168 		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
169 		    GPIO_PIN_OUTPUT);
170 	} else {
171 		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
172 		    GPIO_PIN_INPUT);
173 	}
174 }
175 
176 static void
177 gpioiic_setscl(device_t dev, int val)
178 {
179 	struct gpioiic_softc		*sc = device_get_softc(dev);
180 
181 	if (val == 0) {
182 		GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->scl_pin, 0);
183 		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
184 		    GPIO_PIN_OUTPUT);
185 	} else {
186 		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
187 		    GPIO_PIN_INPUT);
188 	}
189 }
190 
191 static int
192 gpioiic_getscl(device_t dev)
193 {
194 	struct gpioiic_softc		*sc = device_get_softc(dev);
195 	unsigned int			val;
196 
197 	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
198 	    GPIO_PIN_INPUT);
199 	GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->scl_pin, &val);
200 
201 	return ((int)val);
202 }
203 
204 static int
205 gpioiic_getsda(device_t dev)
206 {
207 	struct gpioiic_softc		*sc = device_get_softc(dev);
208 	unsigned int			val;
209 
210 	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
211 	    GPIO_PIN_INPUT);
212 	GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->sda_pin, &val);
213 
214 	return ((int)val);
215 }
216 
217 static int
218 gpioiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
219 {
220 	struct gpioiic_softc		*sc;
221 
222 	sc = device_get_softc(dev);
223 	gpioiic_reset_bus(sc->sc_dev);
224 
225 	return (IIC_ENOADDR);
226 }
227 
228 #ifdef FDT
229 static phandle_t
230 gpioiic_get_node(device_t bus, device_t dev)
231 {
232 
233 	/* We only have one child, the iicbb, which needs our own node. */
234 	return (ofw_bus_get_node(bus));
235 }
236 #endif
237 
238 static devclass_t gpioiic_devclass;
239 
240 static device_method_t gpioiic_methods[] = {
241 	/* Device interface */
242 	DEVMETHOD(device_probe,		gpioiic_probe),
243 	DEVMETHOD(device_attach,	gpioiic_attach),
244 	DEVMETHOD(device_detach,	bus_generic_detach),
245 
246 	/* iicbb interface */
247 	DEVMETHOD(iicbb_setsda,		gpioiic_setsda),
248 	DEVMETHOD(iicbb_setscl,		gpioiic_setscl),
249 	DEVMETHOD(iicbb_getsda,		gpioiic_getsda),
250 	DEVMETHOD(iicbb_getscl,		gpioiic_getscl),
251 	DEVMETHOD(iicbb_reset,		gpioiic_reset),
252 
253 #ifdef FDT
254 	/* OFW bus interface */
255 	DEVMETHOD(ofw_bus_get_node,	gpioiic_get_node),
256 #endif
257 
258 	DEVMETHOD_END
259 };
260 
261 static driver_t gpioiic_driver = {
262 	"gpioiic",
263 	gpioiic_methods,
264 	sizeof(struct gpioiic_softc),
265 };
266 
267 DRIVER_MODULE(gpioiic, gpiobus, gpioiic_driver, gpioiic_devclass, 0, 0);
268 DRIVER_MODULE(iicbb, gpioiic, iicbb_driver, iicbb_devclass, 0, 0);
269 MODULE_DEPEND(gpioiic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
270 MODULE_DEPEND(gpioiic, gpiobus, 1, 1, 1);
271