xref: /freebsd/sys/dev/gpio/gpioiic.c (revision f7167e0ea0bf5aaabff9490453b3b71b3f1b4d51)
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 <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/bus.h>
34 #include <sys/conf.h>
35 #include <sys/kernel.h>
36 #include <sys/module.h>
37 
38 #include <sys/gpio.h>
39 #include "gpiobus_if.h"
40 
41 #include <dev/iicbus/iiconf.h>
42 #include <dev/iicbus/iicbus.h>
43 
44 #include "iicbb_if.h"
45 
46 #define	SCL_PIN_DEFAULT	0	/* default index of SCL pin on gpiobus */
47 #define	SDA_PIN_DEFAULT	1
48 
49 struct gpioiic_softc
50 {
51 	device_t	sc_dev;
52 	device_t	sc_busdev;
53 	int		scl_pin;
54 	int		sda_pin;
55 };
56 
57 static int gpioiic_probe(device_t);
58 static int gpioiic_attach(device_t);
59 
60 /* iicbb interface */
61 static void gpioiic_reset_bus(device_t);
62 static int gpioiic_callback(device_t, int, caddr_t);
63 static void gpioiic_setsda(device_t, int);
64 static void gpioiic_setscl(device_t, int);
65 static int gpioiic_getsda(device_t);
66 static int gpioiic_getscl(device_t);
67 static int gpioiic_reset(device_t, u_char, u_char, u_char *);
68 
69 
70 static int
71 gpioiic_probe(device_t dev)
72 {
73 
74 	device_set_desc(dev, "GPIO I2C bit-banging driver");
75 
76 	return (0);
77 }
78 
79 static int
80 gpioiic_attach(device_t dev)
81 {
82 	struct gpioiic_softc	*sc = device_get_softc(dev);
83 	device_t		bitbang;
84 
85 	sc->sc_dev = dev;
86 	sc->sc_busdev = device_get_parent(dev);
87 	if (resource_int_value(device_get_name(dev),
88 		device_get_unit(dev), "scl", &sc->scl_pin))
89 		sc->scl_pin = SCL_PIN_DEFAULT;
90 	if (resource_int_value(device_get_name(dev),
91 		device_get_unit(dev), "sda", &sc->sda_pin))
92 		sc->sda_pin = SDA_PIN_DEFAULT;
93 
94 	/* add generic bit-banging code */
95 	bitbang = device_add_child(dev, "iicbb", -1);
96 	device_probe_and_attach(bitbang);
97 
98 	return (0);
99 }
100 
101 /*
102  * Reset bus by setting SDA first and then SCL.
103  * Must always be called with gpio bus locked.
104  */
105 static void
106 gpioiic_reset_bus(device_t dev)
107 {
108 	struct gpioiic_softc		*sc = device_get_softc(dev);
109 
110 	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
111 	    GPIO_PIN_INPUT);
112 	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
113 	    GPIO_PIN_INPUT);
114 }
115 
116 static int
117 gpioiic_callback(device_t dev, int index, caddr_t data)
118 {
119 	struct gpioiic_softc	*sc = device_get_softc(dev);
120 	int			error = 0;
121 
122 	switch (index) {
123 	case IIC_REQUEST_BUS:
124 		GPIOBUS_LOCK_BUS(sc->sc_busdev);
125 		GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev);
126 		GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
127 		break;
128 	case IIC_RELEASE_BUS:
129 		GPIOBUS_LOCK_BUS(sc->sc_busdev);
130 		GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev);
131 		GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
132 		break;
133 	default:
134 		error = EINVAL;
135 	}
136 
137 	return (error);
138 }
139 
140 static void
141 gpioiic_setsda(device_t dev, int val)
142 {
143 	struct gpioiic_softc		*sc = device_get_softc(dev);
144 
145 	if (val == 0) {
146 		GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sda_pin, 0);
147 		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
148 		    GPIO_PIN_OUTPUT);
149 	} else {
150 		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
151 		    GPIO_PIN_INPUT);
152 	}
153 }
154 
155 static void
156 gpioiic_setscl(device_t dev, int val)
157 {
158 	struct gpioiic_softc		*sc = device_get_softc(dev);
159 
160 	if (val == 0) {
161 		GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->scl_pin, 0);
162 		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
163 		    GPIO_PIN_OUTPUT);
164 	} else {
165 		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
166 		    GPIO_PIN_INPUT);
167 	}
168 }
169 
170 static int
171 gpioiic_getscl(device_t dev)
172 {
173 	struct gpioiic_softc		*sc = device_get_softc(dev);
174 	unsigned int			val;
175 
176 	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
177 	    GPIO_PIN_INPUT);
178 	GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->scl_pin, &val);
179 
180 	return ((int)val);
181 }
182 
183 static int
184 gpioiic_getsda(device_t dev)
185 {
186 	struct gpioiic_softc		*sc = device_get_softc(dev);
187 	unsigned int			val;
188 
189 	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
190 	    GPIO_PIN_INPUT);
191 	GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->sda_pin, &val);
192 
193 	return ((int)val);
194 }
195 
196 static int
197 gpioiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
198 {
199 	struct gpioiic_softc		*sc = device_get_softc(dev);
200 
201 	GPIOBUS_LOCK_BUS(sc->sc_busdev);
202 	GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev);
203 
204 	gpioiic_reset_bus(sc->sc_dev);
205 
206 	GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev);
207 	GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
208 
209 	return (IIC_ENOADDR);
210 }
211 
212 static devclass_t gpioiic_devclass;
213 
214 static device_method_t gpioiic_methods[] = {
215 	/* Device interface */
216 	DEVMETHOD(device_probe,		gpioiic_probe),
217 	DEVMETHOD(device_attach,	gpioiic_attach),
218 	DEVMETHOD(device_detach,	bus_generic_detach),
219 
220 	/* iicbb interface */
221 	DEVMETHOD(iicbb_callback,	gpioiic_callback),
222 	DEVMETHOD(iicbb_setsda,		gpioiic_setsda),
223 	DEVMETHOD(iicbb_setscl,		gpioiic_setscl),
224 	DEVMETHOD(iicbb_getsda,		gpioiic_getsda),
225 	DEVMETHOD(iicbb_getscl,		gpioiic_getscl),
226 	DEVMETHOD(iicbb_reset,		gpioiic_reset),
227 
228 	{ 0, 0 }
229 };
230 
231 static driver_t gpioiic_driver = {
232 	"gpioiic",
233 	gpioiic_methods,
234 	sizeof(struct gpioiic_softc),
235 };
236 
237 DRIVER_MODULE(gpioiic, gpiobus, gpioiic_driver, gpioiic_devclass, 0, 0);
238 DRIVER_MODULE(iicbb, gpioiic, iicbb_driver, iicbb_devclass, 0, 0);
239 MODULE_DEPEND(gpioiic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
240 MODULE_DEPEND(gpioiic, gpiobus, 1, 1, 1);
241