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: releng/10.2/sys/dev/gpio/gpioiic.c 278783 2015-02-14 20:50:38Z 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
49270236Sloos#include <dev/gpio/gpiobusvar.h>
50270236Sloos
51213237Sgonzo#include <dev/iicbus/iiconf.h>
52213237Sgonzo#include <dev/iicbus/iicbus.h>
53213237Sgonzo
54213237Sgonzo#include "iicbb_if.h"
55213237Sgonzo
56228258Sadrian#define	SCL_PIN_DEFAULT	0	/* default index of SCL pin on gpiobus */
57228258Sadrian#define	SDA_PIN_DEFAULT	1
58213237Sgonzo
59213237Sgonzostruct gpioiic_softc
60213237Sgonzo{
61213237Sgonzo	device_t	sc_dev;
62213237Sgonzo	device_t	sc_busdev;
63228258Sadrian	int		scl_pin;
64228258Sadrian	int		sda_pin;
65213237Sgonzo};
66213237Sgonzo
67213237Sgonzostatic int gpioiic_probe(device_t);
68213237Sgonzostatic int gpioiic_attach(device_t);
69213237Sgonzo
70213237Sgonzo/* iicbb interface */
71213237Sgonzostatic void gpioiic_reset_bus(device_t);
72213237Sgonzostatic int gpioiic_callback(device_t, int, caddr_t);
73213237Sgonzostatic void gpioiic_setsda(device_t, int);
74213237Sgonzostatic void gpioiic_setscl(device_t, int);
75213237Sgonzostatic int gpioiic_getsda(device_t);
76213237Sgonzostatic int gpioiic_getscl(device_t);
77213237Sgonzostatic int gpioiic_reset(device_t, u_char, u_char, u_char *);
78213237Sgonzo
79213237Sgonzostatic int
80213237Sgonzogpioiic_probe(device_t dev)
81213237Sgonzo{
82213237Sgonzo
83266105Sloos#ifdef FDT
84266105Sloos	if (!ofw_bus_is_compatible(dev, "gpioiic"))
85266105Sloos		return (ENXIO);
86266105Sloos#endif
87213237Sgonzo	device_set_desc(dev, "GPIO I2C bit-banging driver");
88266105Sloos
89213237Sgonzo	return (0);
90213237Sgonzo}
91213237Sgonzo
92213237Sgonzostatic int
93213237Sgonzogpioiic_attach(device_t dev)
94213237Sgonzo{
95213237Sgonzo	device_t		bitbang;
96266105Sloos#ifdef FDT
97266105Sloos	phandle_t		node;
98266105Sloos	pcell_t			pin;
99266105Sloos#endif
100270236Sloos	struct gpiobus_ivar	*devi;
101270236Sloos	struct gpioiic_softc	*sc;
102213237Sgonzo
103270236Sloos	sc = device_get_softc(dev);
104213237Sgonzo	sc->sc_dev = dev;
105213237Sgonzo	sc->sc_busdev = device_get_parent(dev);
106228258Sadrian	if (resource_int_value(device_get_name(dev),
107228258Sadrian		device_get_unit(dev), "scl", &sc->scl_pin))
108228258Sadrian		sc->scl_pin = SCL_PIN_DEFAULT;
109228258Sadrian	if (resource_int_value(device_get_name(dev),
110228258Sadrian		device_get_unit(dev), "sda", &sc->sda_pin))
111228258Sadrian		sc->sda_pin = SDA_PIN_DEFAULT;
112213237Sgonzo
113266105Sloos#ifdef FDT
114266105Sloos	if ((node = ofw_bus_get_node(dev)) == -1)
115266105Sloos		return (ENXIO);
116266105Sloos	if (OF_getencprop(node, "scl", &pin, sizeof(pin)) > 0)
117266105Sloos		sc->scl_pin = (int)pin;
118266105Sloos	if (OF_getencprop(node, "sda", &pin, sizeof(pin)) > 0)
119266105Sloos		sc->sda_pin = (int)pin;
120266105Sloos#endif
121266105Sloos
122270236Sloos	if (sc->scl_pin < 0 || sc->scl_pin > 1)
123270236Sloos		sc->scl_pin = SCL_PIN_DEFAULT;
124270236Sloos	if (sc->sda_pin < 0 || sc->sda_pin > 1)
125270236Sloos		sc->sda_pin = SDA_PIN_DEFAULT;
126270236Sloos
127270236Sloos	devi = GPIOBUS_IVAR(dev);
128270236Sloos	device_printf(dev, "SCL pin: %d, SDA pin: %d\n",
129270236Sloos	    devi->pins[sc->scl_pin], devi->pins[sc->sda_pin]);
130270236Sloos
131213237Sgonzo	/* add generic bit-banging code */
132213237Sgonzo	bitbang = device_add_child(dev, "iicbb", -1);
133213237Sgonzo	device_probe_and_attach(bitbang);
134213237Sgonzo
135213237Sgonzo	return (0);
136213237Sgonzo}
137213237Sgonzo
138213237Sgonzo/*
139213237Sgonzo * Reset bus by setting SDA first and then SCL.
140213237Sgonzo * Must always be called with gpio bus locked.
141213237Sgonzo */
142213237Sgonzostatic void
143213237Sgonzogpioiic_reset_bus(device_t dev)
144213237Sgonzo{
145213237Sgonzo	struct gpioiic_softc		*sc = device_get_softc(dev);
146213237Sgonzo
147228258Sadrian	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
148213237Sgonzo	    GPIO_PIN_INPUT);
149228258Sadrian	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
150213237Sgonzo	    GPIO_PIN_INPUT);
151213237Sgonzo}
152213237Sgonzo
153213237Sgonzostatic int
154213237Sgonzogpioiic_callback(device_t dev, int index, caddr_t data)
155213237Sgonzo{
156213237Sgonzo	struct gpioiic_softc	*sc = device_get_softc(dev);
157278783Sloos	int error, how;
158213237Sgonzo
159278783Sloos	how = GPIOBUS_DONTWAIT;
160278783Sloos	if (data != NULL && (int)*data == IIC_WAIT)
161278783Sloos		how = GPIOBUS_WAIT;
162278783Sloos	error = 0;
163213237Sgonzo	switch (index) {
164213237Sgonzo	case IIC_REQUEST_BUS:
165278783Sloos		error = GPIOBUS_ACQUIRE_BUS(sc->sc_busdev, sc->sc_dev, how);
166213237Sgonzo		break;
167213237Sgonzo	case IIC_RELEASE_BUS:
168213237Sgonzo		GPIOBUS_RELEASE_BUS(sc->sc_busdev, sc->sc_dev);
169213237Sgonzo		break;
170213237Sgonzo	default:
171213237Sgonzo		error = EINVAL;
172213237Sgonzo	}
173213237Sgonzo
174266105Sloos	return (error);
175213237Sgonzo}
176213237Sgonzo
177213237Sgonzostatic void
178213237Sgonzogpioiic_setsda(device_t dev, int val)
179213237Sgonzo{
180213237Sgonzo	struct gpioiic_softc		*sc = device_get_softc(dev);
181213237Sgonzo
182213237Sgonzo	if (val == 0) {
183228258Sadrian		GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sda_pin, 0);
184228258Sadrian		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
185213237Sgonzo		    GPIO_PIN_OUTPUT);
186213237Sgonzo	} else {
187228258Sadrian		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
188213237Sgonzo		    GPIO_PIN_INPUT);
189213237Sgonzo	}
190213237Sgonzo}
191213237Sgonzo
192213237Sgonzostatic void
193213237Sgonzogpioiic_setscl(device_t dev, int val)
194213237Sgonzo{
195213237Sgonzo	struct gpioiic_softc		*sc = device_get_softc(dev);
196213237Sgonzo
197213237Sgonzo	if (val == 0) {
198228258Sadrian		GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->scl_pin, 0);
199228258Sadrian		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
200213237Sgonzo		    GPIO_PIN_OUTPUT);
201213237Sgonzo	} else {
202228258Sadrian		GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
203213237Sgonzo		    GPIO_PIN_INPUT);
204213237Sgonzo	}
205213237Sgonzo}
206213237Sgonzo
207213237Sgonzostatic int
208213237Sgonzogpioiic_getscl(device_t dev)
209213237Sgonzo{
210213237Sgonzo	struct gpioiic_softc		*sc = device_get_softc(dev);
211213237Sgonzo	unsigned int			val;
212213237Sgonzo
213228258Sadrian	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
214213237Sgonzo	    GPIO_PIN_INPUT);
215228258Sadrian	GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->scl_pin, &val);
216213237Sgonzo
217213237Sgonzo	return ((int)val);
218213237Sgonzo}
219213237Sgonzo
220213237Sgonzostatic int
221213237Sgonzogpioiic_getsda(device_t dev)
222213237Sgonzo{
223213237Sgonzo	struct gpioiic_softc		*sc = device_get_softc(dev);
224213237Sgonzo	unsigned int			val;
225213237Sgonzo
226228258Sadrian	GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
227213237Sgonzo	    GPIO_PIN_INPUT);
228228258Sadrian	GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->sda_pin, &val);
229213237Sgonzo
230213237Sgonzo	return ((int)val);
231213237Sgonzo}
232213237Sgonzo
233213237Sgonzostatic int
234213237Sgonzogpioiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
235213237Sgonzo{
236270236Sloos	struct gpioiic_softc		*sc;
237213237Sgonzo
238270236Sloos	sc = device_get_softc(dev);
239213237Sgonzo	gpioiic_reset_bus(sc->sc_dev);
240213237Sgonzo
241213237Sgonzo	return (IIC_ENOADDR);
242213237Sgonzo}
243213237Sgonzo
244266105Sloos#ifdef FDT
245266105Sloosstatic phandle_t
246266105Sloosgpioiic_get_node(device_t bus, device_t dev)
247266105Sloos{
248266105Sloos
249266105Sloos	/* We only have one child, the iicbb, which needs our own node. */
250266105Sloos	return (ofw_bus_get_node(bus));
251266105Sloos}
252266105Sloos#endif
253266105Sloos
254213237Sgonzostatic devclass_t gpioiic_devclass;
255213237Sgonzo
256213237Sgonzostatic device_method_t gpioiic_methods[] = {
257213237Sgonzo	/* Device interface */
258213237Sgonzo	DEVMETHOD(device_probe,		gpioiic_probe),
259213237Sgonzo	DEVMETHOD(device_attach,	gpioiic_attach),
260213237Sgonzo	DEVMETHOD(device_detach,	bus_generic_detach),
261213237Sgonzo
262213237Sgonzo	/* iicbb interface */
263213237Sgonzo	DEVMETHOD(iicbb_callback,	gpioiic_callback),
264213237Sgonzo	DEVMETHOD(iicbb_setsda,		gpioiic_setsda),
265213237Sgonzo	DEVMETHOD(iicbb_setscl,		gpioiic_setscl),
266213237Sgonzo	DEVMETHOD(iicbb_getsda,		gpioiic_getsda),
267213237Sgonzo	DEVMETHOD(iicbb_getscl,		gpioiic_getscl),
268213237Sgonzo	DEVMETHOD(iicbb_reset,		gpioiic_reset),
269213237Sgonzo
270266105Sloos#ifdef FDT
271266105Sloos	/* OFW bus interface */
272266105Sloos	DEVMETHOD(ofw_bus_get_node,	gpioiic_get_node),
273266105Sloos#endif
274266105Sloos
275213237Sgonzo	{ 0, 0 }
276213237Sgonzo};
277213237Sgonzo
278213237Sgonzostatic driver_t gpioiic_driver = {
279213237Sgonzo	"gpioiic",
280213237Sgonzo	gpioiic_methods,
281213237Sgonzo	sizeof(struct gpioiic_softc),
282213237Sgonzo};
283213237Sgonzo
284213237SgonzoDRIVER_MODULE(gpioiic, gpiobus, gpioiic_driver, gpioiic_devclass, 0, 0);
285213237SgonzoDRIVER_MODULE(iicbb, gpioiic, iicbb_driver, iicbb_devclass, 0, 0);
286213237SgonzoMODULE_DEPEND(gpioiic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
287213237SgonzoMODULE_DEPEND(gpioiic, gpiobus, 1, 1, 1);
288