gpioiic.c revision 213237
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 ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include <sys/cdefs.h>
28__FBSDID("$FreeBSD: head/sys/dev/gpio/gpioiic.c 213237 2010-09-28 03:24:53Z gonzo $");
29
30#include <sys/param.h>
31#include <sys/systm.h>
32#include <sys/bio.h>
33#include <sys/bus.h>
34#include <sys/conf.h>
35#include <sys/kernel.h>
36#include <sys/kthread.h>
37#include <sys/lock.h>
38#include <sys/malloc.h>
39#include <sys/module.h>
40#include <sys/mutex.h>
41
42#include <sys/gpio.h>
43#include "gpiobus_if.h"
44
45#include <dev/iicbus/iiconf.h>
46#include <dev/iicbus/iicbus.h>
47
48#include "iicbb_if.h"
49
50#define	SCL_PIN		0	/* gpiobus mapped pin 6 */
51#define	SDA_PIN		1	/* gpiobus mapped pin 7 */
52
53struct gpioiic_softc
54{
55	device_t	sc_dev;
56	device_t	sc_busdev;
57	struct mtx	sc_mtx;
58	struct cdev	*sc_leddev;
59};
60
61static int gpioiic_probe(device_t);
62static int gpioiic_attach(device_t);
63
64/* iicbb interface */
65static void gpioiic_reset_bus(device_t);
66static int gpioiic_callback(device_t, int, caddr_t);
67static void gpioiic_setsda(device_t, int);
68static void gpioiic_setscl(device_t, int);
69static int gpioiic_getsda(device_t);
70static int gpioiic_getscl(device_t);
71static int gpioiic_reset(device_t, u_char, u_char, u_char *);
72
73
74static int
75gpioiic_probe(device_t dev)
76{
77
78	device_set_desc(dev, "GPIO I2C bit-banging driver");
79	return (0);
80}
81
82static int
83gpioiic_attach(device_t dev)
84{
85	struct gpioiic_softc	*sc = device_get_softc(dev);
86	device_t		bitbang;
87
88	sc->sc_dev = dev;
89	sc->sc_busdev = device_get_parent(dev);
90
91	/* add generic bit-banging code */
92	bitbang = device_add_child(dev, "iicbb", -1);
93	device_probe_and_attach(bitbang);
94
95	return (0);
96}
97
98/*
99 * Reset bus by setting SDA first and then SCL.
100 * Must always be called with gpio bus locked.
101 */
102static void
103gpioiic_reset_bus(device_t dev)
104{
105	struct gpioiic_softc		*sc = device_get_softc(dev);
106
107	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, SDA_PIN,
108	    GPIO_PIN_INPUT);
109	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, SCL_PIN,
110	    GPIO_PIN_INPUT);
111}
112
113static int
114gpioiic_callback(device_t dev, int index, caddr_t data)
115{
116	struct gpioiic_softc	*sc = device_get_softc(dev);
117	int			error = 0;
118
119	switch (index) {
120	case IIC_REQUEST_BUS:
121		GPIOBUS_LOCK_BUS(sc->sc_busdev);
122		GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev);
123		GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
124		break;
125	case IIC_RELEASE_BUS:
126		GPIOBUS_LOCK_BUS(sc->sc_busdev);
127		GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev);
128		GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
129		break;
130	default:
131		error = EINVAL;
132	}
133
134	return(error);
135}
136
137static void
138gpioiic_setsda(device_t dev, int val)
139{
140	struct gpioiic_softc		*sc = device_get_softc(dev);
141
142	GPIOBUS_LOCK_BUS(sc->sc_busdev);
143	if (val == 0) {
144		GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, SDA_PIN, 0);
145		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, SDA_PIN,
146		    GPIO_PIN_OUTPUT);
147	} else {
148		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, SDA_PIN,
149		    GPIO_PIN_INPUT);
150	}
151	GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
152}
153
154static void
155gpioiic_setscl(device_t dev, int val)
156{
157	struct gpioiic_softc		*sc = device_get_softc(dev);
158
159	GPIOBUS_LOCK_BUS(sc->sc_busdev);
160	if (val == 0) {
161		GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, SCL_PIN, 0);
162		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, SCL_PIN,
163		    GPIO_PIN_OUTPUT);
164	} else {
165		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, SCL_PIN,
166		    GPIO_PIN_INPUT);
167	}
168	GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
169}
170
171static int
172gpioiic_getscl(device_t dev)
173{
174	struct gpioiic_softc		*sc = device_get_softc(dev);
175	unsigned int			val;
176
177	GPIOBUS_LOCK_BUS(sc->sc_busdev);
178	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, SCL_PIN,
179	    GPIO_PIN_INPUT);
180	GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, SCL_PIN, &val);
181	GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
182
183	return ((int)val);
184}
185
186static int
187gpioiic_getsda(device_t dev)
188{
189	struct gpioiic_softc		*sc = device_get_softc(dev);
190	unsigned int			val;
191
192	GPIOBUS_LOCK_BUS(sc->sc_busdev);
193	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, SDA_PIN,
194	    GPIO_PIN_INPUT);
195	GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, SDA_PIN, &val);
196	GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
197
198	return ((int)val);
199}
200
201static int
202gpioiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
203{
204	struct gpioiic_softc		*sc = device_get_softc(dev);
205
206	GPIOBUS_LOCK_BUS(sc->sc_busdev);
207	GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev);
208
209	gpioiic_reset_bus(sc->sc_dev);
210
211	GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev);
212	GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
213
214	return (IIC_ENOADDR);
215}
216
217static devclass_t gpioiic_devclass;
218
219static device_method_t gpioiic_methods[] = {
220	/* Device interface */
221	DEVMETHOD(device_probe,		gpioiic_probe),
222	DEVMETHOD(device_attach,	gpioiic_attach),
223	DEVMETHOD(device_detach,	bus_generic_detach),
224
225	/* iicbb interface */
226	DEVMETHOD(iicbb_callback,	gpioiic_callback),
227	DEVMETHOD(iicbb_setsda,		gpioiic_setsda),
228	DEVMETHOD(iicbb_setscl,		gpioiic_setscl),
229	DEVMETHOD(iicbb_getsda,		gpioiic_getsda),
230	DEVMETHOD(iicbb_getscl,		gpioiic_getscl),
231	DEVMETHOD(iicbb_reset,		gpioiic_reset),
232
233	{ 0, 0 }
234};
235
236static driver_t gpioiic_driver = {
237	"gpioiic",
238	gpioiic_methods,
239	sizeof(struct gpioiic_softc),
240};
241
242DRIVER_MODULE(gpioiic, gpiobus, gpioiic_driver, gpioiic_devclass, 0, 0);
243DRIVER_MODULE(iicbb, gpioiic, iicbb_driver, iicbb_devclass, 0, 0);
244MODULE_DEPEND(gpioiic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
245MODULE_DEPEND(gpioiic, gpiobus, 1, 1, 1);
246