gpioiic.c revision 266105
1213237Sgonzo/*-
2213237Sgonzo * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org>
3213237Sgonzo * Copyright (c) 2010 Luiz Otavio O Souza
4213237Sgonzo * All rights reserved.
5213237Sgonzo *
6213237Sgonzo * Redistribution and use in source and binary forms, with or without
7213237Sgonzo * modification, are permitted provided that the following conditions
8213237Sgonzo * are met:
9213237Sgonzo * 1. Redistributions of source code must retain the above copyright
10213237Sgonzo *    notice, this list of conditions and the following disclaimer.
11213237Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
12213237Sgonzo *    notice, this list of conditions and the following disclaimer in the
13213237Sgonzo *    documentation and/or other materials provided with the distribution.
14213237Sgonzo *
15213277Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16213277Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17213277Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18213277Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19213277Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20213277Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21213277Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22213277Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23213277Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24213277Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25213277Sgonzo * SUCH DAMAGE.
26213237Sgonzo */
27213237Sgonzo
28213237Sgonzo#include <sys/cdefs.h>
29213237Sgonzo__FBSDID("$FreeBSD: stable/10/sys/dev/gpio/gpioiic.c 266105 2014-05-15 01:27:53Z loos $");
30213237Sgonzo
31266105Sloos#include "opt_platform.h"
32266105Sloos
33213237Sgonzo#include <sys/param.h>
34213237Sgonzo#include <sys/systm.h>
35213237Sgonzo#include <sys/bus.h>
36213237Sgonzo#include <sys/conf.h>
37213237Sgonzo#include <sys/kernel.h>
38213237Sgonzo#include <sys/module.h>
39213237Sgonzo
40213237Sgonzo#include <sys/gpio.h>
41213237Sgonzo#include "gpiobus_if.h"
42213237Sgonzo
43266105Sloos#ifdef FDT
44266105Sloos#include <dev/ofw/ofw_bus.h>
45266105Sloos#include <dev/ofw/ofw_bus_subr.h>
46266105Sloos#include <dev/fdt/fdt_common.h>
47266105Sloos#endif
48266105Sloos
49213237Sgonzo#include <dev/iicbus/iiconf.h>
50213237Sgonzo#include <dev/iicbus/iicbus.h>
51213237Sgonzo
52213237Sgonzo#include "iicbb_if.h"
53213237Sgonzo
54228258Sadrian#define	SCL_PIN_DEFAULT	0	/* default index of SCL pin on gpiobus */
55228258Sadrian#define	SDA_PIN_DEFAULT	1
56213237Sgonzo
57213237Sgonzostruct gpioiic_softc
58213237Sgonzo{
59213237Sgonzo	device_t	sc_dev;
60213237Sgonzo	device_t	sc_busdev;
61228258Sadrian	int		scl_pin;
62228258Sadrian	int		sda_pin;
63213237Sgonzo};
64213237Sgonzo
65213237Sgonzostatic int gpioiic_probe(device_t);
66213237Sgonzostatic int gpioiic_attach(device_t);
67213237Sgonzo
68213237Sgonzo/* iicbb interface */
69213237Sgonzostatic void gpioiic_reset_bus(device_t);
70213237Sgonzostatic int gpioiic_callback(device_t, int, caddr_t);
71213237Sgonzostatic void gpioiic_setsda(device_t, int);
72213237Sgonzostatic void gpioiic_setscl(device_t, int);
73213237Sgonzostatic int gpioiic_getsda(device_t);
74213237Sgonzostatic int gpioiic_getscl(device_t);
75213237Sgonzostatic int gpioiic_reset(device_t, u_char, u_char, u_char *);
76213237Sgonzo
77213237Sgonzo
78213237Sgonzostatic int
79213237Sgonzogpioiic_probe(device_t dev)
80213237Sgonzo{
81213237Sgonzo
82266105Sloos#ifdef FDT
83266105Sloos	if (!ofw_bus_is_compatible(dev, "gpioiic"))
84266105Sloos		return (ENXIO);
85266105Sloos#endif
86213237Sgonzo	device_set_desc(dev, "GPIO I2C bit-banging driver");
87266105Sloos
88213237Sgonzo	return (0);
89213237Sgonzo}
90213237Sgonzo
91213237Sgonzostatic int
92213237Sgonzogpioiic_attach(device_t dev)
93213237Sgonzo{
94213237Sgonzo	struct gpioiic_softc	*sc = device_get_softc(dev);
95213237Sgonzo	device_t		bitbang;
96266105Sloos#ifdef FDT
97266105Sloos	phandle_t		node;
98266105Sloos	pcell_t			pin;
99266105Sloos#endif
100213237Sgonzo
101213237Sgonzo	sc->sc_dev = dev;
102213237Sgonzo	sc->sc_busdev = device_get_parent(dev);
103228258Sadrian	if (resource_int_value(device_get_name(dev),
104228258Sadrian		device_get_unit(dev), "scl", &sc->scl_pin))
105228258Sadrian		sc->scl_pin = SCL_PIN_DEFAULT;
106228258Sadrian	if (resource_int_value(device_get_name(dev),
107228258Sadrian		device_get_unit(dev), "sda", &sc->sda_pin))
108228258Sadrian		sc->sda_pin = SDA_PIN_DEFAULT;
109213237Sgonzo
110266105Sloos#ifdef FDT
111266105Sloos	if ((node = ofw_bus_get_node(dev)) == -1)
112266105Sloos		return (ENXIO);
113266105Sloos	if (OF_getencprop(node, "scl", &pin, sizeof(pin)) > 0)
114266105Sloos		sc->scl_pin = (int)pin;
115266105Sloos	if (OF_getencprop(node, "sda", &pin, sizeof(pin)) > 0)
116266105Sloos		sc->sda_pin = (int)pin;
117266105Sloos#endif
118266105Sloos
119213237Sgonzo	/* add generic bit-banging code */
120213237Sgonzo	bitbang = device_add_child(dev, "iicbb", -1);
121213237Sgonzo	device_probe_and_attach(bitbang);
122213237Sgonzo
123213237Sgonzo	return (0);
124213237Sgonzo}
125213237Sgonzo
126213237Sgonzo/*
127213237Sgonzo * Reset bus by setting SDA first and then SCL.
128213237Sgonzo * Must always be called with gpio bus locked.
129213237Sgonzo */
130213237Sgonzostatic void
131213237Sgonzogpioiic_reset_bus(device_t dev)
132213237Sgonzo{
133213237Sgonzo	struct gpioiic_softc		*sc = device_get_softc(dev);
134213237Sgonzo
135228258Sadrian	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
136213237Sgonzo	    GPIO_PIN_INPUT);
137228258Sadrian	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
138213237Sgonzo	    GPIO_PIN_INPUT);
139213237Sgonzo}
140213237Sgonzo
141213237Sgonzostatic int
142213237Sgonzogpioiic_callback(device_t dev, int index, caddr_t data)
143213237Sgonzo{
144213237Sgonzo	struct gpioiic_softc	*sc = device_get_softc(dev);
145213237Sgonzo	int			error = 0;
146213237Sgonzo
147213237Sgonzo	switch (index) {
148213237Sgonzo	case IIC_REQUEST_BUS:
149213237Sgonzo		GPIOBUS_LOCK_BUS(sc->sc_busdev);
150213237Sgonzo		GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev);
151213237Sgonzo		GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
152213237Sgonzo		break;
153213237Sgonzo	case IIC_RELEASE_BUS:
154213237Sgonzo		GPIOBUS_LOCK_BUS(sc->sc_busdev);
155213237Sgonzo		GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev);
156213237Sgonzo		GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
157213237Sgonzo		break;
158213237Sgonzo	default:
159213237Sgonzo		error = EINVAL;
160213237Sgonzo	}
161213237Sgonzo
162266105Sloos	return (error);
163213237Sgonzo}
164213237Sgonzo
165213237Sgonzostatic void
166213237Sgonzogpioiic_setsda(device_t dev, int val)
167213237Sgonzo{
168213237Sgonzo	struct gpioiic_softc		*sc = device_get_softc(dev);
169213237Sgonzo
170213237Sgonzo	if (val == 0) {
171228258Sadrian		GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sda_pin, 0);
172228258Sadrian		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
173213237Sgonzo		    GPIO_PIN_OUTPUT);
174213237Sgonzo	} else {
175228258Sadrian		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
176213237Sgonzo		    GPIO_PIN_INPUT);
177213237Sgonzo	}
178213237Sgonzo}
179213237Sgonzo
180213237Sgonzostatic void
181213237Sgonzogpioiic_setscl(device_t dev, int val)
182213237Sgonzo{
183213237Sgonzo	struct gpioiic_softc		*sc = device_get_softc(dev);
184213237Sgonzo
185213237Sgonzo	if (val == 0) {
186228258Sadrian		GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->scl_pin, 0);
187228258Sadrian		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
188213237Sgonzo		    GPIO_PIN_OUTPUT);
189213237Sgonzo	} else {
190228258Sadrian		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
191213237Sgonzo		    GPIO_PIN_INPUT);
192213237Sgonzo	}
193213237Sgonzo}
194213237Sgonzo
195213237Sgonzostatic int
196213237Sgonzogpioiic_getscl(device_t dev)
197213237Sgonzo{
198213237Sgonzo	struct gpioiic_softc		*sc = device_get_softc(dev);
199213237Sgonzo	unsigned int			val;
200213237Sgonzo
201228258Sadrian	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
202213237Sgonzo	    GPIO_PIN_INPUT);
203228258Sadrian	GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->scl_pin, &val);
204213237Sgonzo
205213237Sgonzo	return ((int)val);
206213237Sgonzo}
207213237Sgonzo
208213237Sgonzostatic int
209213237Sgonzogpioiic_getsda(device_t dev)
210213237Sgonzo{
211213237Sgonzo	struct gpioiic_softc		*sc = device_get_softc(dev);
212213237Sgonzo	unsigned int			val;
213213237Sgonzo
214228258Sadrian	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
215213237Sgonzo	    GPIO_PIN_INPUT);
216228258Sadrian	GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->sda_pin, &val);
217213237Sgonzo
218213237Sgonzo	return ((int)val);
219213237Sgonzo}
220213237Sgonzo
221213237Sgonzostatic int
222213237Sgonzogpioiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
223213237Sgonzo{
224213237Sgonzo	struct gpioiic_softc		*sc = device_get_softc(dev);
225213237Sgonzo
226228729Sadrian	GPIOBUS_LOCK_BUS(sc->sc_busdev);
227213237Sgonzo	GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev);
228213237Sgonzo
229213237Sgonzo	gpioiic_reset_bus(sc->sc_dev);
230213237Sgonzo
231213237Sgonzo	GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev);
232228729Sadrian	GPIOBUS_UNLOCK_BUS(sc->sc_busdev);
233213237Sgonzo
234213237Sgonzo	return (IIC_ENOADDR);
235213237Sgonzo}
236213237Sgonzo
237266105Sloos#ifdef FDT
238266105Sloosstatic phandle_t
239266105Sloosgpioiic_get_node(device_t bus, device_t dev)
240266105Sloos{
241266105Sloos
242266105Sloos	/* We only have one child, the iicbb, which needs our own node. */
243266105Sloos	return (ofw_bus_get_node(bus));
244266105Sloos}
245266105Sloos#endif
246266105Sloos
247213237Sgonzostatic devclass_t gpioiic_devclass;
248213237Sgonzo
249213237Sgonzostatic device_method_t gpioiic_methods[] = {
250213237Sgonzo	/* Device interface */
251213237Sgonzo	DEVMETHOD(device_probe,		gpioiic_probe),
252213237Sgonzo	DEVMETHOD(device_attach,	gpioiic_attach),
253213237Sgonzo	DEVMETHOD(device_detach,	bus_generic_detach),
254213237Sgonzo
255213237Sgonzo	/* iicbb interface */
256213237Sgonzo	DEVMETHOD(iicbb_callback,	gpioiic_callback),
257213237Sgonzo	DEVMETHOD(iicbb_setsda,		gpioiic_setsda),
258213237Sgonzo	DEVMETHOD(iicbb_setscl,		gpioiic_setscl),
259213237Sgonzo	DEVMETHOD(iicbb_getsda,		gpioiic_getsda),
260213237Sgonzo	DEVMETHOD(iicbb_getscl,		gpioiic_getscl),
261213237Sgonzo	DEVMETHOD(iicbb_reset,		gpioiic_reset),
262213237Sgonzo
263266105Sloos#ifdef FDT
264266105Sloos	/* OFW bus interface */
265266105Sloos	DEVMETHOD(ofw_bus_get_node,	gpioiic_get_node),
266266105Sloos#endif
267266105Sloos
268213237Sgonzo	{ 0, 0 }
269213237Sgonzo};
270213237Sgonzo
271213237Sgonzostatic driver_t gpioiic_driver = {
272213237Sgonzo	"gpioiic",
273213237Sgonzo	gpioiic_methods,
274213237Sgonzo	sizeof(struct gpioiic_softc),
275213237Sgonzo};
276213237Sgonzo
277213237SgonzoDRIVER_MODULE(gpioiic, gpiobus, gpioiic_driver, gpioiic_devclass, 0, 0);
278213237SgonzoDRIVER_MODULE(iicbb, gpioiic, iicbb_driver, iicbb_devclass, 0, 0);
279213237SgonzoMODULE_DEPEND(gpioiic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
280213237SgonzoMODULE_DEPEND(gpioiic, gpiobus, 1, 1, 1);
281