]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/gpio/gpioiic.c
Merge upstream r948: fix race condition in openpam_ttyconv(3).
[FreeBSD/FreeBSD.git] / sys / dev / gpio / gpioiic.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2009 Oleksandr Tymoshenko <gonzo@freebsd.org>
5  * Copyright (c) 2010 Luiz Otavio O Souza
6  * Copyright (c) 2019 Ian Lepore <ian@freebsd.org>
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 #include <dev/gpio/gpiobusvar.h>
43 #include <dev/iicbus/iiconf.h>
44
45 #include "gpiobus_if.h"
46 #include "iicbb_if.h"
47
48 #define GPIOIIC_SCL_DFLT        0
49 #define GPIOIIC_SDA_DFLT        1
50 #define GPIOIIC_MIN_PINS        2
51
52 struct gpioiic_softc 
53 {
54         device_t        dev;
55         gpio_pin_t      sclpin;
56         gpio_pin_t      sdapin;
57 };
58
59 #ifdef FDT
60
61 #include <dev/ofw/ofw_bus.h>
62
63 static struct ofw_compat_data compat_data[] = {
64         {"i2c-gpio",  true}, /* Standard devicetree compat string */
65         {"gpioiic",   true}, /* Deprecated old freebsd compat string */
66         {NULL,        false}
67 };
68 OFWBUS_PNP_INFO(compat_data);
69 SIMPLEBUS_PNP_INFO(compat_data);
70
71 static phandle_t
72 gpioiic_get_node(device_t bus, device_t dev)
73 {
74
75         /* Share our fdt node with iicbus so it can find its child nodes. */
76         return (ofw_bus_get_node(bus));
77 }
78
79 static int
80 gpioiic_setup_fdt_pins(struct gpioiic_softc *sc)
81 {
82         phandle_t node;
83         int err;
84
85         node = ofw_bus_get_node(sc->dev);
86
87         /*
88          * Historically, we used the first two array elements of the gpios
89          * property.  The modern bindings specify separate scl-gpios and
90          * sda-gpios properties.  We cope with whichever is present.
91          */
92         if (OF_hasprop(node, "gpios")) {
93                 if ((err = gpio_pin_get_by_ofw_idx(sc->dev, node,
94                     GPIOIIC_SCL_DFLT, &sc->sclpin)) != 0) {
95                         device_printf(sc->dev, "invalid gpios property\n");
96                         return (err);
97                 }
98                 if ((err = gpio_pin_get_by_ofw_idx(sc->dev, node,
99                     GPIOIIC_SDA_DFLT, &sc->sdapin)) != 0) {
100                         device_printf(sc->dev, "ivalid gpios property\n");
101                         return (err);
102                 }
103         } else {
104                 if ((err = gpio_pin_get_by_ofw_property(sc->dev, node,
105                     "scl-gpios", &sc->sclpin)) != 0) {
106                         device_printf(sc->dev, "missing scl-gpios property\n");
107                         return (err);
108                 }
109                 if ((err = gpio_pin_get_by_ofw_property(sc->dev, node,
110                     "sda-gpios", &sc->sdapin)) != 0) {
111                         device_printf(sc->dev, "missing sda-gpios property\n");
112                         return (err);
113                 }
114         }
115         return (0);
116 }
117 #endif /* FDT */
118
119 static int
120 gpioiic_setup_hinted_pins(struct gpioiic_softc *sc)
121 {
122         device_t busdev;
123         const char *busname, *devname;
124         int err, numpins, sclnum, sdanum, unit;
125
126         devname = device_get_name(sc->dev);
127         unit = device_get_unit(sc->dev);
128         busdev = device_get_parent(sc->dev);
129
130         /*
131          * If there is not an "at" hint naming our actual parent, then we
132          * weren't instantiated as a child of gpiobus via hints, and we thus
133          * can't access ivars that only exist for such children.
134          */
135         if (resource_string_value(devname, unit, "at", &busname) != 0 ||
136             (strcmp(busname, device_get_nameunit(busdev)) != 0 &&
137              strcmp(busname, device_get_name(busdev)) != 0)) {
138                 return (ENOENT);
139         }
140
141         /* Make sure there were hints for at least two pins. */
142         numpins = gpiobus_get_npins(sc->dev);
143         if (numpins < GPIOIIC_MIN_PINS) {
144 #ifdef FDT
145                 /*
146                  * Be silent when there are no hints on FDT systems; the FDT
147                  * data will provide the pin config (we'll whine if it doesn't).
148                  */
149                 if (numpins == 0) {
150                         return (ENOENT);
151                 }
152 #endif
153                 device_printf(sc->dev, 
154                     "invalid pins hint; it must contain at least %d pins\n",
155                     GPIOIIC_MIN_PINS);
156                 return (EINVAL);
157         }
158
159         /*
160          * Our parent bus has already parsed the pins hint and it will use that
161          * info when we call gpio_pin_get_by_child_index().  But we have to
162          * handle the scl/sda index hints that tell us which of the two pins is
163          * the clock and which is the data.  They're optional, but if present
164          * they must be a valid index (0 <= index < numpins).
165          */
166         if ((err = resource_int_value(devname, unit, "scl", &sclnum)) != 0)
167                 sclnum = GPIOIIC_SCL_DFLT;
168         else if (sclnum < 0 || sclnum >= numpins) {
169                 device_printf(sc->dev, "invalid scl hint %d\n", sclnum);
170                 return (EINVAL);
171         }
172         if ((err = resource_int_value(devname, unit, "sda", &sdanum)) != 0)
173                 sdanum = GPIOIIC_SDA_DFLT;
174         else if (sdanum < 0 || sdanum >= numpins) {
175                 device_printf(sc->dev, "invalid sda hint %d\n", sdanum);
176                 return (EINVAL);
177         }
178
179         /* Allocate gpiobus_pin structs for the pins we found above. */
180         if ((err = gpio_pin_get_by_child_index(sc->dev, sclnum,
181             &sc->sclpin)) != 0)
182                 return (err);
183         if ((err = gpio_pin_get_by_child_index(sc->dev, sdanum,
184             &sc->sdapin)) != 0)
185                 return (err);
186
187         return (0);
188 }
189
190 static void
191 gpioiic_setsda(device_t dev, int val)
192 {
193         struct gpioiic_softc *sc = device_get_softc(dev);
194
195         if (val) {
196                 gpio_pin_setflags(sc->sdapin, GPIO_PIN_INPUT);
197         } else {
198                 gpio_pin_setflags(sc->sdapin,
199                     GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN);
200                 gpio_pin_set_active(sc->sdapin, 0);
201         }
202 }
203
204 static void
205 gpioiic_setscl(device_t dev, int val)
206 {
207         struct gpioiic_softc *sc = device_get_softc(dev);
208
209         if (val) {
210                 gpio_pin_setflags(sc->sclpin, GPIO_PIN_INPUT);
211         } else {
212                 gpio_pin_setflags(sc->sclpin,
213                     GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN);
214                 gpio_pin_set_active(sc->sclpin, 0);
215         }
216 }
217
218 static int
219 gpioiic_getscl(device_t dev)
220 {
221         struct gpioiic_softc *sc = device_get_softc(dev);
222         bool val;
223
224         gpio_pin_setflags(sc->sclpin, GPIO_PIN_INPUT);
225         gpio_pin_is_active(sc->sclpin, &val);
226         return (val);
227 }
228
229 static int
230 gpioiic_getsda(device_t dev)
231 {
232         struct gpioiic_softc *sc = device_get_softc(dev);
233         bool val;
234
235         gpio_pin_setflags(sc->sdapin, GPIO_PIN_INPUT);
236         gpio_pin_is_active(sc->sdapin, &val);
237         return (val);
238 }
239
240 static int
241 gpioiic_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
242 {
243         struct gpioiic_softc *sc = device_get_softc(dev);
244
245         /* Stop driving the bus pins. */
246         gpio_pin_setflags(sc->sdapin, GPIO_PIN_INPUT);
247         gpio_pin_setflags(sc->sclpin, GPIO_PIN_INPUT);
248
249         /* Indicate that we have no slave address (master mode). */
250         return (IIC_ENOADDR);
251 }
252
253 static void
254 gpioiic_cleanup(struct gpioiic_softc *sc)
255 {
256
257         device_delete_children(sc->dev);
258
259         if (sc->sclpin != NULL)
260                 gpio_pin_release(sc->sclpin);
261
262         if (sc->sdapin != NULL)
263                 gpio_pin_release(sc->sdapin);
264 }
265
266 static int
267 gpioiic_probe(device_t dev)
268 {
269         int rv;
270
271         /*
272          * By default we only bid to attach if specifically added by our parent
273          * (usually via hint.gpioiic.#.at=busname).  On FDT systems we bid as
274          * the default driver based on being configured in the FDT data.
275          */
276         rv = BUS_PROBE_NOWILDCARD;
277
278 #ifdef FDT
279         if (ofw_bus_status_okay(dev) &&
280             ofw_bus_search_compatible(dev, compat_data)->ocd_data)
281                 rv = BUS_PROBE_DEFAULT;
282 #endif
283
284         device_set_desc(dev, "GPIO I2C");
285
286         return (rv);
287 }
288
289 static int
290 gpioiic_attach(device_t dev)
291 {
292         struct gpioiic_softc *sc = device_get_softc(dev);
293         int err;
294
295         sc->dev = dev;
296
297         /* Acquire our gpio pins. */
298         err = gpioiic_setup_hinted_pins(sc);
299 #ifdef FDT
300         if (err != 0)
301                 err = gpioiic_setup_fdt_pins(sc);
302 #endif
303         if (err != 0) {
304                 device_printf(sc->dev, "no pins configured\n");
305                 gpioiic_cleanup(sc);
306                 return (ENXIO);
307         }
308
309         /*
310          * Say what we came up with for pin config.
311          * NB: in the !FDT case the controller driver might not be set up enough
312          * for GPIO_GET_BUS() to work.  Also, our parent is the only gpiobus
313          * that can provide our pins.
314          */
315         device_printf(dev, "SCL pin: %s:%d, SDA pin: %s:%d\n",
316 #ifdef FDT
317             device_get_nameunit(GPIO_GET_BUS(sc->sclpin->dev)), sc->sclpin->pin,
318             device_get_nameunit(GPIO_GET_BUS(sc->sdapin->dev)), sc->sdapin->pin);
319 #else
320             device_get_nameunit(device_get_parent(dev)), sc->sclpin->pin,
321             device_get_nameunit(device_get_parent(dev)), sc->sdapin->pin);
322 #endif
323
324         /* Add the bitbang driver as our only child; it will add iicbus. */
325         device_add_child(sc->dev, "iicbb", -1);
326         return (bus_generic_attach(dev));
327 }
328
329 static int
330 gpioiic_detach(device_t dev)
331 {
332         struct gpioiic_softc *sc = device_get_softc(dev);
333         int err;
334
335         if ((err = bus_generic_detach(dev)) != 0)
336                 return (err);
337
338         gpioiic_cleanup(sc);
339
340         return (0);
341 }
342
343 static devclass_t gpioiic_devclass;
344
345 static device_method_t gpioiic_methods[] = {
346         /* Device interface */
347         DEVMETHOD(device_probe,         gpioiic_probe),
348         DEVMETHOD(device_attach,        gpioiic_attach),
349         DEVMETHOD(device_detach,        gpioiic_detach),
350
351         /* iicbb interface */
352         DEVMETHOD(iicbb_setsda,         gpioiic_setsda),
353         DEVMETHOD(iicbb_setscl,         gpioiic_setscl),
354         DEVMETHOD(iicbb_getsda,         gpioiic_getsda),
355         DEVMETHOD(iicbb_getscl,         gpioiic_getscl),
356         DEVMETHOD(iicbb_reset,          gpioiic_reset),
357
358 #ifdef FDT
359         /* OFW bus interface */
360         DEVMETHOD(ofw_bus_get_node,     gpioiic_get_node),
361 #endif
362
363         DEVMETHOD_END
364 };
365
366 static driver_t gpioiic_driver = {
367         "gpioiic",
368         gpioiic_methods,
369         sizeof(struct gpioiic_softc),
370 };
371
372 DRIVER_MODULE(gpioiic, gpiobus, gpioiic_driver, gpioiic_devclass, 0, 0);
373 DRIVER_MODULE(gpioiic, simplebus, gpioiic_driver, gpioiic_devclass, 0, 0);
374 DRIVER_MODULE(iicbb, gpioiic, iicbb_driver, iicbb_devclass, 0, 0);
375 MODULE_DEPEND(gpioiic, iicbb, IICBB_MINVER, IICBB_PREFVER, IICBB_MAXVER);
376 MODULE_DEPEND(gpioiic, gpiobus, 1, 1, 1);