]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/arm/broadcom/bcm2835/raspberrypi_gpio.c
Merge bmake-20201117
[FreeBSD/FreeBSD.git] / sys / arm / broadcom / bcm2835 / raspberrypi_gpio.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
5  * Copyright (c) 2012-2015 Luiz Otavio O Souza <loos@FreeBSD.org>
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/lock.h>
41 #include <sys/module.h>
42 #include <sys/sx.h>
43 #include <sys/proc.h>
44
45 #include <dev/gpio/gpiobusvar.h>
46 #include <dev/ofw/ofw_bus.h>
47
48 #include <arm/broadcom/bcm2835/bcm2835_firmware.h>
49
50 #include "gpio_if.h"
51
52 #define RPI_FW_GPIO_PINS                8
53 #define RPI_FW_GPIO_BASE                128
54 #define RPI_FW_GPIO_DEFAULT_CAPS        (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)
55
56 struct rpi_fw_gpio_softc {
57         device_t                sc_busdev;
58         device_t                sc_firmware;
59         struct sx               sc_sx;
60         struct gpio_pin         sc_gpio_pins[RPI_FW_GPIO_PINS];
61         uint8_t                 sc_gpio_state;
62 };
63
64 #define RPI_FW_GPIO_LOCK(_sc)   sx_xlock(&(_sc)->sc_sx)
65 #define RPI_FW_GPIO_UNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx)
66
67 static struct ofw_compat_data compat_data[] = {
68         {"raspberrypi,firmware-gpio",   1},
69         {NULL,                          0}
70 };
71
72 static int
73 rpi_fw_gpio_pin_configure(struct rpi_fw_gpio_softc *sc, struct gpio_pin *pin,
74     unsigned int flags)
75 {
76         union msg_get_gpio_config old_cfg;
77         union msg_set_gpio_config new_cfg;
78         int rv;
79
80         bzero(&old_cfg, sizeof(old_cfg));
81         bzero(&new_cfg, sizeof(new_cfg));
82         old_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin;
83
84         RPI_FW_GPIO_LOCK(sc);
85         rv = bcm2835_firmware_property(sc->sc_firmware,
86             BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &old_cfg, sizeof(old_cfg));
87         if (rv == 0 && old_cfg.resp.gpio != 0)
88                 rv = EIO;
89         if (rv != 0)
90                 goto fail;
91
92         new_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin;
93         if (flags & GPIO_PIN_INPUT) {
94                 new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_IN;
95                 new_cfg.req.state = 0;
96                 pin->gp_flags = GPIO_PIN_INPUT;
97         } else if (flags & GPIO_PIN_OUTPUT) {
98                 new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_OUT;
99                 if (flags & (GPIO_PIN_PRESET_HIGH | GPIO_PIN_PRESET_LOW)) {
100                         if (flags & GPIO_PIN_PRESET_HIGH) {
101                                 new_cfg.req.state = 1;
102                                 sc->sc_gpio_state |= (1 << pin->gp_pin);
103                         } else {
104                                 new_cfg.req.state = 0;
105                                 sc->sc_gpio_state &= ~(1 << pin->gp_pin);
106                         }
107                 } else {
108                         if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0) {
109                                 new_cfg.req.state = 1;
110                         } else {
111                                 new_cfg.req.state = 0;
112                         }
113                 }
114                 pin->gp_flags = GPIO_PIN_OUTPUT;
115         } else {
116                 new_cfg.req.dir = old_cfg.resp.dir;
117                 /* Use the old state to decide high/low */
118                 if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0)
119                         new_cfg.req.state = 1;
120                 else
121                         new_cfg.req.state = 0;
122         }
123         new_cfg.req.pol = old_cfg.resp.pol;
124         new_cfg.req.term_en = 0;
125         new_cfg.req.term_pull_up = 0;
126
127         rv = bcm2835_firmware_property(sc->sc_firmware,
128             BCM2835_FIRMWARE_TAG_SET_GPIO_CONFIG, &new_cfg, sizeof(new_cfg));
129
130 fail:
131         RPI_FW_GPIO_UNLOCK(sc);
132
133         return (rv);
134 }
135
136 static device_t
137 rpi_fw_gpio_get_bus(device_t dev)
138 {
139         struct rpi_fw_gpio_softc *sc;
140
141         sc = device_get_softc(dev);
142
143         return (sc->sc_busdev);
144 }
145
146 static int
147 rpi_fw_gpio_pin_max(device_t dev, int *maxpin)
148 {
149
150         *maxpin = RPI_FW_GPIO_PINS - 1;
151         return (0);
152 }
153
154 static int
155 rpi_fw_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
156 {
157         struct rpi_fw_gpio_softc *sc;
158         int i;
159
160         sc = device_get_softc(dev);
161         for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
162                 if (sc->sc_gpio_pins[i].gp_pin == pin)
163                         break;
164         }
165
166         if (i >= RPI_FW_GPIO_PINS)
167                 return (EINVAL);
168
169         *caps = RPI_FW_GPIO_DEFAULT_CAPS;
170         return (0);
171 }
172
173 static int
174 rpi_fw_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
175 {
176         struct rpi_fw_gpio_softc *sc = device_get_softc(dev);
177         int i;
178
179         for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
180                 if (sc->sc_gpio_pins[i].gp_pin == pin)
181                         break;
182         }
183
184         if (i >= RPI_FW_GPIO_PINS)
185                 return (EINVAL);
186
187         RPI_FW_GPIO_LOCK(sc);
188         *flags = sc->sc_gpio_pins[i].gp_flags;
189         RPI_FW_GPIO_UNLOCK(sc);
190
191         return (0);
192 }
193
194 static int
195 rpi_fw_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
196 {
197         struct rpi_fw_gpio_softc *sc;
198         int i;
199
200         sc = device_get_softc(dev);
201         for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
202                 if (sc->sc_gpio_pins[i].gp_pin == pin)
203                         break;
204         }
205
206         if (i >= RPI_FW_GPIO_PINS)
207                 return (EINVAL);
208
209         RPI_FW_GPIO_LOCK(sc);
210         memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME);
211         RPI_FW_GPIO_UNLOCK(sc);
212
213         return (0);
214 }
215
216 static int
217 rpi_fw_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
218 {
219         struct rpi_fw_gpio_softc *sc;
220         int i;
221
222         sc = device_get_softc(dev);
223         for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
224                 if (sc->sc_gpio_pins[i].gp_pin == pin)
225                         break;
226         }
227
228         if (i >= RPI_FW_GPIO_PINS)
229                 return (EINVAL);
230
231         return (rpi_fw_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags));
232 }
233
234 static int
235 rpi_fw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
236 {
237         struct rpi_fw_gpio_softc *sc;
238         union msg_set_gpio_state state;
239         int i, rv;
240
241         sc = device_get_softc(dev);
242         for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
243                 if (sc->sc_gpio_pins[i].gp_pin == pin)
244                         break;
245         }
246         if (i >= RPI_FW_GPIO_PINS)
247                 return (EINVAL);
248
249         state.req.gpio = RPI_FW_GPIO_BASE + pin;
250         state.req.state = value;
251
252         RPI_FW_GPIO_LOCK(sc);
253         rv = bcm2835_firmware_property(sc->sc_firmware,
254             BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &state, sizeof(state));
255         /* The firmware sets gpio to 0 on success */
256         if (rv == 0 && state.resp.gpio != 0)
257                 rv = EINVAL;
258         if (rv == 0) {
259                 sc->sc_gpio_pins[i].gp_flags &= ~(GPIO_PIN_PRESET_HIGH |
260                     GPIO_PIN_PRESET_LOW);
261                 if (value)
262                         sc->sc_gpio_state |= (1 << i);
263                 else
264                         sc->sc_gpio_state &= ~(1 << i);
265         }
266         RPI_FW_GPIO_UNLOCK(sc);
267
268         return (rv);
269 }
270
271 static int
272 rpi_fw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
273 {
274         struct rpi_fw_gpio_softc *sc;
275         union msg_get_gpio_state state;
276         int i, rv;
277
278         sc = device_get_softc(dev);
279         for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
280                 if (sc->sc_gpio_pins[i].gp_pin == pin)
281                         break;
282         }
283         if (i >= RPI_FW_GPIO_PINS)
284                 return (EINVAL);
285
286         bzero(&state, sizeof(state));
287         state.req.gpio = RPI_FW_GPIO_BASE + pin;
288
289         RPI_FW_GPIO_LOCK(sc);
290         rv = bcm2835_firmware_property(sc->sc_firmware,
291             BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &state, sizeof(state));
292         RPI_FW_GPIO_UNLOCK(sc);
293
294         /* The firmware sets gpio to 0 on success */
295         if (rv == 0 && state.resp.gpio != 0)
296                 rv = EINVAL;
297         if (rv == 0)
298                 *val = !state.resp.state;
299
300         return (rv);
301 }
302
303 static int
304 rpi_fw_gpio_pin_toggle(device_t dev, uint32_t pin)
305 {
306         struct rpi_fw_gpio_softc *sc;
307         union msg_get_gpio_state old_state;
308         union msg_set_gpio_state new_state;
309         int i, rv;
310
311         sc = device_get_softc(dev);
312         for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
313                 if (sc->sc_gpio_pins[i].gp_pin == pin)
314                         break;
315         }
316         if (i >= RPI_FW_GPIO_PINS)
317                 return (EINVAL);
318
319         bzero(&old_state, sizeof(old_state));
320         bzero(&new_state, sizeof(new_state));
321
322         old_state.req.gpio = RPI_FW_GPIO_BASE + pin;
323         new_state.req.gpio = RPI_FW_GPIO_BASE + pin;
324
325         RPI_FW_GPIO_LOCK(sc);
326         rv = bcm2835_firmware_property(sc->sc_firmware,
327             BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &old_state, sizeof(old_state));
328         /* The firmware sets gpio to 0 on success */
329         if (rv == 0 && old_state.resp.gpio == 0) {
330                 /* Set the new state to invert the GPIO */
331                 new_state.req.state = !old_state.resp.state;
332                 rv = bcm2835_firmware_property(sc->sc_firmware,
333                     BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &new_state,
334                     sizeof(new_state));
335         }
336         if (rv == 0 && (old_state.resp.gpio != 0 || new_state.resp.gpio != 0))
337                 rv = EINVAL;
338         RPI_FW_GPIO_UNLOCK(sc);
339
340         return (rv);
341 }
342
343 static int
344 rpi_fw_gpio_probe(device_t dev)
345 {
346
347         if (!ofw_bus_status_okay(dev))
348                 return (ENXIO);
349
350         if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
351                 return (ENXIO);
352
353         device_set_desc(dev, "Raspberry Pi Firmware GPIO controller");
354         return (BUS_PROBE_DEFAULT);
355 }
356
357 static int
358 rpi_fw_gpio_attach(device_t dev)
359 {
360         union msg_get_gpio_config cfg;
361         struct rpi_fw_gpio_softc *sc;
362         char *names;
363         phandle_t gpio;
364         int i, nelems, elm_pos, rv;
365
366         sc = device_get_softc(dev);
367         sc->sc_firmware = device_get_parent(dev);
368         sx_init(&sc->sc_sx, "Raspberry Pi firmware gpio");
369         /* Find our node. */
370         gpio = ofw_bus_get_node(dev);
371         if (!OF_hasprop(gpio, "gpio-controller"))
372                 /* This is not a GPIO controller. */
373                 goto fail;
374
375         nelems = OF_getprop_alloc(gpio, "gpio-line-names", (void **)&names);
376         if (nelems <= 0)
377                 names = NULL;
378         elm_pos = 0;
379         for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
380                 /* Set the current pin name */
381                 if (names != NULL && elm_pos < nelems &&
382                     names[elm_pos] != '\0') {
383                         snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
384                             "%s", names + elm_pos);
385                         /* Find the next pin name */
386                         elm_pos += strlen(names + elm_pos) + 1;
387                 } else {
388                         snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
389                             "pin %d", i);
390                 }
391
392                 sc->sc_gpio_pins[i].gp_pin = i;
393                 sc->sc_gpio_pins[i].gp_caps = RPI_FW_GPIO_DEFAULT_CAPS;
394
395                 bzero(&cfg, sizeof(cfg));
396                 cfg.req.gpio = RPI_FW_GPIO_BASE + i;
397                 rv = bcm2835_firmware_property(sc->sc_firmware,
398                     BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &cfg, sizeof(cfg));
399                 if (rv == 0 && cfg.resp.gpio == 0) {
400                         if (cfg.resp.dir == BCM2835_FIRMWARE_GPIO_IN)
401                                 sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;
402                         else
403                                 sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_OUTPUT;
404                 } else {
405                         sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;
406                 }
407         }
408         free(names, M_OFWPROP);
409         sc->sc_busdev = gpiobus_attach_bus(dev);
410         if (sc->sc_busdev == NULL)
411                 goto fail;
412
413         return (0);
414
415 fail:
416         sx_destroy(&sc->sc_sx);
417
418         return (ENXIO);
419 }
420
421 static int
422 rpi_fw_gpio_detach(device_t dev)
423 {
424
425         return (EBUSY);
426 }
427
428 static device_method_t rpi_fw_gpio_methods[] = {
429         /* Device interface */
430         DEVMETHOD(device_probe,         rpi_fw_gpio_probe),
431         DEVMETHOD(device_attach,        rpi_fw_gpio_attach),
432         DEVMETHOD(device_detach,        rpi_fw_gpio_detach),
433
434         /* GPIO protocol */
435         DEVMETHOD(gpio_get_bus,         rpi_fw_gpio_get_bus),
436         DEVMETHOD(gpio_pin_max,         rpi_fw_gpio_pin_max),
437         DEVMETHOD(gpio_pin_getname,     rpi_fw_gpio_pin_getname),
438         DEVMETHOD(gpio_pin_getflags,    rpi_fw_gpio_pin_getflags),
439         DEVMETHOD(gpio_pin_getcaps,     rpi_fw_gpio_pin_getcaps),
440         DEVMETHOD(gpio_pin_setflags,    rpi_fw_gpio_pin_setflags),
441         DEVMETHOD(gpio_pin_get,         rpi_fw_gpio_pin_get),
442         DEVMETHOD(gpio_pin_set,         rpi_fw_gpio_pin_set),
443         DEVMETHOD(gpio_pin_toggle,      rpi_fw_gpio_pin_toggle),
444
445         DEVMETHOD_END
446 };
447
448 static devclass_t rpi_fw_gpio_devclass;
449
450 static driver_t rpi_fw_gpio_driver = {
451         "gpio",
452         rpi_fw_gpio_methods,
453         sizeof(struct rpi_fw_gpio_softc),
454 };
455
456 EARLY_DRIVER_MODULE(rpi_fw_gpio, bcm2835_firmware, rpi_fw_gpio_driver,
457     rpi_fw_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);