gpioiic.c revision 266105
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: stable/10/sys/dev/gpio/gpioiic.c 266105 2014-05-15 01:27:53Z 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/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
57struct gpioiic_softc
58{
59	device_t	sc_dev;
60	device_t	sc_busdev;
61	int		scl_pin;
62	int		sda_pin;
63};
64
65static int gpioiic_probe(device_t);
66static int gpioiic_attach(device_t);
67
68/* iicbb interface */
69static void gpioiic_reset_bus(device_t);
70static int gpioiic_callback(device_t, int, caddr_t);
71static void gpioiic_setsda(device_t, int);
72static void gpioiic_setscl(device_t, int);
73static int gpioiic_getsda(device_t);
74static int gpioiic_getscl(device_t);
75static int gpioiic_reset(device_t, u_char, u_char, u_char *);
76
77
78static int
79gpioiic_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
91static int
92gpioiic_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 */
130static void
131gpioiic_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
141static int
142gpioiic_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
165static void
166gpioiic_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
180static void
181gpioiic_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
195static int
196gpioiic_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
208static int
209gpioiic_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
221static int
222gpioiic_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
238static phandle_t
239gpioiic_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
247static devclass_t gpioiic_devclass;
248
249static 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
271static driver_t gpioiic_driver = {
272	"gpioiic",
273	gpioiic_methods,
274	sizeof(struct gpioiic_softc),
275};
276
277DRIVER_MODULE(gpioiic, gpiobus, gpioiic_driver, gpioiic_devclass, 0, 0);
278DRIVER_MODULE(iicbb, gpioiic, iicbb_driver, iicbb_devclass, 0, 0);
279MODULE_DEPEND(gpioiic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
280MODULE_DEPEND(gpioiic, gpiobus, 1, 1, 1);
281