Path: blob/main/sys/arm/broadcom/bcm2835/raspberrypi_gpio.c
39566 views
/*-1* SPDX-License-Identifier: BSD-2-Clause2*3* Copyright (c) 2012 Oleksandr Tymoshenko <[email protected]>4* Copyright (c) 2012-2015 Luiz Otavio O Souza <[email protected]>5* All rights reserved.6*7* Redistribution and use in source and binary forms, with or without8* modification, are permitted provided that the following conditions9* are met:10* 1. Redistributions of source code must retain the above copyright11* notice, this list of conditions and the following disclaimer.12* 2. Redistributions in binary form must reproduce the above copyright13* notice, this list of conditions and the following disclaimer in the14* documentation and/or other materials provided with the distribution.15*16* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*28*/29#include <sys/cdefs.h>30#include "opt_platform.h"3132#include <sys/param.h>33#include <sys/systm.h>34#include <sys/bus.h>35#include <sys/gpio.h>36#include <sys/kernel.h>37#include <sys/lock.h>38#include <sys/module.h>39#include <sys/sx.h>40#include <sys/proc.h>4142#include <dev/gpio/gpiobusvar.h>43#include <dev/ofw/ofw_bus.h>4445#include <arm/broadcom/bcm2835/bcm2835_firmware.h>4647#include "gpio_if.h"4849#define RPI_FW_GPIO_PINS 850#define RPI_FW_GPIO_BASE 12851#define RPI_FW_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)5253struct rpi_fw_gpio_softc {54device_t sc_busdev;55device_t sc_firmware;56struct sx sc_sx;57struct gpio_pin sc_gpio_pins[RPI_FW_GPIO_PINS];58uint8_t sc_gpio_state;59};6061#define RPI_FW_GPIO_LOCK(_sc) sx_xlock(&(_sc)->sc_sx)62#define RPI_FW_GPIO_UNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx)6364static struct ofw_compat_data compat_data[] = {65{"raspberrypi,firmware-gpio", 1},66{NULL, 0}67};6869static int70rpi_fw_gpio_pin_configure(struct rpi_fw_gpio_softc *sc, struct gpio_pin *pin,71unsigned int flags)72{73union msg_get_gpio_config old_cfg;74union msg_set_gpio_config new_cfg;75int rv;7677bzero(&old_cfg, sizeof(old_cfg));78bzero(&new_cfg, sizeof(new_cfg));79old_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin;8081RPI_FW_GPIO_LOCK(sc);82rv = bcm2835_firmware_property(sc->sc_firmware,83BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &old_cfg, sizeof(old_cfg));84if (rv == 0 && old_cfg.resp.gpio != 0)85rv = EIO;86if (rv != 0)87goto fail;8889new_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin;90if (flags & GPIO_PIN_INPUT) {91new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_IN;92new_cfg.req.state = 0;93pin->gp_flags = GPIO_PIN_INPUT;94} else if (flags & GPIO_PIN_OUTPUT) {95new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_OUT;96if (flags & (GPIO_PIN_PRESET_HIGH | GPIO_PIN_PRESET_LOW)) {97if (flags & GPIO_PIN_PRESET_HIGH) {98new_cfg.req.state = 1;99sc->sc_gpio_state |= (1 << pin->gp_pin);100} else {101new_cfg.req.state = 0;102sc->sc_gpio_state &= ~(1 << pin->gp_pin);103}104} else {105if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0) {106new_cfg.req.state = 1;107} else {108new_cfg.req.state = 0;109}110}111pin->gp_flags = GPIO_PIN_OUTPUT;112} else {113new_cfg.req.dir = old_cfg.resp.dir;114/* Use the old state to decide high/low */115if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0)116new_cfg.req.state = 1;117else118new_cfg.req.state = 0;119}120new_cfg.req.pol = old_cfg.resp.pol;121new_cfg.req.term_en = 0;122new_cfg.req.term_pull_up = 0;123124rv = bcm2835_firmware_property(sc->sc_firmware,125BCM2835_FIRMWARE_TAG_SET_GPIO_CONFIG, &new_cfg, sizeof(new_cfg));126127fail:128RPI_FW_GPIO_UNLOCK(sc);129130return (rv);131}132133static device_t134rpi_fw_gpio_get_bus(device_t dev)135{136struct rpi_fw_gpio_softc *sc;137138sc = device_get_softc(dev);139140return (sc->sc_busdev);141}142143static int144rpi_fw_gpio_pin_max(device_t dev, int *maxpin)145{146147*maxpin = RPI_FW_GPIO_PINS - 1;148return (0);149}150151static int152rpi_fw_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps)153{154struct rpi_fw_gpio_softc *sc;155int i;156157sc = device_get_softc(dev);158for (i = 0; i < RPI_FW_GPIO_PINS; i++) {159if (sc->sc_gpio_pins[i].gp_pin == pin)160break;161}162163if (i >= RPI_FW_GPIO_PINS)164return (EINVAL);165166*caps = RPI_FW_GPIO_DEFAULT_CAPS;167return (0);168}169170static int171rpi_fw_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags)172{173struct rpi_fw_gpio_softc *sc = device_get_softc(dev);174int i;175176for (i = 0; i < RPI_FW_GPIO_PINS; i++) {177if (sc->sc_gpio_pins[i].gp_pin == pin)178break;179}180181if (i >= RPI_FW_GPIO_PINS)182return (EINVAL);183184RPI_FW_GPIO_LOCK(sc);185*flags = sc->sc_gpio_pins[i].gp_flags;186RPI_FW_GPIO_UNLOCK(sc);187188return (0);189}190191static int192rpi_fw_gpio_pin_getname(device_t dev, uint32_t pin, char *name)193{194struct rpi_fw_gpio_softc *sc;195int i;196197sc = device_get_softc(dev);198for (i = 0; i < RPI_FW_GPIO_PINS; i++) {199if (sc->sc_gpio_pins[i].gp_pin == pin)200break;201}202203if (i >= RPI_FW_GPIO_PINS)204return (EINVAL);205206RPI_FW_GPIO_LOCK(sc);207memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME);208RPI_FW_GPIO_UNLOCK(sc);209210return (0);211}212213static int214rpi_fw_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags)215{216struct rpi_fw_gpio_softc *sc;217int i;218219sc = device_get_softc(dev);220for (i = 0; i < RPI_FW_GPIO_PINS; i++) {221if (sc->sc_gpio_pins[i].gp_pin == pin)222break;223}224225if (i >= RPI_FW_GPIO_PINS)226return (EINVAL);227228return (rpi_fw_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags));229}230231static int232rpi_fw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value)233{234struct rpi_fw_gpio_softc *sc;235union msg_set_gpio_state state;236int i, rv;237238sc = device_get_softc(dev);239for (i = 0; i < RPI_FW_GPIO_PINS; i++) {240if (sc->sc_gpio_pins[i].gp_pin == pin)241break;242}243if (i >= RPI_FW_GPIO_PINS)244return (EINVAL);245246state.req.gpio = RPI_FW_GPIO_BASE + pin;247state.req.state = value;248249RPI_FW_GPIO_LOCK(sc);250rv = bcm2835_firmware_property(sc->sc_firmware,251BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &state, sizeof(state));252/* The firmware sets gpio to 0 on success */253if (rv == 0 && state.resp.gpio != 0)254rv = EINVAL;255if (rv == 0) {256sc->sc_gpio_pins[i].gp_flags &= ~(GPIO_PIN_PRESET_HIGH |257GPIO_PIN_PRESET_LOW);258if (value)259sc->sc_gpio_state |= (1 << i);260else261sc->sc_gpio_state &= ~(1 << i);262}263RPI_FW_GPIO_UNLOCK(sc);264265return (rv);266}267268static int269rpi_fw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val)270{271struct rpi_fw_gpio_softc *sc;272union msg_get_gpio_state state;273int i, rv;274275sc = device_get_softc(dev);276for (i = 0; i < RPI_FW_GPIO_PINS; i++) {277if (sc->sc_gpio_pins[i].gp_pin == pin)278break;279}280if (i >= RPI_FW_GPIO_PINS)281return (EINVAL);282283bzero(&state, sizeof(state));284state.req.gpio = RPI_FW_GPIO_BASE + pin;285286RPI_FW_GPIO_LOCK(sc);287rv = bcm2835_firmware_property(sc->sc_firmware,288BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &state, sizeof(state));289RPI_FW_GPIO_UNLOCK(sc);290291/* The firmware sets gpio to 0 on success */292if (rv == 0 && state.resp.gpio != 0)293rv = EINVAL;294if (rv == 0)295*val = !state.resp.state;296297return (rv);298}299300static int301rpi_fw_gpio_pin_toggle(device_t dev, uint32_t pin)302{303struct rpi_fw_gpio_softc *sc;304union msg_get_gpio_state old_state;305union msg_set_gpio_state new_state;306int i, rv;307308sc = device_get_softc(dev);309for (i = 0; i < RPI_FW_GPIO_PINS; i++) {310if (sc->sc_gpio_pins[i].gp_pin == pin)311break;312}313if (i >= RPI_FW_GPIO_PINS)314return (EINVAL);315316bzero(&old_state, sizeof(old_state));317bzero(&new_state, sizeof(new_state));318319old_state.req.gpio = RPI_FW_GPIO_BASE + pin;320new_state.req.gpio = RPI_FW_GPIO_BASE + pin;321322RPI_FW_GPIO_LOCK(sc);323rv = bcm2835_firmware_property(sc->sc_firmware,324BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &old_state, sizeof(old_state));325/* The firmware sets gpio to 0 on success */326if (rv == 0 && old_state.resp.gpio == 0) {327/* Set the new state to invert the GPIO */328new_state.req.state = !old_state.resp.state;329rv = bcm2835_firmware_property(sc->sc_firmware,330BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &new_state,331sizeof(new_state));332}333if (rv == 0 && (old_state.resp.gpio != 0 || new_state.resp.gpio != 0))334rv = EINVAL;335RPI_FW_GPIO_UNLOCK(sc);336337return (rv);338}339340static int341rpi_fw_gpio_probe(device_t dev)342{343344if (!ofw_bus_status_okay(dev))345return (ENXIO);346347if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)348return (ENXIO);349350device_set_desc(dev, "Raspberry Pi Firmware GPIO controller");351return (BUS_PROBE_DEFAULT);352}353354static int355rpi_fw_gpio_attach(device_t dev)356{357union msg_get_gpio_config cfg;358struct rpi_fw_gpio_softc *sc;359char *names;360phandle_t gpio;361int i, nelems, elm_pos, rv;362363sc = device_get_softc(dev);364sc->sc_firmware = device_get_parent(dev);365sx_init(&sc->sc_sx, "Raspberry Pi firmware gpio");366/* Find our node. */367gpio = ofw_bus_get_node(dev);368if (!OF_hasprop(gpio, "gpio-controller"))369/* This is not a GPIO controller. */370goto fail;371372nelems = OF_getprop_alloc(gpio, "gpio-line-names", (void **)&names);373if (nelems <= 0)374names = NULL;375elm_pos = 0;376for (i = 0; i < RPI_FW_GPIO_PINS; i++) {377/* Set the current pin name */378if (names != NULL && elm_pos < nelems &&379names[elm_pos] != '\0') {380snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,381"%s", names + elm_pos);382/* Find the next pin name */383elm_pos += strlen(names + elm_pos) + 1;384} else {385snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME,386"pin %d", i);387}388389sc->sc_gpio_pins[i].gp_pin = i;390sc->sc_gpio_pins[i].gp_caps = RPI_FW_GPIO_DEFAULT_CAPS;391392bzero(&cfg, sizeof(cfg));393cfg.req.gpio = RPI_FW_GPIO_BASE + i;394rv = bcm2835_firmware_property(sc->sc_firmware,395BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &cfg, sizeof(cfg));396if (rv == 0 && cfg.resp.gpio == 0) {397if (cfg.resp.dir == BCM2835_FIRMWARE_GPIO_IN)398sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;399else400sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_OUTPUT;401} else {402sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT;403}404}405free(names, M_OFWPROP);406sc->sc_busdev = gpiobus_add_bus(dev);407if (sc->sc_busdev == NULL)408goto fail;409410bus_attach_children(dev);411return (0);412413fail:414sx_destroy(&sc->sc_sx);415416return (ENXIO);417}418419static int420rpi_fw_gpio_detach(device_t dev)421{422423return (EBUSY);424}425426static device_method_t rpi_fw_gpio_methods[] = {427/* Device interface */428DEVMETHOD(device_probe, rpi_fw_gpio_probe),429DEVMETHOD(device_attach, rpi_fw_gpio_attach),430DEVMETHOD(device_detach, rpi_fw_gpio_detach),431432/* GPIO protocol */433DEVMETHOD(gpio_get_bus, rpi_fw_gpio_get_bus),434DEVMETHOD(gpio_pin_max, rpi_fw_gpio_pin_max),435DEVMETHOD(gpio_pin_getname, rpi_fw_gpio_pin_getname),436DEVMETHOD(gpio_pin_getflags, rpi_fw_gpio_pin_getflags),437DEVMETHOD(gpio_pin_getcaps, rpi_fw_gpio_pin_getcaps),438DEVMETHOD(gpio_pin_setflags, rpi_fw_gpio_pin_setflags),439DEVMETHOD(gpio_pin_get, rpi_fw_gpio_pin_get),440DEVMETHOD(gpio_pin_set, rpi_fw_gpio_pin_set),441DEVMETHOD(gpio_pin_toggle, rpi_fw_gpio_pin_toggle),442443DEVMETHOD_END444};445446static driver_t rpi_fw_gpio_driver = {447"gpio",448rpi_fw_gpio_methods,449sizeof(struct rpi_fw_gpio_softc),450};451452EARLY_DRIVER_MODULE(rpi_fw_gpio, bcm2835_firmware, rpi_fw_gpio_driver, 0, 0,453BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);454455456