2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@FreeBSD.org>
5 * Copyright (c) 2012-2015 Luiz Otavio O Souza <loos@FreeBSD.org>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
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.
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
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
33 #include "opt_platform.h"
35 #include <sys/param.h>
36 #include <sys/systm.h>
39 #include <sys/kernel.h>
41 #include <sys/module.h>
45 #include <dev/gpio/gpiobusvar.h>
46 #include <dev/ofw/ofw_bus.h>
48 #include <arm/broadcom/bcm2835/bcm2835_firmware.h>
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)
56 struct rpi_fw_gpio_softc {
60 struct gpio_pin sc_gpio_pins[RPI_FW_GPIO_PINS];
61 uint8_t sc_gpio_state;
64 #define RPI_FW_GPIO_LOCK(_sc) sx_xlock(&(_sc)->sc_sx)
65 #define RPI_FW_GPIO_UNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx)
67 static struct ofw_compat_data compat_data[] = {
68 {"raspberrypi,firmware-gpio", 1},
73 rpi_fw_gpio_pin_configure(struct rpi_fw_gpio_softc *sc, struct gpio_pin *pin,
76 union msg_get_gpio_config old_cfg;
77 union msg_set_gpio_config new_cfg;
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;
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)
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);
104 new_cfg.req.state = 0;
105 sc->sc_gpio_state &= ~(1 << pin->gp_pin);
108 if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0) {
109 new_cfg.req.state = 1;
111 new_cfg.req.state = 0;
114 pin->gp_flags = GPIO_PIN_OUTPUT;
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;
121 new_cfg.req.state = 0;
123 new_cfg.req.pol = old_cfg.resp.pol;
124 new_cfg.req.term_en = 0;
125 new_cfg.req.term_pull_up = 0;
127 rv = bcm2835_firmware_property(sc->sc_firmware,
128 BCM2835_FIRMWARE_TAG_SET_GPIO_CONFIG, &new_cfg, sizeof(new_cfg));
131 RPI_FW_GPIO_UNLOCK(sc);
137 rpi_fw_gpio_get_bus(device_t dev)
139 struct rpi_fw_gpio_softc *sc;
141 sc = device_get_softc(dev);
143 return (sc->sc_busdev);
147 rpi_fw_gpio_pin_max(device_t dev, int *maxpin)
150 *maxpin = RPI_FW_GPIO_PINS - 1;
155 rpi_fw_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)
157 struct rpi_fw_gpio_softc *sc;
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)
166 if (i >= RPI_FW_GPIO_PINS)
169 *caps = RPI_FW_GPIO_DEFAULT_CAPS;
174 rpi_fw_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)
176 struct rpi_fw_gpio_softc *sc = device_get_softc(dev);
179 for (i = 0; i < RPI_FW_GPIO_PINS; i++) {
180 if (sc->sc_gpio_pins[i].gp_pin == pin)
184 if (i >= RPI_FW_GPIO_PINS)
187 RPI_FW_GPIO_LOCK(sc);
188 *flags = sc->sc_gpio_pins[i].gp_flags;
189 RPI_FW_GPIO_UNLOCK(sc);
195 rpi_fw_gpio_pin_getname(device_t dev, uint32_t pin, char *name)
197 struct rpi_fw_gpio_softc *sc;
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)
206 if (i >= RPI_FW_GPIO_PINS)
209 RPI_FW_GPIO_LOCK(sc);
210 memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME);
211 RPI_FW_GPIO_UNLOCK(sc);
217 rpi_fw_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)
219 struct rpi_fw_gpio_softc *sc;
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)
228 if (i >= RPI_FW_GPIO_PINS)
231 return (rpi_fw_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags));
235 rpi_fw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)
237 struct rpi_fw_gpio_softc *sc;
238 union msg_set_gpio_state state;
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)
246 if (i >= RPI_FW_GPIO_PINS)
249 state.req.gpio = RPI_FW_GPIO_BASE + pin;
250 state.req.state = value;
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)
259 sc->sc_gpio_pins[i].gp_flags &= ~(GPIO_PIN_PRESET_HIGH |
260 GPIO_PIN_PRESET_LOW);
262 sc->sc_gpio_state |= (1 << i);
264 sc->sc_gpio_state &= ~(1 << i);
266 RPI_FW_GPIO_UNLOCK(sc);
272 rpi_fw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)
274 struct rpi_fw_gpio_softc *sc;
275 union msg_get_gpio_state state;
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)
283 if (i >= RPI_FW_GPIO_PINS)
286 bzero(&state, sizeof(state));
287 state.req.gpio = RPI_FW_GPIO_BASE + pin;
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);
294 /* The firmware sets gpio to 0 on success */
295 if (rv == 0 && state.resp.gpio != 0)
298 *val = !state.resp.state;
304 rpi_fw_gpio_pin_toggle(device_t dev, uint32_t pin)
306 struct rpi_fw_gpio_softc *sc;
307 union msg_get_gpio_state old_state;
308 union msg_set_gpio_state new_state;
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)
316 if (i >= RPI_FW_GPIO_PINS)
319 bzero(&old_state, sizeof(old_state));
320 bzero(&new_state, sizeof(new_state));
322 old_state.req.gpio = RPI_FW_GPIO_BASE + pin;
323 new_state.req.gpio = RPI_FW_GPIO_BASE + pin;
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,
336 if (rv == 0 && (old_state.resp.gpio != 0 || new_state.resp.gpio != 0))
338 RPI_FW_GPIO_UNLOCK(sc);
344 rpi_fw_gpio_probe(device_t dev)
347 if (!ofw_bus_status_okay(dev))
350 if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
353 device_set_desc(dev, "Raspberry Pi Firmware GPIO controller");
354 return (BUS_PROBE_DEFAULT);
358 rpi_fw_gpio_attach(device_t dev)
360 union msg_get_gpio_config cfg;
361 struct rpi_fw_gpio_softc *sc;
364 int i, nelems, elm_pos, rv;
366 sc = device_get_softc(dev);
367 sc->sc_firmware = device_get_parent(dev);
368 sx_init(&sc->sc_sx, "Raspberry Pi firmware gpio");
370 gpio = ofw_bus_get_node(dev);
371 if (!OF_hasprop(gpio, "gpio-controller"))
372 /* This is not a GPIO controller. */
375 nelems = OF_getprop_alloc(gpio, "gpio-line-names", (void **)&names);
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;
388 snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,
392 sc->sc_gpio_pins[i].gp_pin = i;
393 sc->sc_gpio_pins[i].gp_caps = RPI_FW_GPIO_DEFAULT_CAPS;
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;
403 sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_OUTPUT;
405 sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;
408 free(names, M_OFWPROP);
409 sc->sc_busdev = gpiobus_attach_bus(dev);
410 if (sc->sc_busdev == NULL)
416 sx_destroy(&sc->sc_sx);
422 rpi_fw_gpio_detach(device_t dev)
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),
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),
448 static devclass_t rpi_fw_gpio_devclass;
450 static driver_t rpi_fw_gpio_driver = {
453 sizeof(struct rpi_fw_gpio_softc),
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);