1*9ca3eaf0SAndrew Turner /*- 2*9ca3eaf0SAndrew Turner * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*9ca3eaf0SAndrew Turner * 4*9ca3eaf0SAndrew Turner * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@FreeBSD.org> 5*9ca3eaf0SAndrew Turner * Copyright (c) 2012-2015 Luiz Otavio O Souza <loos@FreeBSD.org> 6*9ca3eaf0SAndrew Turner * All rights reserved. 7*9ca3eaf0SAndrew Turner * 8*9ca3eaf0SAndrew Turner * Redistribution and use in source and binary forms, with or without 9*9ca3eaf0SAndrew Turner * modification, are permitted provided that the following conditions 10*9ca3eaf0SAndrew Turner * are met: 11*9ca3eaf0SAndrew Turner * 1. Redistributions of source code must retain the above copyright 12*9ca3eaf0SAndrew Turner * notice, this list of conditions and the following disclaimer. 13*9ca3eaf0SAndrew Turner * 2. Redistributions in binary form must reproduce the above copyright 14*9ca3eaf0SAndrew Turner * notice, this list of conditions and the following disclaimer in the 15*9ca3eaf0SAndrew Turner * documentation and/or other materials provided with the distribution. 16*9ca3eaf0SAndrew Turner * 17*9ca3eaf0SAndrew Turner * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18*9ca3eaf0SAndrew Turner * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19*9ca3eaf0SAndrew Turner * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20*9ca3eaf0SAndrew Turner * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21*9ca3eaf0SAndrew Turner * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22*9ca3eaf0SAndrew Turner * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23*9ca3eaf0SAndrew Turner * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24*9ca3eaf0SAndrew Turner * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25*9ca3eaf0SAndrew Turner * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26*9ca3eaf0SAndrew Turner * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27*9ca3eaf0SAndrew Turner * SUCH DAMAGE. 28*9ca3eaf0SAndrew Turner * 29*9ca3eaf0SAndrew Turner */ 30*9ca3eaf0SAndrew Turner #include <sys/cdefs.h> 31*9ca3eaf0SAndrew Turner __FBSDID("$FreeBSD$"); 32*9ca3eaf0SAndrew Turner 33*9ca3eaf0SAndrew Turner #include "opt_platform.h" 34*9ca3eaf0SAndrew Turner 35*9ca3eaf0SAndrew Turner #include <sys/param.h> 36*9ca3eaf0SAndrew Turner #include <sys/systm.h> 37*9ca3eaf0SAndrew Turner #include <sys/bus.h> 38*9ca3eaf0SAndrew Turner #include <sys/gpio.h> 39*9ca3eaf0SAndrew Turner #include <sys/kernel.h> 40*9ca3eaf0SAndrew Turner #include <sys/lock.h> 41*9ca3eaf0SAndrew Turner #include <sys/module.h> 42*9ca3eaf0SAndrew Turner #include <sys/sx.h> 43*9ca3eaf0SAndrew Turner #include <sys/proc.h> 44*9ca3eaf0SAndrew Turner 45*9ca3eaf0SAndrew Turner #include <dev/gpio/gpiobusvar.h> 46*9ca3eaf0SAndrew Turner #include <dev/ofw/ofw_bus.h> 47*9ca3eaf0SAndrew Turner 48*9ca3eaf0SAndrew Turner #include <arm/broadcom/bcm2835/bcm2835_firmware.h> 49*9ca3eaf0SAndrew Turner 50*9ca3eaf0SAndrew Turner #include "gpio_if.h" 51*9ca3eaf0SAndrew Turner 52*9ca3eaf0SAndrew Turner #define RPI_FW_GPIO_PINS 8 53*9ca3eaf0SAndrew Turner #define RPI_FW_GPIO_BASE 128 54*9ca3eaf0SAndrew Turner #define RPI_FW_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) 55*9ca3eaf0SAndrew Turner 56*9ca3eaf0SAndrew Turner struct rpi_fw_gpio_softc { 57*9ca3eaf0SAndrew Turner device_t sc_busdev; 58*9ca3eaf0SAndrew Turner device_t sc_firmware; 59*9ca3eaf0SAndrew Turner struct sx sc_sx; 60*9ca3eaf0SAndrew Turner struct gpio_pin sc_gpio_pins[RPI_FW_GPIO_PINS]; 61*9ca3eaf0SAndrew Turner uint8_t sc_gpio_state; 62*9ca3eaf0SAndrew Turner }; 63*9ca3eaf0SAndrew Turner 64*9ca3eaf0SAndrew Turner #define RPI_FW_GPIO_LOCK(_sc) sx_xlock(&(_sc)->sc_sx) 65*9ca3eaf0SAndrew Turner #define RPI_FW_GPIO_UNLOCK(_sc) sx_xunlock(&(_sc)->sc_sx) 66*9ca3eaf0SAndrew Turner 67*9ca3eaf0SAndrew Turner static struct ofw_compat_data compat_data[] = { 68*9ca3eaf0SAndrew Turner {"raspberrypi,firmware-gpio", 1}, 69*9ca3eaf0SAndrew Turner {NULL, 0} 70*9ca3eaf0SAndrew Turner }; 71*9ca3eaf0SAndrew Turner 72*9ca3eaf0SAndrew Turner static int 73*9ca3eaf0SAndrew Turner rpi_fw_gpio_pin_configure(struct rpi_fw_gpio_softc *sc, struct gpio_pin *pin, 74*9ca3eaf0SAndrew Turner unsigned int flags) 75*9ca3eaf0SAndrew Turner { 76*9ca3eaf0SAndrew Turner union msg_get_gpio_config old_cfg; 77*9ca3eaf0SAndrew Turner union msg_set_gpio_config new_cfg; 78*9ca3eaf0SAndrew Turner int rv; 79*9ca3eaf0SAndrew Turner 80*9ca3eaf0SAndrew Turner bzero(&old_cfg, sizeof(old_cfg)); 81*9ca3eaf0SAndrew Turner bzero(&new_cfg, sizeof(new_cfg)); 82*9ca3eaf0SAndrew Turner old_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin; 83*9ca3eaf0SAndrew Turner 84*9ca3eaf0SAndrew Turner RPI_FW_GPIO_LOCK(sc); 85*9ca3eaf0SAndrew Turner rv = bcm2835_firmware_property(sc->sc_firmware, 86*9ca3eaf0SAndrew Turner BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &old_cfg, sizeof(old_cfg)); 87*9ca3eaf0SAndrew Turner if (rv == 0 && old_cfg.resp.gpio != 0) 88*9ca3eaf0SAndrew Turner rv = EIO; 89*9ca3eaf0SAndrew Turner if (rv != 0) 90*9ca3eaf0SAndrew Turner goto fail; 91*9ca3eaf0SAndrew Turner 92*9ca3eaf0SAndrew Turner new_cfg.req.gpio = RPI_FW_GPIO_BASE + pin->gp_pin; 93*9ca3eaf0SAndrew Turner if (flags & GPIO_PIN_INPUT) { 94*9ca3eaf0SAndrew Turner new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_IN; 95*9ca3eaf0SAndrew Turner new_cfg.req.state = 0; 96*9ca3eaf0SAndrew Turner pin->gp_flags = GPIO_PIN_INPUT; 97*9ca3eaf0SAndrew Turner } else if (flags & GPIO_PIN_OUTPUT) { 98*9ca3eaf0SAndrew Turner new_cfg.req.dir = BCM2835_FIRMWARE_GPIO_OUT; 99*9ca3eaf0SAndrew Turner if (flags & (GPIO_PIN_PRESET_HIGH | GPIO_PIN_PRESET_LOW)) { 100*9ca3eaf0SAndrew Turner if (flags & GPIO_PIN_PRESET_HIGH) { 101*9ca3eaf0SAndrew Turner new_cfg.req.state = 1; 102*9ca3eaf0SAndrew Turner sc->sc_gpio_state |= (1 << pin->gp_pin); 103*9ca3eaf0SAndrew Turner } else { 104*9ca3eaf0SAndrew Turner new_cfg.req.state = 0; 105*9ca3eaf0SAndrew Turner sc->sc_gpio_state &= ~(1 << pin->gp_pin); 106*9ca3eaf0SAndrew Turner } 107*9ca3eaf0SAndrew Turner } else { 108*9ca3eaf0SAndrew Turner if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0) { 109*9ca3eaf0SAndrew Turner new_cfg.req.state = 1; 110*9ca3eaf0SAndrew Turner } else { 111*9ca3eaf0SAndrew Turner new_cfg.req.state = 0; 112*9ca3eaf0SAndrew Turner } 113*9ca3eaf0SAndrew Turner } 114*9ca3eaf0SAndrew Turner pin->gp_flags = GPIO_PIN_OUTPUT; 115*9ca3eaf0SAndrew Turner } else { 116*9ca3eaf0SAndrew Turner new_cfg.req.dir = old_cfg.resp.dir; 117*9ca3eaf0SAndrew Turner /* Use the old state to decide high/low */ 118*9ca3eaf0SAndrew Turner if ((sc->sc_gpio_state & (1 << pin->gp_pin)) != 0) 119*9ca3eaf0SAndrew Turner new_cfg.req.state = 1; 120*9ca3eaf0SAndrew Turner else 121*9ca3eaf0SAndrew Turner new_cfg.req.state = 0; 122*9ca3eaf0SAndrew Turner } 123*9ca3eaf0SAndrew Turner new_cfg.req.pol = old_cfg.resp.pol; 124*9ca3eaf0SAndrew Turner new_cfg.req.term_en = 0; 125*9ca3eaf0SAndrew Turner new_cfg.req.term_pull_up = 0; 126*9ca3eaf0SAndrew Turner 127*9ca3eaf0SAndrew Turner rv = bcm2835_firmware_property(sc->sc_firmware, 128*9ca3eaf0SAndrew Turner BCM2835_FIRMWARE_TAG_SET_GPIO_CONFIG, &new_cfg, sizeof(new_cfg)); 129*9ca3eaf0SAndrew Turner 130*9ca3eaf0SAndrew Turner fail: 131*9ca3eaf0SAndrew Turner RPI_FW_GPIO_UNLOCK(sc); 132*9ca3eaf0SAndrew Turner 133*9ca3eaf0SAndrew Turner return (rv); 134*9ca3eaf0SAndrew Turner } 135*9ca3eaf0SAndrew Turner 136*9ca3eaf0SAndrew Turner static device_t 137*9ca3eaf0SAndrew Turner rpi_fw_gpio_get_bus(device_t dev) 138*9ca3eaf0SAndrew Turner { 139*9ca3eaf0SAndrew Turner struct rpi_fw_gpio_softc *sc; 140*9ca3eaf0SAndrew Turner 141*9ca3eaf0SAndrew Turner sc = device_get_softc(dev); 142*9ca3eaf0SAndrew Turner 143*9ca3eaf0SAndrew Turner return (sc->sc_busdev); 144*9ca3eaf0SAndrew Turner } 145*9ca3eaf0SAndrew Turner 146*9ca3eaf0SAndrew Turner static int 147*9ca3eaf0SAndrew Turner rpi_fw_gpio_pin_max(device_t dev, int *maxpin) 148*9ca3eaf0SAndrew Turner { 149*9ca3eaf0SAndrew Turner 150*9ca3eaf0SAndrew Turner *maxpin = RPI_FW_GPIO_PINS - 1; 151*9ca3eaf0SAndrew Turner return (0); 152*9ca3eaf0SAndrew Turner } 153*9ca3eaf0SAndrew Turner 154*9ca3eaf0SAndrew Turner static int 155*9ca3eaf0SAndrew Turner rpi_fw_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 156*9ca3eaf0SAndrew Turner { 157*9ca3eaf0SAndrew Turner struct rpi_fw_gpio_softc *sc; 158*9ca3eaf0SAndrew Turner int i; 159*9ca3eaf0SAndrew Turner 160*9ca3eaf0SAndrew Turner sc = device_get_softc(dev); 161*9ca3eaf0SAndrew Turner for (i = 0; i < RPI_FW_GPIO_PINS; i++) { 162*9ca3eaf0SAndrew Turner if (sc->sc_gpio_pins[i].gp_pin == pin) 163*9ca3eaf0SAndrew Turner break; 164*9ca3eaf0SAndrew Turner } 165*9ca3eaf0SAndrew Turner 166*9ca3eaf0SAndrew Turner if (i >= RPI_FW_GPIO_PINS) 167*9ca3eaf0SAndrew Turner return (EINVAL); 168*9ca3eaf0SAndrew Turner 169*9ca3eaf0SAndrew Turner *caps = RPI_FW_GPIO_DEFAULT_CAPS; 170*9ca3eaf0SAndrew Turner return (0); 171*9ca3eaf0SAndrew Turner } 172*9ca3eaf0SAndrew Turner 173*9ca3eaf0SAndrew Turner static int 174*9ca3eaf0SAndrew Turner rpi_fw_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 175*9ca3eaf0SAndrew Turner { 176*9ca3eaf0SAndrew Turner struct rpi_fw_gpio_softc *sc = device_get_softc(dev); 177*9ca3eaf0SAndrew Turner int i; 178*9ca3eaf0SAndrew Turner 179*9ca3eaf0SAndrew Turner for (i = 0; i < RPI_FW_GPIO_PINS; i++) { 180*9ca3eaf0SAndrew Turner if (sc->sc_gpio_pins[i].gp_pin == pin) 181*9ca3eaf0SAndrew Turner break; 182*9ca3eaf0SAndrew Turner } 183*9ca3eaf0SAndrew Turner 184*9ca3eaf0SAndrew Turner if (i >= RPI_FW_GPIO_PINS) 185*9ca3eaf0SAndrew Turner return (EINVAL); 186*9ca3eaf0SAndrew Turner 187*9ca3eaf0SAndrew Turner RPI_FW_GPIO_LOCK(sc); 188*9ca3eaf0SAndrew Turner *flags = sc->sc_gpio_pins[i].gp_flags; 189*9ca3eaf0SAndrew Turner RPI_FW_GPIO_UNLOCK(sc); 190*9ca3eaf0SAndrew Turner 191*9ca3eaf0SAndrew Turner return (0); 192*9ca3eaf0SAndrew Turner } 193*9ca3eaf0SAndrew Turner 194*9ca3eaf0SAndrew Turner static int 195*9ca3eaf0SAndrew Turner rpi_fw_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 196*9ca3eaf0SAndrew Turner { 197*9ca3eaf0SAndrew Turner struct rpi_fw_gpio_softc *sc; 198*9ca3eaf0SAndrew Turner int i; 199*9ca3eaf0SAndrew Turner 200*9ca3eaf0SAndrew Turner sc = device_get_softc(dev); 201*9ca3eaf0SAndrew Turner for (i = 0; i < RPI_FW_GPIO_PINS; i++) { 202*9ca3eaf0SAndrew Turner if (sc->sc_gpio_pins[i].gp_pin == pin) 203*9ca3eaf0SAndrew Turner break; 204*9ca3eaf0SAndrew Turner } 205*9ca3eaf0SAndrew Turner 206*9ca3eaf0SAndrew Turner if (i >= RPI_FW_GPIO_PINS) 207*9ca3eaf0SAndrew Turner return (EINVAL); 208*9ca3eaf0SAndrew Turner 209*9ca3eaf0SAndrew Turner RPI_FW_GPIO_LOCK(sc); 210*9ca3eaf0SAndrew Turner memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); 211*9ca3eaf0SAndrew Turner RPI_FW_GPIO_UNLOCK(sc); 212*9ca3eaf0SAndrew Turner 213*9ca3eaf0SAndrew Turner return (0); 214*9ca3eaf0SAndrew Turner } 215*9ca3eaf0SAndrew Turner 216*9ca3eaf0SAndrew Turner static int 217*9ca3eaf0SAndrew Turner rpi_fw_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 218*9ca3eaf0SAndrew Turner { 219*9ca3eaf0SAndrew Turner struct rpi_fw_gpio_softc *sc; 220*9ca3eaf0SAndrew Turner int i; 221*9ca3eaf0SAndrew Turner 222*9ca3eaf0SAndrew Turner sc = device_get_softc(dev); 223*9ca3eaf0SAndrew Turner for (i = 0; i < RPI_FW_GPIO_PINS; i++) { 224*9ca3eaf0SAndrew Turner if (sc->sc_gpio_pins[i].gp_pin == pin) 225*9ca3eaf0SAndrew Turner break; 226*9ca3eaf0SAndrew Turner } 227*9ca3eaf0SAndrew Turner 228*9ca3eaf0SAndrew Turner if (i >= RPI_FW_GPIO_PINS) 229*9ca3eaf0SAndrew Turner return (EINVAL); 230*9ca3eaf0SAndrew Turner 231*9ca3eaf0SAndrew Turner return (rpi_fw_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags)); 232*9ca3eaf0SAndrew Turner } 233*9ca3eaf0SAndrew Turner 234*9ca3eaf0SAndrew Turner static int 235*9ca3eaf0SAndrew Turner rpi_fw_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) 236*9ca3eaf0SAndrew Turner { 237*9ca3eaf0SAndrew Turner struct rpi_fw_gpio_softc *sc; 238*9ca3eaf0SAndrew Turner union msg_set_gpio_state state; 239*9ca3eaf0SAndrew Turner int i, rv; 240*9ca3eaf0SAndrew Turner 241*9ca3eaf0SAndrew Turner sc = device_get_softc(dev); 242*9ca3eaf0SAndrew Turner for (i = 0; i < RPI_FW_GPIO_PINS; i++) { 243*9ca3eaf0SAndrew Turner if (sc->sc_gpio_pins[i].gp_pin == pin) 244*9ca3eaf0SAndrew Turner break; 245*9ca3eaf0SAndrew Turner } 246*9ca3eaf0SAndrew Turner if (i >= RPI_FW_GPIO_PINS) 247*9ca3eaf0SAndrew Turner return (EINVAL); 248*9ca3eaf0SAndrew Turner 249*9ca3eaf0SAndrew Turner state.req.gpio = RPI_FW_GPIO_BASE + pin; 250*9ca3eaf0SAndrew Turner state.req.state = value; 251*9ca3eaf0SAndrew Turner 252*9ca3eaf0SAndrew Turner RPI_FW_GPIO_LOCK(sc); 253*9ca3eaf0SAndrew Turner rv = bcm2835_firmware_property(sc->sc_firmware, 254*9ca3eaf0SAndrew Turner BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &state, sizeof(state)); 255*9ca3eaf0SAndrew Turner /* The firmware sets gpio to 0 on success */ 256*9ca3eaf0SAndrew Turner if (rv == 0 && state.resp.gpio != 0) 257*9ca3eaf0SAndrew Turner rv = EINVAL; 258*9ca3eaf0SAndrew Turner if (rv == 0) { 259*9ca3eaf0SAndrew Turner sc->sc_gpio_pins[i].gp_flags &= ~(GPIO_PIN_PRESET_HIGH | 260*9ca3eaf0SAndrew Turner GPIO_PIN_PRESET_LOW); 261*9ca3eaf0SAndrew Turner if (value) 262*9ca3eaf0SAndrew Turner sc->sc_gpio_state |= (1 << i); 263*9ca3eaf0SAndrew Turner else 264*9ca3eaf0SAndrew Turner sc->sc_gpio_state &= ~(1 << i); 265*9ca3eaf0SAndrew Turner } 266*9ca3eaf0SAndrew Turner RPI_FW_GPIO_UNLOCK(sc); 267*9ca3eaf0SAndrew Turner 268*9ca3eaf0SAndrew Turner return (rv); 269*9ca3eaf0SAndrew Turner } 270*9ca3eaf0SAndrew Turner 271*9ca3eaf0SAndrew Turner static int 272*9ca3eaf0SAndrew Turner rpi_fw_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) 273*9ca3eaf0SAndrew Turner { 274*9ca3eaf0SAndrew Turner struct rpi_fw_gpio_softc *sc; 275*9ca3eaf0SAndrew Turner union msg_get_gpio_state state; 276*9ca3eaf0SAndrew Turner int i, rv; 277*9ca3eaf0SAndrew Turner 278*9ca3eaf0SAndrew Turner sc = device_get_softc(dev); 279*9ca3eaf0SAndrew Turner for (i = 0; i < RPI_FW_GPIO_PINS; i++) { 280*9ca3eaf0SAndrew Turner if (sc->sc_gpio_pins[i].gp_pin == pin) 281*9ca3eaf0SAndrew Turner break; 282*9ca3eaf0SAndrew Turner } 283*9ca3eaf0SAndrew Turner if (i >= RPI_FW_GPIO_PINS) 284*9ca3eaf0SAndrew Turner return (EINVAL); 285*9ca3eaf0SAndrew Turner 286*9ca3eaf0SAndrew Turner bzero(&state, sizeof(state)); 287*9ca3eaf0SAndrew Turner state.req.gpio = RPI_FW_GPIO_BASE + pin; 288*9ca3eaf0SAndrew Turner 289*9ca3eaf0SAndrew Turner RPI_FW_GPIO_LOCK(sc); 290*9ca3eaf0SAndrew Turner rv = bcm2835_firmware_property(sc->sc_firmware, 291*9ca3eaf0SAndrew Turner BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &state, sizeof(state)); 292*9ca3eaf0SAndrew Turner RPI_FW_GPIO_UNLOCK(sc); 293*9ca3eaf0SAndrew Turner 294*9ca3eaf0SAndrew Turner /* The firmware sets gpio to 0 on success */ 295*9ca3eaf0SAndrew Turner if (rv == 0 && state.resp.gpio != 0) 296*9ca3eaf0SAndrew Turner rv = EINVAL; 297*9ca3eaf0SAndrew Turner if (rv == 0) 298*9ca3eaf0SAndrew Turner *val = !state.resp.state; 299*9ca3eaf0SAndrew Turner 300*9ca3eaf0SAndrew Turner return (rv); 301*9ca3eaf0SAndrew Turner } 302*9ca3eaf0SAndrew Turner 303*9ca3eaf0SAndrew Turner static int 304*9ca3eaf0SAndrew Turner rpi_fw_gpio_pin_toggle(device_t dev, uint32_t pin) 305*9ca3eaf0SAndrew Turner { 306*9ca3eaf0SAndrew Turner struct rpi_fw_gpio_softc *sc; 307*9ca3eaf0SAndrew Turner union msg_get_gpio_state old_state; 308*9ca3eaf0SAndrew Turner union msg_set_gpio_state new_state; 309*9ca3eaf0SAndrew Turner int i, rv; 310*9ca3eaf0SAndrew Turner 311*9ca3eaf0SAndrew Turner sc = device_get_softc(dev); 312*9ca3eaf0SAndrew Turner for (i = 0; i < RPI_FW_GPIO_PINS; i++) { 313*9ca3eaf0SAndrew Turner if (sc->sc_gpio_pins[i].gp_pin == pin) 314*9ca3eaf0SAndrew Turner break; 315*9ca3eaf0SAndrew Turner } 316*9ca3eaf0SAndrew Turner if (i >= RPI_FW_GPIO_PINS) 317*9ca3eaf0SAndrew Turner return (EINVAL); 318*9ca3eaf0SAndrew Turner 319*9ca3eaf0SAndrew Turner bzero(&old_state, sizeof(old_state)); 320*9ca3eaf0SAndrew Turner bzero(&new_state, sizeof(new_state)); 321*9ca3eaf0SAndrew Turner 322*9ca3eaf0SAndrew Turner old_state.req.gpio = RPI_FW_GPIO_BASE + pin; 323*9ca3eaf0SAndrew Turner new_state.req.gpio = RPI_FW_GPIO_BASE + pin; 324*9ca3eaf0SAndrew Turner 325*9ca3eaf0SAndrew Turner RPI_FW_GPIO_LOCK(sc); 326*9ca3eaf0SAndrew Turner rv = bcm2835_firmware_property(sc->sc_firmware, 327*9ca3eaf0SAndrew Turner BCM2835_FIRMWARE_TAG_GET_GPIO_STATE, &old_state, sizeof(old_state)); 328*9ca3eaf0SAndrew Turner /* The firmware sets gpio to 0 on success */ 329*9ca3eaf0SAndrew Turner if (rv == 0 && old_state.resp.gpio == 0) { 330*9ca3eaf0SAndrew Turner /* Set the new state to invert the GPIO */ 331*9ca3eaf0SAndrew Turner new_state.req.state = !old_state.resp.state; 332*9ca3eaf0SAndrew Turner rv = bcm2835_firmware_property(sc->sc_firmware, 333*9ca3eaf0SAndrew Turner BCM2835_FIRMWARE_TAG_SET_GPIO_STATE, &new_state, 334*9ca3eaf0SAndrew Turner sizeof(new_state)); 335*9ca3eaf0SAndrew Turner } 336*9ca3eaf0SAndrew Turner if (rv == 0 && (old_state.resp.gpio != 0 || new_state.resp.gpio != 0)) 337*9ca3eaf0SAndrew Turner rv = EINVAL; 338*9ca3eaf0SAndrew Turner RPI_FW_GPIO_UNLOCK(sc); 339*9ca3eaf0SAndrew Turner 340*9ca3eaf0SAndrew Turner return (rv); 341*9ca3eaf0SAndrew Turner } 342*9ca3eaf0SAndrew Turner 343*9ca3eaf0SAndrew Turner static int 344*9ca3eaf0SAndrew Turner rpi_fw_gpio_probe(device_t dev) 345*9ca3eaf0SAndrew Turner { 346*9ca3eaf0SAndrew Turner 347*9ca3eaf0SAndrew Turner if (!ofw_bus_status_okay(dev)) 348*9ca3eaf0SAndrew Turner return (ENXIO); 349*9ca3eaf0SAndrew Turner 350*9ca3eaf0SAndrew Turner if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 351*9ca3eaf0SAndrew Turner return (ENXIO); 352*9ca3eaf0SAndrew Turner 353*9ca3eaf0SAndrew Turner device_set_desc(dev, "Raspberry Pi Firmware GPIO controller"); 354*9ca3eaf0SAndrew Turner return (BUS_PROBE_DEFAULT); 355*9ca3eaf0SAndrew Turner } 356*9ca3eaf0SAndrew Turner 357*9ca3eaf0SAndrew Turner static int 358*9ca3eaf0SAndrew Turner rpi_fw_gpio_attach(device_t dev) 359*9ca3eaf0SAndrew Turner { 360*9ca3eaf0SAndrew Turner union msg_get_gpio_config cfg; 361*9ca3eaf0SAndrew Turner struct rpi_fw_gpio_softc *sc; 362*9ca3eaf0SAndrew Turner char *names; 363*9ca3eaf0SAndrew Turner phandle_t gpio; 364*9ca3eaf0SAndrew Turner int i, nelems, elm_pos, rv; 365*9ca3eaf0SAndrew Turner 366*9ca3eaf0SAndrew Turner sc = device_get_softc(dev); 367*9ca3eaf0SAndrew Turner sc->sc_firmware = device_get_parent(dev); 368*9ca3eaf0SAndrew Turner sx_init(&sc->sc_sx, "Raspberry Pi firmware gpio"); 369*9ca3eaf0SAndrew Turner /* Find our node. */ 370*9ca3eaf0SAndrew Turner gpio = ofw_bus_get_node(dev); 371*9ca3eaf0SAndrew Turner if (!OF_hasprop(gpio, "gpio-controller")) 372*9ca3eaf0SAndrew Turner /* This is not a GPIO controller. */ 373*9ca3eaf0SAndrew Turner goto fail; 374*9ca3eaf0SAndrew Turner 375*9ca3eaf0SAndrew Turner nelems = OF_getprop_alloc(gpio, "gpio-line-names", (void **)&names); 376*9ca3eaf0SAndrew Turner if (nelems <= 0) 377*9ca3eaf0SAndrew Turner names = NULL; 378*9ca3eaf0SAndrew Turner elm_pos = 0; 379*9ca3eaf0SAndrew Turner for (i = 0; i < RPI_FW_GPIO_PINS; i++) { 380*9ca3eaf0SAndrew Turner /* Set the current pin name */ 381*9ca3eaf0SAndrew Turner if (names != NULL && elm_pos < nelems && 382*9ca3eaf0SAndrew Turner names[elm_pos] != '\0') { 383*9ca3eaf0SAndrew Turner snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, 384*9ca3eaf0SAndrew Turner "%s", names + elm_pos); 385*9ca3eaf0SAndrew Turner /* Find the next pin name */ 386*9ca3eaf0SAndrew Turner elm_pos += strlen(names + elm_pos) + 1; 387*9ca3eaf0SAndrew Turner } else { 388*9ca3eaf0SAndrew Turner snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, 389*9ca3eaf0SAndrew Turner "pin %d", i); 390*9ca3eaf0SAndrew Turner } 391*9ca3eaf0SAndrew Turner 392*9ca3eaf0SAndrew Turner sc->sc_gpio_pins[i].gp_pin = i; 393*9ca3eaf0SAndrew Turner sc->sc_gpio_pins[i].gp_caps = RPI_FW_GPIO_DEFAULT_CAPS; 394*9ca3eaf0SAndrew Turner 395*9ca3eaf0SAndrew Turner bzero(&cfg, sizeof(cfg)); 396*9ca3eaf0SAndrew Turner cfg.req.gpio = RPI_FW_GPIO_BASE + i; 397*9ca3eaf0SAndrew Turner rv = bcm2835_firmware_property(sc->sc_firmware, 398*9ca3eaf0SAndrew Turner BCM2835_FIRMWARE_TAG_GET_GPIO_CONFIG, &cfg, sizeof(cfg)); 399*9ca3eaf0SAndrew Turner if (rv == 0 && cfg.resp.gpio == 0) { 400*9ca3eaf0SAndrew Turner if (cfg.resp.dir == BCM2835_FIRMWARE_GPIO_IN) 401*9ca3eaf0SAndrew Turner sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT; 402*9ca3eaf0SAndrew Turner else 403*9ca3eaf0SAndrew Turner sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_OUTPUT; 404*9ca3eaf0SAndrew Turner } else { 405*9ca3eaf0SAndrew Turner sc->sc_gpio_pins[i].gp_flags = GPIO_PIN_INPUT; 406*9ca3eaf0SAndrew Turner } 407*9ca3eaf0SAndrew Turner } 408*9ca3eaf0SAndrew Turner free(names, M_OFWPROP); 409*9ca3eaf0SAndrew Turner sc->sc_busdev = gpiobus_attach_bus(dev); 410*9ca3eaf0SAndrew Turner if (sc->sc_busdev == NULL) 411*9ca3eaf0SAndrew Turner goto fail; 412*9ca3eaf0SAndrew Turner 413*9ca3eaf0SAndrew Turner return (0); 414*9ca3eaf0SAndrew Turner 415*9ca3eaf0SAndrew Turner fail: 416*9ca3eaf0SAndrew Turner sx_destroy(&sc->sc_sx); 417*9ca3eaf0SAndrew Turner 418*9ca3eaf0SAndrew Turner return (ENXIO); 419*9ca3eaf0SAndrew Turner } 420*9ca3eaf0SAndrew Turner 421*9ca3eaf0SAndrew Turner static int 422*9ca3eaf0SAndrew Turner rpi_fw_gpio_detach(device_t dev) 423*9ca3eaf0SAndrew Turner { 424*9ca3eaf0SAndrew Turner 425*9ca3eaf0SAndrew Turner return (EBUSY); 426*9ca3eaf0SAndrew Turner } 427*9ca3eaf0SAndrew Turner 428*9ca3eaf0SAndrew Turner static device_method_t rpi_fw_gpio_methods[] = { 429*9ca3eaf0SAndrew Turner /* Device interface */ 430*9ca3eaf0SAndrew Turner DEVMETHOD(device_probe, rpi_fw_gpio_probe), 431*9ca3eaf0SAndrew Turner DEVMETHOD(device_attach, rpi_fw_gpio_attach), 432*9ca3eaf0SAndrew Turner DEVMETHOD(device_detach, rpi_fw_gpio_detach), 433*9ca3eaf0SAndrew Turner 434*9ca3eaf0SAndrew Turner /* GPIO protocol */ 435*9ca3eaf0SAndrew Turner DEVMETHOD(gpio_get_bus, rpi_fw_gpio_get_bus), 436*9ca3eaf0SAndrew Turner DEVMETHOD(gpio_pin_max, rpi_fw_gpio_pin_max), 437*9ca3eaf0SAndrew Turner DEVMETHOD(gpio_pin_getname, rpi_fw_gpio_pin_getname), 438*9ca3eaf0SAndrew Turner DEVMETHOD(gpio_pin_getflags, rpi_fw_gpio_pin_getflags), 439*9ca3eaf0SAndrew Turner DEVMETHOD(gpio_pin_getcaps, rpi_fw_gpio_pin_getcaps), 440*9ca3eaf0SAndrew Turner DEVMETHOD(gpio_pin_setflags, rpi_fw_gpio_pin_setflags), 441*9ca3eaf0SAndrew Turner DEVMETHOD(gpio_pin_get, rpi_fw_gpio_pin_get), 442*9ca3eaf0SAndrew Turner DEVMETHOD(gpio_pin_set, rpi_fw_gpio_pin_set), 443*9ca3eaf0SAndrew Turner DEVMETHOD(gpio_pin_toggle, rpi_fw_gpio_pin_toggle), 444*9ca3eaf0SAndrew Turner 445*9ca3eaf0SAndrew Turner DEVMETHOD_END 446*9ca3eaf0SAndrew Turner }; 447*9ca3eaf0SAndrew Turner 448*9ca3eaf0SAndrew Turner static devclass_t rpi_fw_gpio_devclass; 449*9ca3eaf0SAndrew Turner 450*9ca3eaf0SAndrew Turner static driver_t rpi_fw_gpio_driver = { 451*9ca3eaf0SAndrew Turner "gpio", 452*9ca3eaf0SAndrew Turner rpi_fw_gpio_methods, 453*9ca3eaf0SAndrew Turner sizeof(struct rpi_fw_gpio_softc), 454*9ca3eaf0SAndrew Turner }; 455*9ca3eaf0SAndrew Turner 456*9ca3eaf0SAndrew Turner EARLY_DRIVER_MODULE(rpi_fw_gpio, bcm2835_firmware, rpi_fw_gpio_driver, 457*9ca3eaf0SAndrew Turner rpi_fw_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); 458