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