]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/gpio/gpioiic.c
Upgrade our copies of clang, llvm, lld, lldb, compiler-rt and libc++ to
[FreeBSD/FreeBSD.git] / sys / dev / gpio / gpioiic.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org>
5  * Copyright (c) 2010 Luiz Otavio O Souza
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include "opt_platform.h"
34
35 #include <sys/param.h>
36 #include <sys/systm.h>
37 #include <sys/bus.h>
38 #include <sys/gpio.h>
39 #include <sys/kernel.h>
40 #include <sys/module.h>
41
42 #ifdef FDT
43 #include <dev/fdt/fdt_common.h>
44 #include <dev/ofw/ofw_bus.h>
45 #endif
46
47 #include <dev/gpio/gpiobusvar.h>
48 #include <dev/iicbus/iiconf.h>
49 #include <dev/iicbus/iicbus.h>
50
51 #include "gpiobus_if.h"
52 #include "iicbb_if.h"
53
54 #define GPIOIIC_SCL_DFLT        0
55 #define GPIOIIC_SDA_DFLT        1
56 #define GPIOIIC_MIN_PINS        2
57
58 struct gpioiic_softc 
59 {
60         device_t        sc_dev;
61         device_t        sc_busdev;
62         int             scl_pin;
63         int             sda_pin;
64 };
65
66 static int gpioiic_probe(device_t);
67 static int gpioiic_attach(device_t);
68
69 /* iicbb interface */
70 static void gpioiic_reset_bus(device_t);
71 static void gpioiic_setsda(device_t, int);
72 static void gpioiic_setscl(device_t, int);
73 static int gpioiic_getsda(device_t);
74 static int gpioiic_getscl(device_t);
75 static int gpioiic_reset(device_t, u_char, u_char, u_char *);
76
77 static int
78 gpioiic_probe(device_t dev)
79 {
80         struct gpiobus_ivar *devi;
81
82 #ifdef FDT
83         if (!ofw_bus_status_okay(dev))
84                 return (ENXIO);
85         if (!ofw_bus_is_compatible(dev, "gpioiic"))
86                 return (ENXIO);
87 #endif
88         devi = GPIOBUS_IVAR(dev);
89         if (devi->npins < GPIOIIC_MIN_PINS) {
90                 device_printf(dev,
91                     "gpioiic needs at least %d GPIO pins (only %d given).\n",
92                     GPIOIIC_MIN_PINS, devi->npins);
93                 return (ENXIO);
94         }
95         device_set_desc(dev, "GPIO I2C bit-banging driver");
96
97         return (BUS_PROBE_DEFAULT);
98 }
99
100 static int
101 gpioiic_attach(device_t dev)
102 {
103         device_t                bitbang;
104 #ifdef FDT
105         phandle_t               node;
106         pcell_t                 pin;
107 #endif
108         struct gpiobus_ivar     *devi;
109         struct gpioiic_softc    *sc;
110
111         sc = device_get_softc(dev);
112         sc->sc_dev = dev;
113         sc->sc_busdev = device_get_parent(dev);
114         if (resource_int_value(device_get_name(dev),
115                 device_get_unit(dev), "scl", &sc->scl_pin))
116                 sc->scl_pin = GPIOIIC_SCL_DFLT;
117         if (resource_int_value(device_get_name(dev),
118                 device_get_unit(dev), "sda", &sc->sda_pin))
119                 sc->sda_pin = GPIOIIC_SDA_DFLT;
120
121 #ifdef FDT
122         if ((node = ofw_bus_get_node(dev)) == -1)
123                 return (ENXIO);
124         if (OF_getencprop(node, "scl", &pin, sizeof(pin)) > 0)
125                 sc->scl_pin = (int)pin;
126         if (OF_getencprop(node, "sda", &pin, sizeof(pin)) > 0)
127                 sc->sda_pin = (int)pin;
128 #endif
129
130         if (sc->scl_pin < 0 || sc->scl_pin > 1)
131                 sc->scl_pin = GPIOIIC_SCL_DFLT;
132         if (sc->sda_pin < 0 || sc->sda_pin > 1)
133                 sc->sda_pin = GPIOIIC_SDA_DFLT;
134
135         devi = GPIOBUS_IVAR(dev);
136         device_printf(dev, "SCL pin: %d, SDA pin: %d\n",
137             devi->pins[sc->scl_pin], devi->pins[sc->sda_pin]);
138
139         /* add generic bit-banging code */
140         bitbang = device_add_child(dev, "iicbb", -1);
141         device_probe_and_attach(bitbang);
142
143         return (0);
144 }
145
146 /*
147  * Reset bus by setting SDA first and then SCL. 
148  * Must always be called with gpio bus locked.
149  */
150 static void
151 gpioiic_reset_bus(device_t dev)
152 {
153         struct gpioiic_softc            *sc = device_get_softc(dev);
154
155         GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
156             GPIO_PIN_INPUT);
157         GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
158             GPIO_PIN_INPUT);
159 }
160
161 static void
162 gpioiic_setsda(device_t dev, int val)
163 {
164         struct gpioiic_softc            *sc = device_get_softc(dev);
165
166         if (val == 0) {
167                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->sda_pin, 0);
168                 GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
169                     GPIO_PIN_OUTPUT);
170         } else {
171                 GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
172                     GPIO_PIN_INPUT);
173         }
174 }
175
176 static void
177 gpioiic_setscl(device_t dev, int val)
178 {
179         struct gpioiic_softc            *sc = device_get_softc(dev);
180
181         if (val == 0) {
182                 GPIOBUS_PIN_SET(sc->sc_busdev, sc->sc_dev, sc->scl_pin, 0);
183                 GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
184                     GPIO_PIN_OUTPUT);
185         } else {
186                 GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
187                     GPIO_PIN_INPUT);
188         }
189 }
190
191 static int
192 gpioiic_getscl(device_t dev)
193 {
194         struct gpioiic_softc            *sc = device_get_softc(dev);
195         unsigned int                    val;
196
197         GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->scl_pin,
198             GPIO_PIN_INPUT);
199         GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->scl_pin, &val);
200
201         return ((int)val);
202 }
203
204 static int
205 gpioiic_getsda(device_t dev)
206 {
207         struct gpioiic_softc            *sc = device_get_softc(dev);
208         unsigned int                    val;
209
210         GPIOBUS_PIN_SETFLAGS(sc->sc_busdev, sc->sc_dev, sc->sda_pin,
211             GPIO_PIN_INPUT);
212         GPIOBUS_PIN_GET(sc->sc_busdev, sc->sc_dev, sc->sda_pin, &val);
213
214         return ((int)val);
215 }
216
217 static int
218 gpioiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
219 {
220         struct gpioiic_softc            *sc;
221
222         sc = device_get_softc(dev);
223         gpioiic_reset_bus(sc->sc_dev);
224
225         return (IIC_ENOADDR);
226 }
227
228 #ifdef FDT
229 static phandle_t
230 gpioiic_get_node(device_t bus, device_t dev)
231 {
232
233         /* We only have one child, the iicbb, which needs our own node. */
234         return (ofw_bus_get_node(bus));
235 }
236 #endif
237
238 static devclass_t gpioiic_devclass;
239
240 static device_method_t gpioiic_methods[] = {
241         /* Device interface */
242         DEVMETHOD(device_probe,         gpioiic_probe),
243         DEVMETHOD(device_attach,        gpioiic_attach),
244         DEVMETHOD(device_detach,        bus_generic_detach),
245
246         /* iicbb interface */
247         DEVMETHOD(iicbb_setsda,         gpioiic_setsda),
248         DEVMETHOD(iicbb_setscl,         gpioiic_setscl),
249         DEVMETHOD(iicbb_getsda,         gpioiic_getsda),
250         DEVMETHOD(iicbb_getscl,         gpioiic_getscl),
251         DEVMETHOD(iicbb_reset,          gpioiic_reset),
252
253 #ifdef FDT
254         /* OFW bus interface */
255         DEVMETHOD(ofw_bus_get_node,     gpioiic_get_node),
256 #endif
257
258         DEVMETHOD_END
259 };
260
261 static driver_t gpioiic_driver = {
262         "gpioiic",
263         gpioiic_methods,
264         sizeof(struct gpioiic_softc),
265 };
266
267 DRIVER_MODULE(gpioiic, gpiobus, gpioiic_driver, gpioiic_devclass, 0, 0);
268 DRIVER_MODULE(iicbb, gpioiic, iicbb_driver, iicbb_devclass, 0, 0);
269 MODULE_DEPEND(gpioiic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
270 MODULE_DEPEND(gpioiic, gpiobus, 1, 1, 1);