1243465Sgonzo/*- 2243465Sgonzo * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> 3243465Sgonzo * Copyright (c) 2012 Luiz Otavio O Souza. 4243465Sgonzo * All rights reserved. 5243465Sgonzo * 6243465Sgonzo * Redistribution and use in source and binary forms, with or without 7243465Sgonzo * modification, are permitted provided that the following conditions 8243465Sgonzo * are met: 9243465Sgonzo * 1. Redistributions of source code must retain the above copyright 10243465Sgonzo * notice, this list of conditions and the following disclaimer. 11243465Sgonzo * 2. Redistributions in binary form must reproduce the above copyright 12243465Sgonzo * notice, this list of conditions and the following disclaimer in the 13243465Sgonzo * documentation and/or other materials provided with the distribution. 14243465Sgonzo * 15243465Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16243465Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17243465Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18243465Sgonzo * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19243465Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20243465Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21243465Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22243465Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23243465Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24243465Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25243465Sgonzo * SUCH DAMAGE. 26243465Sgonzo * 27243465Sgonzo */ 28243465Sgonzo#include <sys/cdefs.h> 29243465Sgonzo__FBSDID("$FreeBSD$"); 30243465Sgonzo 31243465Sgonzo#include <sys/param.h> 32243465Sgonzo#include <sys/systm.h> 33243465Sgonzo#include <sys/bus.h> 34243465Sgonzo 35243465Sgonzo#include <sys/kernel.h> 36243465Sgonzo#include <sys/module.h> 37243465Sgonzo#include <sys/rman.h> 38243465Sgonzo#include <sys/lock.h> 39243465Sgonzo#include <sys/mutex.h> 40243465Sgonzo#include <sys/gpio.h> 41244412Sgonzo#include <sys/sysctl.h> 42243465Sgonzo 43243465Sgonzo#include <machine/bus.h> 44243465Sgonzo#include <machine/cpu.h> 45243465Sgonzo#include <machine/cpufunc.h> 46243465Sgonzo#include <machine/resource.h> 47243465Sgonzo#include <machine/fdt.h> 48243465Sgonzo#include <machine/frame.h> 49243465Sgonzo#include <machine/intr.h> 50243465Sgonzo 51243465Sgonzo#include <dev/fdt/fdt_common.h> 52243465Sgonzo#include <dev/ofw/ofw_bus.h> 53243465Sgonzo#include <dev/ofw/ofw_bus_subr.h> 54243465Sgonzo 55255370Sloos#include <arm/broadcom/bcm2835/bcm2835_gpio.h> 56255370Sloos 57243465Sgonzo#include "gpio_if.h" 58243465Sgonzo 59243465Sgonzo#undef DEBUG 60243465Sgonzo 61243465Sgonzo#ifdef DEBUG 62243465Sgonzo#define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ 63243465Sgonzo printf(fmt,##args); } while (0) 64243465Sgonzo#else 65243465Sgonzo#define dprintf(fmt, args...) 66243465Sgonzo#endif 67243465Sgonzo 68243465Sgonzo#define BCM_GPIO_PINS 54 69243465Sgonzo#define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ 70243465Sgonzo GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) 71243465Sgonzo 72244412Sgonzostruct bcm_gpio_sysctl { 73244412Sgonzo struct bcm_gpio_softc *sc; 74244412Sgonzo uint32_t pin; 75244412Sgonzo}; 76244412Sgonzo 77243465Sgonzostruct bcm_gpio_softc { 78243465Sgonzo device_t sc_dev; 79243465Sgonzo struct mtx sc_mtx; 80243465Sgonzo struct resource * sc_mem_res; 81243465Sgonzo struct resource * sc_irq_res; 82243465Sgonzo bus_space_tag_t sc_bst; 83243465Sgonzo bus_space_handle_t sc_bsh; 84243465Sgonzo void * sc_intrhand; 85243465Sgonzo int sc_gpio_npins; 86243465Sgonzo int sc_ro_npins; 87243465Sgonzo int sc_ro_pins[BCM_GPIO_PINS]; 88243465Sgonzo struct gpio_pin sc_gpio_pins[BCM_GPIO_PINS]; 89244412Sgonzo struct bcm_gpio_sysctl sc_sysctl[BCM_GPIO_PINS]; 90243465Sgonzo}; 91243465Sgonzo 92243465Sgonzoenum bcm_gpio_pud { 93243465Sgonzo BCM_GPIO_NONE, 94243465Sgonzo BCM_GPIO_PULLDOWN, 95243465Sgonzo BCM_GPIO_PULLUP, 96243465Sgonzo}; 97243465Sgonzo 98243465Sgonzo#define BCM_GPIO_LOCK(_sc) mtx_lock(&_sc->sc_mtx) 99243465Sgonzo#define BCM_GPIO_UNLOCK(_sc) mtx_unlock(&_sc->sc_mtx) 100244412Sgonzo#define BCM_GPIO_LOCK_ASSERT(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED) 101243465Sgonzo 102243465Sgonzo#define BCM_GPIO_GPFSEL(_bank) 0x00 + _bank * 4 103243465Sgonzo#define BCM_GPIO_GPSET(_bank) 0x1c + _bank * 4 104243465Sgonzo#define BCM_GPIO_GPCLR(_bank) 0x28 + _bank * 4 105243465Sgonzo#define BCM_GPIO_GPLEV(_bank) 0x34 + _bank * 4 106243465Sgonzo#define BCM_GPIO_GPPUD(_bank) 0x94 107243465Sgonzo#define BCM_GPIO_GPPUDCLK(_bank) 0x98 + _bank * 4 108243465Sgonzo 109243465Sgonzo#define BCM_GPIO_WRITE(_sc, _off, _val) \ 110243465Sgonzo bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) 111243465Sgonzo#define BCM_GPIO_READ(_sc, _off) \ 112243465Sgonzo bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off) 113243465Sgonzo 114243465Sgonzostatic int 115243465Sgonzobcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin) 116243465Sgonzo{ 117243465Sgonzo int i; 118243465Sgonzo 119243465Sgonzo for (i = 0; i < sc->sc_ro_npins; i++) 120243465Sgonzo if (pin == sc->sc_ro_pins[i]) 121243465Sgonzo return (1); 122243465Sgonzo return (0); 123243465Sgonzo} 124243465Sgonzo 125243465Sgonzostatic uint32_t 126243465Sgonzobcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin) 127243465Sgonzo{ 128244412Sgonzo uint32_t bank, func, offset; 129243465Sgonzo 130243465Sgonzo /* Five banks, 10 pins per bank, 3 bits per pin. */ 131243465Sgonzo bank = pin / 10; 132243465Sgonzo offset = (pin - bank * 10) * 3; 133243465Sgonzo 134243465Sgonzo BCM_GPIO_LOCK(sc); 135244412Sgonzo func = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7; 136243465Sgonzo BCM_GPIO_UNLOCK(sc); 137243465Sgonzo 138244412Sgonzo return (func); 139244412Sgonzo} 140244412Sgonzo 141244412Sgonzostatic void 142244412Sgonzobcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize) 143244412Sgonzo{ 144244412Sgonzo 145244412Sgonzo switch (nfunc) { 146243465Sgonzo case BCM_GPIO_INPUT: 147244412Sgonzo strncpy(buf, "input", bufsize); 148243465Sgonzo break; 149243465Sgonzo case BCM_GPIO_OUTPUT: 150244412Sgonzo strncpy(buf, "output", bufsize); 151243465Sgonzo break; 152243465Sgonzo case BCM_GPIO_ALT0: 153244412Sgonzo strncpy(buf, "alt0", bufsize); 154243465Sgonzo break; 155243465Sgonzo case BCM_GPIO_ALT1: 156244412Sgonzo strncpy(buf, "alt1", bufsize); 157243465Sgonzo break; 158243465Sgonzo case BCM_GPIO_ALT2: 159244412Sgonzo strncpy(buf, "alt2", bufsize); 160243465Sgonzo break; 161243465Sgonzo case BCM_GPIO_ALT3: 162244412Sgonzo strncpy(buf, "alt3", bufsize); 163243465Sgonzo break; 164243465Sgonzo case BCM_GPIO_ALT4: 165244412Sgonzo strncpy(buf, "alt4", bufsize); 166243465Sgonzo break; 167243465Sgonzo case BCM_GPIO_ALT5: 168244412Sgonzo strncpy(buf, "alt5", bufsize); 169243465Sgonzo break; 170244412Sgonzo default: 171244412Sgonzo strncpy(buf, "invalid", bufsize); 172243465Sgonzo } 173244412Sgonzo} 174243465Sgonzo 175244412Sgonzostatic int 176244412Sgonzobcm_gpio_str_func(char *func, uint32_t *nfunc) 177244412Sgonzo{ 178244412Sgonzo 179244412Sgonzo if (strcasecmp(func, "input") == 0) 180244412Sgonzo *nfunc = BCM_GPIO_INPUT; 181244412Sgonzo else if (strcasecmp(func, "output") == 0) 182244412Sgonzo *nfunc = BCM_GPIO_OUTPUT; 183244412Sgonzo else if (strcasecmp(func, "alt0") == 0) 184244412Sgonzo *nfunc = BCM_GPIO_ALT0; 185244412Sgonzo else if (strcasecmp(func, "alt1") == 0) 186244412Sgonzo *nfunc = BCM_GPIO_ALT1; 187244412Sgonzo else if (strcasecmp(func, "alt2") == 0) 188244412Sgonzo *nfunc = BCM_GPIO_ALT2; 189244412Sgonzo else if (strcasecmp(func, "alt3") == 0) 190244412Sgonzo *nfunc = BCM_GPIO_ALT3; 191244412Sgonzo else if (strcasecmp(func, "alt4") == 0) 192244412Sgonzo *nfunc = BCM_GPIO_ALT4; 193244412Sgonzo else if (strcasecmp(func, "alt5") == 0) 194244412Sgonzo *nfunc = BCM_GPIO_ALT5; 195244412Sgonzo else 196244412Sgonzo return (-1); 197244412Sgonzo 198244412Sgonzo return (0); 199244412Sgonzo} 200244412Sgonzo 201244412Sgonzostatic uint32_t 202244412Sgonzobcm_gpio_func_flag(uint32_t nfunc) 203244412Sgonzo{ 204244412Sgonzo 205244412Sgonzo switch (nfunc) { 206243465Sgonzo case BCM_GPIO_INPUT: 207243465Sgonzo return (GPIO_PIN_INPUT); 208243465Sgonzo case BCM_GPIO_OUTPUT: 209243465Sgonzo return (GPIO_PIN_OUTPUT); 210243465Sgonzo } 211243465Sgonzo return (0); 212243465Sgonzo} 213243465Sgonzo 214243465Sgonzostatic void 215243465Sgonzobcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f) 216243465Sgonzo{ 217243465Sgonzo uint32_t bank, data, offset; 218243465Sgonzo 219244412Sgonzo /* Must be called with lock held. */ 220244412Sgonzo BCM_GPIO_LOCK_ASSERT(sc); 221244412Sgonzo 222243465Sgonzo /* Five banks, 10 pins per bank, 3 bits per pin. */ 223243465Sgonzo bank = pin / 10; 224243465Sgonzo offset = (pin - bank * 10) * 3; 225243465Sgonzo 226243465Sgonzo data = BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)); 227243465Sgonzo data &= ~(7 << offset); 228243465Sgonzo data |= (f << offset); 229243465Sgonzo BCM_GPIO_WRITE(sc, BCM_GPIO_GPFSEL(bank), data); 230243465Sgonzo} 231243465Sgonzo 232243465Sgonzostatic void 233243465Sgonzobcm_gpio_set_pud(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t state) 234243465Sgonzo{ 235243465Sgonzo uint32_t bank, offset; 236243465Sgonzo 237244412Sgonzo /* Must be called with lock held. */ 238244412Sgonzo BCM_GPIO_LOCK_ASSERT(sc); 239244412Sgonzo 240243465Sgonzo bank = pin / 32; 241243465Sgonzo offset = pin - 32 * bank; 242243465Sgonzo 243243465Sgonzo BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state); 244243465Sgonzo DELAY(10); 245243465Sgonzo BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), (1 << offset)); 246243465Sgonzo DELAY(10); 247243465Sgonzo BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0); 248243465Sgonzo BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0); 249243465Sgonzo} 250243465Sgonzo 251255370Sloosvoid 252255370Sloosbcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc) 253255370Sloos{ 254255370Sloos struct bcm_gpio_softc *sc; 255255370Sloos int i; 256255370Sloos 257255370Sloos sc = device_get_softc(dev); 258255370Sloos BCM_GPIO_LOCK(sc); 259255370Sloos 260255370Sloos /* Disable pull-up or pull-down on pin. */ 261255370Sloos bcm_gpio_set_pud(sc, pin, BCM_GPIO_NONE); 262255370Sloos 263255370Sloos /* And now set the pin function. */ 264255370Sloos bcm_gpio_set_function(sc, pin, nfunc); 265255370Sloos 266255370Sloos /* Update the pin flags. */ 267255370Sloos for (i = 0; i < sc->sc_gpio_npins; i++) { 268255370Sloos if (sc->sc_gpio_pins[i].gp_pin == pin) 269255370Sloos break; 270255370Sloos } 271255370Sloos if (i < sc->sc_gpio_npins) 272255370Sloos sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc); 273255370Sloos 274255370Sloos BCM_GPIO_UNLOCK(sc); 275255370Sloos} 276255370Sloos 277243465Sgonzostatic void 278243465Sgonzobcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin, 279243465Sgonzo unsigned int flags) 280243465Sgonzo{ 281243465Sgonzo 282244412Sgonzo BCM_GPIO_LOCK(sc); 283244412Sgonzo 284243465Sgonzo /* 285243465Sgonzo * Manage input/output. 286243465Sgonzo */ 287243465Sgonzo if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { 288243465Sgonzo pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); 289243465Sgonzo if (flags & GPIO_PIN_OUTPUT) { 290243465Sgonzo pin->gp_flags |= GPIO_PIN_OUTPUT; 291243465Sgonzo bcm_gpio_set_function(sc, pin->gp_pin, 292243465Sgonzo BCM_GPIO_OUTPUT); 293243465Sgonzo } else { 294243465Sgonzo pin->gp_flags |= GPIO_PIN_INPUT; 295243465Sgonzo bcm_gpio_set_function(sc, pin->gp_pin, 296243465Sgonzo BCM_GPIO_INPUT); 297243465Sgonzo } 298243465Sgonzo } 299243465Sgonzo 300243465Sgonzo /* Manage Pull-up/pull-down. */ 301243465Sgonzo pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN); 302243465Sgonzo if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) { 303243465Sgonzo if (flags & GPIO_PIN_PULLUP) { 304243465Sgonzo pin->gp_flags |= GPIO_PIN_PULLUP; 305243465Sgonzo bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLUP); 306243465Sgonzo } else { 307243465Sgonzo pin->gp_flags |= GPIO_PIN_PULLDOWN; 308243465Sgonzo bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLDOWN); 309243465Sgonzo } 310243465Sgonzo } else 311243465Sgonzo bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE); 312244412Sgonzo 313244412Sgonzo BCM_GPIO_UNLOCK(sc); 314243465Sgonzo} 315243465Sgonzo 316243465Sgonzostatic int 317243465Sgonzobcm_gpio_pin_max(device_t dev, int *maxpin) 318243465Sgonzo{ 319243465Sgonzo 320243465Sgonzo *maxpin = BCM_GPIO_PINS - 1; 321243465Sgonzo return (0); 322243465Sgonzo} 323243465Sgonzo 324243465Sgonzostatic int 325243465Sgonzobcm_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 326243465Sgonzo{ 327243465Sgonzo struct bcm_gpio_softc *sc = device_get_softc(dev); 328243465Sgonzo int i; 329243465Sgonzo 330243465Sgonzo for (i = 0; i < sc->sc_gpio_npins; i++) { 331243465Sgonzo if (sc->sc_gpio_pins[i].gp_pin == pin) 332243465Sgonzo break; 333243465Sgonzo } 334243465Sgonzo 335243465Sgonzo if (i >= sc->sc_gpio_npins) 336243465Sgonzo return (EINVAL); 337243465Sgonzo 338243465Sgonzo BCM_GPIO_LOCK(sc); 339243465Sgonzo *caps = sc->sc_gpio_pins[i].gp_caps; 340243465Sgonzo BCM_GPIO_UNLOCK(sc); 341243465Sgonzo 342243465Sgonzo return (0); 343243465Sgonzo} 344243465Sgonzo 345243465Sgonzostatic int 346243465Sgonzobcm_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 347243465Sgonzo{ 348243465Sgonzo struct bcm_gpio_softc *sc = device_get_softc(dev); 349243465Sgonzo int i; 350243465Sgonzo 351243465Sgonzo for (i = 0; i < sc->sc_gpio_npins; i++) { 352243465Sgonzo if (sc->sc_gpio_pins[i].gp_pin == pin) 353243465Sgonzo break; 354243465Sgonzo } 355243465Sgonzo 356243465Sgonzo if (i >= sc->sc_gpio_npins) 357243465Sgonzo return (EINVAL); 358243465Sgonzo 359243465Sgonzo BCM_GPIO_LOCK(sc); 360243465Sgonzo *flags = sc->sc_gpio_pins[i].gp_flags; 361243465Sgonzo BCM_GPIO_UNLOCK(sc); 362243465Sgonzo 363243465Sgonzo return (0); 364243465Sgonzo} 365243465Sgonzo 366243465Sgonzostatic int 367243465Sgonzobcm_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 368243465Sgonzo{ 369243465Sgonzo struct bcm_gpio_softc *sc = device_get_softc(dev); 370243465Sgonzo int i; 371243465Sgonzo 372243465Sgonzo for (i = 0; i < sc->sc_gpio_npins; i++) { 373243465Sgonzo if (sc->sc_gpio_pins[i].gp_pin == pin) 374243465Sgonzo break; 375243465Sgonzo } 376243465Sgonzo 377243465Sgonzo if (i >= sc->sc_gpio_npins) 378243465Sgonzo return (EINVAL); 379243465Sgonzo 380243465Sgonzo BCM_GPIO_LOCK(sc); 381243465Sgonzo memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); 382243465Sgonzo BCM_GPIO_UNLOCK(sc); 383243465Sgonzo 384243465Sgonzo return (0); 385243465Sgonzo} 386243465Sgonzo 387243465Sgonzostatic int 388243465Sgonzobcm_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 389243465Sgonzo{ 390243465Sgonzo struct bcm_gpio_softc *sc = device_get_softc(dev); 391243465Sgonzo int i; 392243465Sgonzo 393243465Sgonzo for (i = 0; i < sc->sc_gpio_npins; i++) { 394243465Sgonzo if (sc->sc_gpio_pins[i].gp_pin == pin) 395243465Sgonzo break; 396243465Sgonzo } 397243465Sgonzo 398243465Sgonzo if (i >= sc->sc_gpio_npins) 399243465Sgonzo return (EINVAL); 400243465Sgonzo 401243465Sgonzo /* We never touch on read-only/reserved pins. */ 402243465Sgonzo if (bcm_gpio_pin_is_ro(sc, pin)) 403243465Sgonzo return (EINVAL); 404243465Sgonzo 405249449Sdim /* Check for unwanted flags. */ 406249449Sdim if ((flags & sc->sc_gpio_pins[i].gp_caps) != flags) 407243465Sgonzo return (EINVAL); 408243465Sgonzo 409243465Sgonzo /* Can't mix input/output together. */ 410243465Sgonzo if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == 411243465Sgonzo (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) 412243465Sgonzo return (EINVAL); 413243465Sgonzo 414243465Sgonzo /* Can't mix pull-up/pull-down together. */ 415243465Sgonzo if ((flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) == 416243465Sgonzo (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) 417243465Sgonzo return (EINVAL); 418243465Sgonzo 419243465Sgonzo bcm_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); 420243465Sgonzo 421243465Sgonzo return (0); 422243465Sgonzo} 423243465Sgonzo 424243465Sgonzostatic int 425243465Sgonzobcm_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) 426243465Sgonzo{ 427243465Sgonzo struct bcm_gpio_softc *sc = device_get_softc(dev); 428243465Sgonzo uint32_t bank, offset; 429243465Sgonzo int i; 430243465Sgonzo 431243465Sgonzo for (i = 0; i < sc->sc_gpio_npins; i++) { 432243465Sgonzo if (sc->sc_gpio_pins[i].gp_pin == pin) 433243465Sgonzo break; 434243465Sgonzo } 435243465Sgonzo 436243465Sgonzo if (i >= sc->sc_gpio_npins) 437243465Sgonzo return (EINVAL); 438243465Sgonzo 439243465Sgonzo /* We never write to read-only/reserved pins. */ 440243465Sgonzo if (bcm_gpio_pin_is_ro(sc, pin)) 441243465Sgonzo return (EINVAL); 442243465Sgonzo 443243465Sgonzo bank = pin / 32; 444243465Sgonzo offset = pin - 32 * bank; 445243465Sgonzo 446243465Sgonzo BCM_GPIO_LOCK(sc); 447243465Sgonzo if (value) 448243465Sgonzo BCM_GPIO_WRITE(sc, BCM_GPIO_GPSET(bank), (1 << offset)); 449243465Sgonzo else 450243465Sgonzo BCM_GPIO_WRITE(sc, BCM_GPIO_GPCLR(bank), (1 << offset)); 451243465Sgonzo BCM_GPIO_UNLOCK(sc); 452243465Sgonzo 453243465Sgonzo return (0); 454243465Sgonzo} 455243465Sgonzo 456243465Sgonzostatic int 457243465Sgonzobcm_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) 458243465Sgonzo{ 459243465Sgonzo struct bcm_gpio_softc *sc = device_get_softc(dev); 460243465Sgonzo uint32_t bank, offset, reg_data; 461243465Sgonzo int i; 462243465Sgonzo 463243465Sgonzo for (i = 0; i < sc->sc_gpio_npins; i++) { 464243465Sgonzo if (sc->sc_gpio_pins[i].gp_pin == pin) 465243465Sgonzo break; 466243465Sgonzo } 467243465Sgonzo 468243465Sgonzo if (i >= sc->sc_gpio_npins) 469243465Sgonzo return (EINVAL); 470243465Sgonzo 471243465Sgonzo bank = pin / 32; 472243465Sgonzo offset = pin - 32 * bank; 473243465Sgonzo 474243465Sgonzo BCM_GPIO_LOCK(sc); 475243465Sgonzo reg_data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); 476243465Sgonzo BCM_GPIO_UNLOCK(sc); 477243465Sgonzo *val = (reg_data & (1 << offset)) ? 1 : 0; 478243465Sgonzo 479243465Sgonzo return (0); 480243465Sgonzo} 481243465Sgonzo 482243465Sgonzostatic int 483243465Sgonzobcm_gpio_pin_toggle(device_t dev, uint32_t pin) 484243465Sgonzo{ 485243465Sgonzo struct bcm_gpio_softc *sc = device_get_softc(dev); 486243465Sgonzo uint32_t bank, data, offset; 487243465Sgonzo int i; 488243465Sgonzo 489243465Sgonzo for (i = 0; i < sc->sc_gpio_npins; i++) { 490243465Sgonzo if (sc->sc_gpio_pins[i].gp_pin == pin) 491243465Sgonzo break; 492243465Sgonzo } 493243465Sgonzo 494243465Sgonzo if (i >= sc->sc_gpio_npins) 495243465Sgonzo return (EINVAL); 496243465Sgonzo 497243465Sgonzo /* We never write to read-only/reserved pins. */ 498243465Sgonzo if (bcm_gpio_pin_is_ro(sc, pin)) 499243465Sgonzo return (EINVAL); 500243465Sgonzo 501243465Sgonzo bank = pin / 32; 502243465Sgonzo offset = pin - 32 * bank; 503243465Sgonzo 504243465Sgonzo BCM_GPIO_LOCK(sc); 505243465Sgonzo data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); 506243465Sgonzo if (data & (1 << offset)) 507243465Sgonzo BCM_GPIO_WRITE(sc, BCM_GPIO_GPCLR(bank), (1 << offset)); 508243465Sgonzo else 509243465Sgonzo BCM_GPIO_WRITE(sc, BCM_GPIO_GPSET(bank), (1 << offset)); 510243465Sgonzo BCM_GPIO_UNLOCK(sc); 511243465Sgonzo 512243465Sgonzo return (0); 513243465Sgonzo} 514243465Sgonzo 515243465Sgonzostatic int 516243465Sgonzobcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc) 517243465Sgonzo{ 518243465Sgonzo int i, len; 519243465Sgonzo pcell_t pins[BCM_GPIO_PINS]; 520243465Sgonzo phandle_t gpio; 521243465Sgonzo 522243465Sgonzo /* Find the gpio node to start. */ 523243465Sgonzo gpio = ofw_bus_get_node(sc->sc_dev); 524243465Sgonzo 525243465Sgonzo len = OF_getproplen(gpio, "broadcom,read-only"); 526243465Sgonzo if (len < 0 || len > sizeof(pins)) 527243465Sgonzo return (-1); 528243465Sgonzo 529243465Sgonzo if (OF_getprop(gpio, "broadcom,read-only", &pins, len) < 0) 530243465Sgonzo return (-1); 531243465Sgonzo 532243465Sgonzo sc->sc_ro_npins = len / sizeof(pcell_t); 533243465Sgonzo 534243465Sgonzo device_printf(sc->sc_dev, "read-only pins: "); 535243465Sgonzo for (i = 0; i < sc->sc_ro_npins; i++) { 536243465Sgonzo sc->sc_ro_pins[i] = fdt32_to_cpu(pins[i]); 537243465Sgonzo if (i > 0) 538243465Sgonzo printf(","); 539243465Sgonzo printf("%d", sc->sc_ro_pins[i]); 540243465Sgonzo } 541244412Sgonzo if (i > 0) 542243465Sgonzo printf("."); 543243465Sgonzo printf("\n"); 544243465Sgonzo 545243465Sgonzo return (0); 546243465Sgonzo} 547243465Sgonzo 548243465Sgonzostatic int 549244412Sgonzobcm_gpio_func_proc(SYSCTL_HANDLER_ARGS) 550244412Sgonzo{ 551244412Sgonzo char buf[16]; 552244412Sgonzo struct bcm_gpio_softc *sc; 553244412Sgonzo struct bcm_gpio_sysctl *sc_sysctl; 554244412Sgonzo uint32_t nfunc; 555255370Sloos int error; 556244412Sgonzo 557244412Sgonzo sc_sysctl = arg1; 558244412Sgonzo sc = sc_sysctl->sc; 559244412Sgonzo 560244412Sgonzo /* Get the current pin function. */ 561244412Sgonzo nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin); 562244412Sgonzo bcm_gpio_func_str(nfunc, buf, sizeof(buf)); 563244412Sgonzo 564244412Sgonzo error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 565244412Sgonzo if (error != 0 || req->newptr == NULL) 566244412Sgonzo return (error); 567244412Sgonzo 568244412Sgonzo /* Parse the user supplied string and check for a valid pin function. */ 569244412Sgonzo if (bcm_gpio_str_func(buf, &nfunc) != 0) 570244412Sgonzo return (EINVAL); 571244412Sgonzo 572255370Sloos /* Update the pin alternate function. */ 573255370Sloos bcm_gpio_set_alternate(sc->sc_dev, sc_sysctl->pin, nfunc); 574244412Sgonzo 575244412Sgonzo return (0); 576244412Sgonzo} 577244412Sgonzo 578244412Sgonzostatic void 579244412Sgonzobcm_gpio_sysctl_init(struct bcm_gpio_softc *sc) 580244412Sgonzo{ 581244412Sgonzo char pinbuf[3]; 582244412Sgonzo struct bcm_gpio_sysctl *sc_sysctl; 583244412Sgonzo struct sysctl_ctx_list *ctx; 584244412Sgonzo struct sysctl_oid *tree_node, *pin_node, *pinN_node; 585244412Sgonzo struct sysctl_oid_list *tree, *pin_tree, *pinN_tree; 586244412Sgonzo int i; 587244412Sgonzo 588244412Sgonzo /* 589244412Sgonzo * Add per-pin sysctl tree/handlers. 590244412Sgonzo */ 591244412Sgonzo ctx = device_get_sysctl_ctx(sc->sc_dev); 592244412Sgonzo tree_node = device_get_sysctl_tree(sc->sc_dev); 593244412Sgonzo tree = SYSCTL_CHILDREN(tree_node); 594244412Sgonzo pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin", 595244412Sgonzo CTLFLAG_RW, NULL, "GPIO Pins"); 596244412Sgonzo pin_tree = SYSCTL_CHILDREN(pin_node); 597244412Sgonzo 598244412Sgonzo for (i = 0; i < sc->sc_gpio_npins; i++) { 599244412Sgonzo 600244412Sgonzo snprintf(pinbuf, sizeof(pinbuf), "%d", i); 601244412Sgonzo pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf, 602244412Sgonzo CTLFLAG_RD, NULL, "GPIO Pin"); 603244412Sgonzo pinN_tree = SYSCTL_CHILDREN(pinN_node); 604244412Sgonzo 605244412Sgonzo sc->sc_sysctl[i].sc = sc; 606244412Sgonzo sc_sysctl = &sc->sc_sysctl[i]; 607244412Sgonzo sc_sysctl->sc = sc; 608244412Sgonzo sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin; 609244412Sgonzo SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function", 610244412Sgonzo CTLFLAG_RW | CTLTYPE_STRING, sc_sysctl, 611244412Sgonzo sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc, 612244412Sgonzo "A", "Pin Function"); 613244412Sgonzo } 614244412Sgonzo} 615244412Sgonzo 616244412Sgonzostatic int 617243465Sgonzobcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc) 618243465Sgonzo{ 619243465Sgonzo int i, j, len, npins; 620243465Sgonzo pcell_t pins[BCM_GPIO_PINS]; 621243465Sgonzo phandle_t gpio, node, reserved; 622243465Sgonzo char name[32]; 623243465Sgonzo 624243465Sgonzo /* Get read-only pins. */ 625243465Sgonzo if (bcm_gpio_get_ro_pins(sc) != 0) 626243465Sgonzo return (-1); 627243465Sgonzo 628243465Sgonzo /* Find the gpio/reserved pins node to start. */ 629243465Sgonzo gpio = ofw_bus_get_node(sc->sc_dev); 630243465Sgonzo node = OF_child(gpio); 631243465Sgonzo 632243465Sgonzo /* 633243465Sgonzo * Find reserved node 634243465Sgonzo */ 635243465Sgonzo reserved = 0; 636243465Sgonzo while ((node != 0) && (reserved == 0)) { 637244412Sgonzo len = OF_getprop(node, "name", name, 638243465Sgonzo sizeof(name) - 1); 639243465Sgonzo name[len] = 0; 640243465Sgonzo if (strcmp(name, "reserved") == 0) 641243465Sgonzo reserved = node; 642243465Sgonzo node = OF_peer(node); 643243465Sgonzo } 644243465Sgonzo 645243465Sgonzo if (reserved == 0) 646243465Sgonzo return (-1); 647243465Sgonzo 648243465Sgonzo /* Get the reserved pins. */ 649243465Sgonzo len = OF_getproplen(reserved, "broadcom,pins"); 650243465Sgonzo if (len < 0 || len > sizeof(pins)) 651243465Sgonzo return (-1); 652243465Sgonzo 653243465Sgonzo if (OF_getprop(reserved, "broadcom,pins", &pins, len) < 0) 654243465Sgonzo return (-1); 655243465Sgonzo 656243465Sgonzo npins = len / sizeof(pcell_t); 657243465Sgonzo 658243465Sgonzo j = 0; 659243465Sgonzo device_printf(sc->sc_dev, "reserved pins: "); 660243465Sgonzo for (i = 0; i < npins; i++) { 661243465Sgonzo if (i > 0) 662243465Sgonzo printf(","); 663243465Sgonzo printf("%d", fdt32_to_cpu(pins[i])); 664243465Sgonzo /* Some pins maybe already on the list of read-only pins. */ 665243465Sgonzo if (bcm_gpio_pin_is_ro(sc, fdt32_to_cpu(pins[i]))) 666243465Sgonzo continue; 667243465Sgonzo sc->sc_ro_pins[j++ + sc->sc_ro_npins] = fdt32_to_cpu(pins[i]); 668243465Sgonzo } 669243465Sgonzo sc->sc_ro_npins += j; 670244412Sgonzo if (i > 0) 671243465Sgonzo printf("."); 672243465Sgonzo printf("\n"); 673243465Sgonzo 674243465Sgonzo return (0); 675243465Sgonzo} 676243465Sgonzo 677243465Sgonzostatic int 678243465Sgonzobcm_gpio_probe(device_t dev) 679243465Sgonzo{ 680243465Sgonzo if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-gpio")) 681243465Sgonzo return (ENXIO); 682243465Sgonzo 683243465Sgonzo device_set_desc(dev, "BCM2708/2835 GPIO controller"); 684243465Sgonzo return (BUS_PROBE_DEFAULT); 685243465Sgonzo} 686243465Sgonzo 687243465Sgonzostatic int 688243465Sgonzobcm_gpio_attach(device_t dev) 689243465Sgonzo{ 690243465Sgonzo struct bcm_gpio_softc *sc = device_get_softc(dev); 691244412Sgonzo uint32_t func; 692243465Sgonzo int i, j, rid; 693243465Sgonzo phandle_t gpio; 694243465Sgonzo 695243465Sgonzo sc->sc_dev = dev; 696243465Sgonzo 697243465Sgonzo mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_DEF); 698243465Sgonzo 699243465Sgonzo rid = 0; 700243465Sgonzo sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 701243465Sgonzo RF_ACTIVE); 702243465Sgonzo if (!sc->sc_mem_res) { 703243465Sgonzo device_printf(dev, "cannot allocate memory window\n"); 704243465Sgonzo return (ENXIO); 705243465Sgonzo } 706243465Sgonzo 707243465Sgonzo sc->sc_bst = rman_get_bustag(sc->sc_mem_res); 708243465Sgonzo sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); 709243465Sgonzo 710243465Sgonzo rid = 0; 711243465Sgonzo sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 712243465Sgonzo RF_ACTIVE); 713243465Sgonzo if (!sc->sc_irq_res) { 714243465Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 715243465Sgonzo device_printf(dev, "cannot allocate interrupt\n"); 716243465Sgonzo return (ENXIO); 717243465Sgonzo } 718243465Sgonzo 719243465Sgonzo /* Find our node. */ 720243465Sgonzo gpio = ofw_bus_get_node(sc->sc_dev); 721243465Sgonzo 722243465Sgonzo if (!OF_hasprop(gpio, "gpio-controller")) 723243465Sgonzo /* Node is not a GPIO controller. */ 724243465Sgonzo goto fail; 725243465Sgonzo 726243465Sgonzo /* 727243465Sgonzo * Find the read-only pins. These are pins we never touch or bad 728243465Sgonzo * things could happen. 729243465Sgonzo */ 730243465Sgonzo if (bcm_gpio_get_reserved_pins(sc) == -1) 731243465Sgonzo goto fail; 732243465Sgonzo 733243465Sgonzo /* Initialize the software controlled pins. */ 734255334Sloos for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) { 735243465Sgonzo if (bcm_gpio_pin_is_ro(sc, j)) 736243465Sgonzo continue; 737243465Sgonzo snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, 738243465Sgonzo "pin %d", j); 739244412Sgonzo func = bcm_gpio_get_function(sc, j); 740243465Sgonzo sc->sc_gpio_pins[i].gp_pin = j; 741243465Sgonzo sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS; 742244412Sgonzo sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func); 743243465Sgonzo i++; 744243465Sgonzo } 745243465Sgonzo sc->sc_gpio_npins = i; 746243465Sgonzo 747244412Sgonzo bcm_gpio_sysctl_init(sc); 748244412Sgonzo 749244412Sgonzo device_add_child(dev, "gpioc", device_get_unit(dev)); 750244412Sgonzo device_add_child(dev, "gpiobus", device_get_unit(dev)); 751243465Sgonzo return (bus_generic_attach(dev)); 752243465Sgonzo 753243465Sgonzofail: 754243465Sgonzo if (sc->sc_irq_res) 755243465Sgonzo bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 756243465Sgonzo if (sc->sc_mem_res) 757243465Sgonzo bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 758243465Sgonzo return (ENXIO); 759243465Sgonzo} 760243465Sgonzo 761243465Sgonzostatic int 762243465Sgonzobcm_gpio_detach(device_t dev) 763243465Sgonzo{ 764243465Sgonzo 765243465Sgonzo return (EBUSY); 766243465Sgonzo} 767243465Sgonzo 768243465Sgonzostatic device_method_t bcm_gpio_methods[] = { 769243465Sgonzo /* Device interface */ 770243465Sgonzo DEVMETHOD(device_probe, bcm_gpio_probe), 771243465Sgonzo DEVMETHOD(device_attach, bcm_gpio_attach), 772243465Sgonzo DEVMETHOD(device_detach, bcm_gpio_detach), 773243465Sgonzo 774243465Sgonzo /* GPIO protocol */ 775243465Sgonzo DEVMETHOD(gpio_pin_max, bcm_gpio_pin_max), 776243465Sgonzo DEVMETHOD(gpio_pin_getname, bcm_gpio_pin_getname), 777243465Sgonzo DEVMETHOD(gpio_pin_getflags, bcm_gpio_pin_getflags), 778243465Sgonzo DEVMETHOD(gpio_pin_getcaps, bcm_gpio_pin_getcaps), 779243465Sgonzo DEVMETHOD(gpio_pin_setflags, bcm_gpio_pin_setflags), 780243465Sgonzo DEVMETHOD(gpio_pin_get, bcm_gpio_pin_get), 781243465Sgonzo DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set), 782243465Sgonzo DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle), 783243465Sgonzo 784243465Sgonzo DEVMETHOD_END 785243465Sgonzo}; 786243465Sgonzo 787243465Sgonzostatic devclass_t bcm_gpio_devclass; 788243465Sgonzo 789243465Sgonzostatic driver_t bcm_gpio_driver = { 790243465Sgonzo "gpio", 791243465Sgonzo bcm_gpio_methods, 792243465Sgonzo sizeof(struct bcm_gpio_softc), 793243465Sgonzo}; 794243465Sgonzo 795243465SgonzoDRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0); 796