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