gpioiic.c revision 273917
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: head/sys/dev/gpio/gpioiic.c 273917 2014-10-31 19:15:14Z loos $");
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/gpio/gpiobusvar.h>
50
51#include <dev/iicbus/iiconf.h>
52#include <dev/iicbus/iicbus.h>
53
54#include "iicbb_if.h"
55
56#define	SCL_PIN_DEFAULT	0	/* default index of SCL pin on gpiobus */
57#define	SDA_PIN_DEFAULT	1
58
59struct gpioiic_softc
60{
61	device_t	sc_dev;
62	device_t	sc_busdev;
63	int		scl_pin;
64	int		sda_pin;
65};
66
67static int gpioiic_probe(device_t);
68static int gpioiic_attach(device_t);
69
70/* iicbb interface */
71static void gpioiic_reset_bus(device_t);
72static int gpioiic_callback(device_t, int, caddr_t);
73static void gpioiic_setsda(device_t, int);
74static void gpioiic_setscl(device_t, int);
75static int gpioiic_getsda(device_t);
76static int gpioiic_getscl(device_t);
77static int gpioiic_reset(device_t, u_char, u_char, u_char *);
78
79static int
80gpioiic_probe(device_t dev)
81{
82
83#ifdef FDT
84	if (!ofw_bus_is_compatible(dev, "gpioiic"))
85		return (ENXIO);
86#endif
87	device_set_desc(dev, "GPIO I2C bit-banging driver");
88
89	return (0);
90}
91
92static int
93gpioiic_attach(device_t dev)
94{
95	device_t		bitbang;
96#ifdef FDT
97	phandle_t		node;
98	pcell_t			pin;
99#endif
100	struct gpiobus_ivar	*devi;
101	struct gpioiic_softc	*sc;
102
103	sc = device_get_softc(dev);
104	sc->sc_dev = dev;
105	sc->sc_busdev = device_get_parent(dev);
106	if (resource_int_value(device_get_name(dev),
107		device_get_unit(dev), "scl", &sc->scl_pin))
108		sc->scl_pin = SCL_PIN_DEFAULT;
109	if (resource_int_value(device_get_name(dev),
110		device_get_unit(dev), "sda", &sc->sda_pin))
111		sc->sda_pin = SDA_PIN_DEFAULT;
112
113#ifdef FDT
114	if ((node = ofw_bus_get_node(dev)) == -1)
115		return (ENXIO);
116	if (OF_getencprop(node, "scl", &pin, sizeof(pin)) > 0)
117		sc->scl_pin = (int)pin;
118	if (OF_getencprop(node, "sda", &pin, sizeof(pin)) > 0)
119		sc->sda_pin = (int)pin;
120#endif
121
122	if (sc->scl_pin < 0 || sc->scl_pin > 1)
123		sc->scl_pin = SCL_PIN_DEFAULT;
124	if (sc->sda_pin < 0 || sc->sda_pin > 1)
125		sc->sda_pin = SDA_PIN_DEFAULT;
126
127	devi = GPIOBUS_IVAR(dev);
128	device_printf(dev, "SCL pin: %d, SDA pin: %d\n",
129	    devi->pins[sc->scl_pin], devi->pins[sc->sda_pin]);
130
131	/* add generic bit-banging code */
132	bitbang = device_add_child(dev, "iicbb", -1);
133	device_probe_and_attach(bitbang);
134
135	return (0);
136}
137
138/*
139 * Reset bus by setting SDA first and then SCL.
140 * Must always be called with gpio bus locked.
141 */
142static void
143gpioiic_reset_bus(device_t dev)
144{
145	struct gpioiic_softc		*sc = device_get_softc(dev);
146
147	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
148	    GPIO_PIN_INPUT);
149	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
150	    GPIO_PIN_INPUT);
151}
152
153static int
154gpioiic_callback(device_t dev, int index, caddr_t data)
155{
156	struct gpioiic_softc	*sc = device_get_softc(dev);
157	int error, how;
158
159	how = GPIOBUS_DONTWAIT;
160	if (data != NULL && (int)*data == IIC_WAIT)
161		how = GPIOBUS_WAIT;
162	error = 0;
163	switch (index) {
164	case IIC_REQUEST_BUS:
165		error = GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev, how);
166		break;
167	case IIC_RELEASE_BUS:
168		GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev);
169		break;
170	default:
171		error = EINVAL;
172	}
173
174	return (error);
175}
176
177static void
178gpioiic_setsda(device_t dev, int val)
179{
180	struct gpioiic_softc		*sc = device_get_softc(dev);
181
182	if (val == 0) {
183		GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sda_pin, 0);
184		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
185		    GPIO_PIN_OUTPUT);
186	} else {
187		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
188		    GPIO_PIN_INPUT);
189	}
190}
191
192static void
193gpioiic_setscl(device_t dev, int val)
194{
195	struct gpioiic_softc		*sc = device_get_softc(dev);
196
197	if (val == 0) {
198		GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->scl_pin, 0);
199		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
200		    GPIO_PIN_OUTPUT);
201	} else {
202		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
203		    GPIO_PIN_INPUT);
204	}
205}
206
207static int
208gpioiic_getscl(device_t dev)
209{
210	struct gpioiic_softc		*sc = device_get_softc(dev);
211	unsigned int			val;
212
213	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
214	    GPIO_PIN_INPUT);
215	GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->scl_pin, &val);
216
217	return ((int)val);
218}
219
220static int
221gpioiic_getsda(device_t dev)
222{
223	struct gpioiic_softc		*sc = device_get_softc(dev);
224	unsigned int			val;
225
226	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
227	    GPIO_PIN_INPUT);
228	GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->sda_pin, &val);
229
230	return ((int)val);
231}
232
233static int
234gpioiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
235{
236	struct gpioiic_softc		*sc;
237
238	sc = device_get_softc(dev);
239	gpioiic_reset_bus(sc->sc_dev);
240
241	return (IIC_ENOADDR);
242}
243
244#ifdef FDT
245static phandle_t
246gpioiic_get_node(device_t bus, device_t dev)
247{
248
249	/* We only have one child, the iicbb, which needs our own node. */
250	return (ofw_bus_get_node(bus));
251}
252#endif
253
254static devclass_t gpioiic_devclass;
255
256static device_method_t gpioiic_methods[] = {
257	/* Device interface */
258	DEVMETHOD(device_probe,		gpioiic_probe),
259	DEVMETHOD(device_attach,	gpioiic_attach),
260	DEVMETHOD(device_detach,	bus_generic_detach),
261
262	/* iicbb interface */
263	DEVMETHOD(iicbb_callback,	gpioiic_callback),
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	{ 0, 0 }
276};
277
278static driver_t gpioiic_driver = {
279	"gpioiic",
280	gpioiic_methods,
281	sizeof(struct gpioiic_softc),
282};
283
284DRIVER_MODULE(gpioiic, gpiobus, gpioiic_driver, gpioiic_devclass, 0, 0);
285DRIVER_MODULE(iicbb, gpioiic, iicbb_driver, iicbb_devclass, 0, 0);
286MODULE_DEPEND(gpioiic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
287MODULE_DEPEND(gpioiic, gpiobus, 1, 1, 1);
288