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); 458