1286908Sloos/*- 2286908Sloos * Copyright (c) 2015 Rubicon Communications, LLC (Netgate) 3286908Sloos * All rights reserved. 4286908Sloos * 5286908Sloos * Redistribution and use in source and binary forms, with or without 6286908Sloos * modification, are permitted provided that the following conditions 7286908Sloos * are met: 8286908Sloos * 1. Redistributions of source code must retain the above copyright 9286908Sloos * notice, this list of conditions and the following disclaimer. 10286908Sloos * 2. Redistributions in binary form must reproduce the above copyright 11286908Sloos * notice, this list of conditions and the following disclaimer in the 12286908Sloos * documentation and/or other materials provided with the distribution. 13286908Sloos * 14286908Sloos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15286908Sloos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16286908Sloos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17286908Sloos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18286908Sloos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19286908Sloos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20286908Sloos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21286908Sloos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22286908Sloos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23286908Sloos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24286908Sloos * SUCH DAMAGE. 25286908Sloos */ 26286908Sloos#include <sys/cdefs.h> 27286908Sloos__FBSDID("$FreeBSD: stable/11/sys/dev/rccgpio/rccgpio.c 311205 2017-01-04 00:25:22Z loos $"); 28286908Sloos 29286908Sloos/* 30286908Sloos * GPIO driver for the ADI Engineering RCC-VE and RCC-DFF/DFFv2. 31286908Sloos */ 32286908Sloos 33286908Sloos#include <sys/param.h> 34286908Sloos#include <sys/systm.h> 35286908Sloos#include <sys/bus.h> 36286908Sloos#include <sys/gpio.h> 37286908Sloos#include <sys/kernel.h> 38286908Sloos#include <sys/module.h> 39286908Sloos#include <sys/rman.h> 40286908Sloos 41286908Sloos#include <machine/bus.h> 42286908Sloos 43286908Sloos#include <dev/gpio/gpiobusvar.h> 44286908Sloos#include <isa/isavar.h> 45286908Sloos 46286908Sloos#include "gpio_if.h" 47286908Sloos 48286908Sloos#define RCC_GPIO_BASE 0x500 49286908Sloos#define RCC_GPIO_USE_SEL 0x00 50286908Sloos#define RCC_GPIO_IO_SEL 0x04 51286908Sloos#define RCC_GPIO_GP_LVL 0x08 52286908Sloos 53286908Sloosstruct rcc_gpio_pin { 54286908Sloos uint32_t pin; 55286908Sloos const char *name; 56286908Sloos uint32_t caps; 57286908Sloos}; 58286908Sloos 59286908Sloosstatic struct rcc_gpio_pin rcc_pins[] = { 60311205Sloos { .pin = (1 << 11), .name = "reset switch", .caps = GPIO_PIN_INPUT }, 61311205Sloos { .pin = (1 << 15), .name = "red LED", .caps = GPIO_PIN_OUTPUT }, 62311205Sloos { .pin = (1 << 17), .name = "green LED", .caps = GPIO_PIN_OUTPUT }, 63286908Sloos#if 0 64311205Sloos { .pin = (1 << 16), .name = "HD1 LED", .caps = GPIO_PIN_OUTPUT }, 65311205Sloos { .pin = (1 << 18), .name = "HD2 LED", .caps = GPIO_PIN_OUTPUT }, 66286908Sloos#endif 67286908Sloos}; 68286908Sloos 69286908Sloosstruct rcc_gpio_softc { 70286908Sloos device_t sc_dev; 71286908Sloos device_t sc_busdev; 72286908Sloos struct mtx sc_mtx; 73286908Sloos struct resource *sc_io_res; 74286908Sloos bus_space_tag_t sc_bst; 75286908Sloos bus_space_handle_t sc_bsh; 76286908Sloos uint32_t sc_output; 77286908Sloos int sc_io_rid; 78286908Sloos int sc_gpio_npins; 79286908Sloos}; 80286908Sloos 81286908Sloos#define RCC_GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) 82286908Sloos#define RCC_GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) 83286908Sloos#define RCC_WRITE(_sc, _off, _val) \ 84286908Sloos bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val) 85286908Sloos#define RCC_READ(_sc, _off) \ 86286908Sloos bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off) 87286908Sloos 88286908Sloosstatic void 89286908Sloosrcc_gpio_modify_bits(struct rcc_gpio_softc *sc, uint32_t reg, uint32_t mask, 90311205Sloos uint32_t writebits) 91286908Sloos{ 92286908Sloos uint32_t value; 93286908Sloos 94286908Sloos RCC_GPIO_LOCK(sc); 95286908Sloos value = RCC_READ(sc, reg); 96311205Sloos value &= ~mask; 97311205Sloos value |= writebits; 98286908Sloos RCC_WRITE(sc, reg, value); 99286908Sloos RCC_GPIO_UNLOCK(sc); 100286908Sloos} 101286908Sloos 102286908Sloosstatic device_t 103286908Sloosrcc_gpio_get_bus(device_t dev) 104286908Sloos{ 105286908Sloos struct rcc_gpio_softc *sc; 106286908Sloos 107286908Sloos sc = device_get_softc(dev); 108286908Sloos 109286908Sloos return (sc->sc_busdev); 110286908Sloos} 111286908Sloos 112286908Sloosstatic int 113286908Sloosrcc_gpio_pin_max(device_t dev, int *maxpin) 114286908Sloos{ 115286908Sloos struct rcc_gpio_softc *sc; 116286908Sloos 117286908Sloos sc = device_get_softc(dev); 118286908Sloos *maxpin = sc->sc_gpio_npins - 1; 119286908Sloos 120286908Sloos return (0); 121286908Sloos} 122286908Sloos 123286908Sloosstatic int 124286908Sloosrcc_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 125286908Sloos{ 126286908Sloos struct rcc_gpio_softc *sc; 127286908Sloos 128286908Sloos sc = device_get_softc(dev); 129287542Sloos if (pin >= sc->sc_gpio_npins) 130286908Sloos return (EINVAL); 131286908Sloos 132286908Sloos *caps = rcc_pins[pin].caps; 133286908Sloos 134286908Sloos return (0); 135286908Sloos} 136286908Sloos 137286908Sloosstatic int 138286908Sloosrcc_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 139286908Sloos{ 140286908Sloos struct rcc_gpio_softc *sc; 141286908Sloos 142286908Sloos sc = device_get_softc(dev); 143287542Sloos if (pin >= sc->sc_gpio_npins) 144286908Sloos return (EINVAL); 145286908Sloos 146286908Sloos /* Flags cannot be changed. */ 147286908Sloos *flags = rcc_pins[pin].caps; 148286908Sloos 149286908Sloos return (0); 150286908Sloos} 151286908Sloos 152286908Sloosstatic int 153286908Sloosrcc_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 154286908Sloos{ 155286908Sloos struct rcc_gpio_softc *sc; 156286908Sloos 157286908Sloos sc = device_get_softc(dev); 158287542Sloos if (pin >= sc->sc_gpio_npins) 159286908Sloos return (EINVAL); 160286908Sloos 161286908Sloos memcpy(name, rcc_pins[pin].name, GPIOMAXNAME); 162286908Sloos 163286908Sloos return (0); 164286908Sloos} 165286908Sloos 166286908Sloosstatic int 167286908Sloosrcc_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 168286908Sloos{ 169286908Sloos struct rcc_gpio_softc *sc; 170286908Sloos 171286908Sloos sc = device_get_softc(dev); 172287542Sloos if (pin >= sc->sc_gpio_npins) 173286908Sloos return (EINVAL); 174286908Sloos 175286908Sloos /* Flags cannot be changed - risk of short-circuit!!! */ 176286908Sloos 177286908Sloos return (0); 178286908Sloos} 179286908Sloos 180286908Sloosstatic int 181286908Sloosrcc_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) 182286908Sloos{ 183286908Sloos struct rcc_gpio_softc *sc; 184286908Sloos 185286908Sloos sc = device_get_softc(dev); 186287542Sloos if (pin >= sc->sc_gpio_npins) 187286908Sloos return (EINVAL); 188286908Sloos 189287542Sloos if ((rcc_pins[pin].caps & GPIO_PIN_OUTPUT) == 0) 190287542Sloos return (EINVAL); 191287542Sloos 192286908Sloos RCC_GPIO_LOCK(sc); 193286908Sloos if (value) 194286908Sloos sc->sc_output |= (1 << rcc_pins[pin].pin); 195286908Sloos else 196286908Sloos sc->sc_output &= ~(1 << rcc_pins[pin].pin); 197286908Sloos RCC_WRITE(sc, RCC_GPIO_GP_LVL, sc->sc_output); 198286908Sloos RCC_GPIO_UNLOCK(sc); 199286908Sloos 200286908Sloos return (0); 201286908Sloos} 202286908Sloos 203286908Sloosstatic int 204286908Sloosrcc_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) 205286908Sloos{ 206286908Sloos struct rcc_gpio_softc *sc; 207286908Sloos uint32_t value; 208286908Sloos 209286908Sloos sc = device_get_softc(dev); 210287542Sloos if (pin >= sc->sc_gpio_npins) 211286908Sloos return (EINVAL); 212286908Sloos 213286908Sloos RCC_GPIO_LOCK(sc); 214286908Sloos if (rcc_pins[pin].caps & GPIO_PIN_INPUT) 215286908Sloos value = RCC_READ(sc, RCC_GPIO_GP_LVL); 216286908Sloos else 217286908Sloos value = sc->sc_output; 218286908Sloos RCC_GPIO_UNLOCK(sc); 219286908Sloos *val = (value & (1 << rcc_pins[pin].pin)) ? 1 : 0; 220286908Sloos 221286908Sloos return (0); 222286908Sloos} 223286908Sloos 224286908Sloosstatic int 225286908Sloosrcc_gpio_pin_toggle(device_t dev, uint32_t pin) 226286908Sloos{ 227286908Sloos struct rcc_gpio_softc *sc; 228286908Sloos 229286908Sloos sc = device_get_softc(dev); 230287542Sloos if (pin >= sc->sc_gpio_npins) 231286908Sloos return (EINVAL); 232286908Sloos 233287542Sloos if ((rcc_pins[pin].caps & GPIO_PIN_OUTPUT) == 0) 234287542Sloos return (EINVAL); 235287542Sloos 236286908Sloos RCC_GPIO_LOCK(sc); 237286908Sloos if ((sc->sc_output & (1 << rcc_pins[pin].pin)) == 0) 238286908Sloos sc->sc_output |= (1 << rcc_pins[pin].pin); 239286908Sloos else 240286908Sloos sc->sc_output &= ~(1 << rcc_pins[pin].pin); 241286908Sloos RCC_WRITE(sc, RCC_GPIO_GP_LVL, sc->sc_output); 242286908Sloos RCC_GPIO_UNLOCK(sc); 243286908Sloos 244286908Sloos return (0); 245286908Sloos} 246286908Sloos 247286908Sloosstatic int 248286908Sloosrcc_gpio_probe(device_t dev) 249286908Sloos{ 250286908Sloos char *prod; 251286908Sloos int port; 252286908Sloos 253286908Sloos /* 254286908Sloos * We don't know of any PnP ID's for this GPIO controller. 255286908Sloos */ 256286908Sloos if (isa_get_logicalid(dev) != 0) 257286908Sloos return (ENXIO); 258286908Sloos 259286908Sloos /* 260286908Sloos * We have to have an IO port hint that is valid. 261286908Sloos */ 262286908Sloos port = isa_get_port(dev); 263286908Sloos if (port != RCC_GPIO_BASE) 264286908Sloos return (ENXIO); 265286908Sloos 266286908Sloos prod = kern_getenv("smbios.system.product"); 267286908Sloos if (prod == NULL || 268286908Sloos (strcmp(prod, "RCC-VE") != 0 && strcmp(prod, "RCC-DFF") != 0)) 269286908Sloos return (ENXIO); 270286908Sloos 271286908Sloos device_set_desc(dev, "RCC-VE/DFF GPIO controller"); 272286908Sloos 273286908Sloos return (BUS_PROBE_DEFAULT); 274286908Sloos} 275286908Sloos 276286908Sloosstatic int 277286908Sloosrcc_gpio_attach(device_t dev) 278286908Sloos{ 279286908Sloos int i; 280286908Sloos struct rcc_gpio_softc *sc; 281286908Sloos 282286908Sloos sc = device_get_softc(dev); 283286908Sloos sc->sc_dev = dev; 284286908Sloos 285286908Sloos /* Allocate IO resources. */ 286286908Sloos sc->sc_io_rid = 0; 287286908Sloos sc->sc_io_res = bus_alloc_resource_any(dev, SYS_RES_IOPORT, 288286908Sloos &sc->sc_io_rid, RF_ACTIVE); 289286908Sloos if (sc->sc_io_res == NULL) { 290286908Sloos device_printf(dev, "cannot allocate memory window\n"); 291286908Sloos return (ENXIO); 292286908Sloos } 293286908Sloos sc->sc_bst = rman_get_bustag(sc->sc_io_res); 294286908Sloos sc->sc_bsh = rman_get_bushandle(sc->sc_io_res); 295286908Sloos mtx_init(&sc->sc_mtx, "rcc-gpio", "gpio", MTX_DEF); 296286908Sloos 297286908Sloos /* Initialize the pins. */ 298286908Sloos sc->sc_gpio_npins = nitems(rcc_pins); 299286908Sloos for (i = 0; i < sc->sc_gpio_npins; i++) { 300286908Sloos /* Enable it for GPIO. */ 301286908Sloos rcc_gpio_modify_bits(sc, RCC_GPIO_USE_SEL, 0, rcc_pins[i].pin); 302286908Sloos /* Set the pin as input or output. */ 303286908Sloos if (rcc_pins[i].caps & GPIO_PIN_OUTPUT) 304286908Sloos rcc_gpio_modify_bits(sc, RCC_GPIO_IO_SEL, 305286908Sloos rcc_pins[i].pin, 0); 306286908Sloos else 307286908Sloos rcc_gpio_modify_bits(sc, RCC_GPIO_IO_SEL, 308286908Sloos 0, rcc_pins[i].pin); 309286908Sloos } 310286908Sloos RCC_WRITE(sc, RCC_GPIO_GP_LVL, sc->sc_output); 311286908Sloos 312286908Sloos /* Attach the gpiobus. */ 313286908Sloos sc->sc_busdev = gpiobus_attach_bus(dev); 314286908Sloos if (sc->sc_busdev == NULL) { 315286908Sloos bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_io_rid, 316286908Sloos sc->sc_io_res); 317286908Sloos mtx_destroy(&sc->sc_mtx); 318286908Sloos return (ENXIO); 319286908Sloos } 320286908Sloos 321286908Sloos return (0); 322286908Sloos} 323286908Sloos 324286908Sloosstatic int 325286908Sloosrcc_gpio_detach(device_t dev) 326286908Sloos{ 327286908Sloos int i; 328286908Sloos struct rcc_gpio_softc *sc; 329286908Sloos 330286908Sloos sc = device_get_softc(dev); 331286908Sloos gpiobus_detach_bus(dev); 332286908Sloos 333286908Sloos /* Disable the GPIO function. */ 334286908Sloos for (i = 0; i < sc->sc_gpio_npins; i++) 335286908Sloos rcc_gpio_modify_bits(sc, RCC_GPIO_USE_SEL, rcc_pins[i].pin, 0); 336286908Sloos 337286908Sloos if (sc->sc_io_res != NULL) 338286908Sloos bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_io_rid, 339286908Sloos sc->sc_io_res); 340286908Sloos mtx_destroy(&sc->sc_mtx); 341286908Sloos 342286908Sloos return (0); 343286908Sloos} 344286908Sloos 345286908Sloosstatic device_method_t rcc_gpio_methods[] = { 346286908Sloos /* Device interface */ 347286908Sloos DEVMETHOD(device_probe, rcc_gpio_probe), 348286908Sloos DEVMETHOD(device_attach, rcc_gpio_attach), 349286908Sloos DEVMETHOD(device_detach, rcc_gpio_detach), 350286908Sloos 351286908Sloos /* GPIO protocol */ 352286908Sloos DEVMETHOD(gpio_get_bus, rcc_gpio_get_bus), 353286908Sloos DEVMETHOD(gpio_pin_max, rcc_gpio_pin_max), 354286908Sloos DEVMETHOD(gpio_pin_getname, rcc_gpio_pin_getname), 355286908Sloos DEVMETHOD(gpio_pin_getflags, rcc_gpio_pin_getflags), 356286908Sloos DEVMETHOD(gpio_pin_getcaps, rcc_gpio_pin_getcaps), 357286908Sloos DEVMETHOD(gpio_pin_setflags, rcc_gpio_pin_setflags), 358286908Sloos DEVMETHOD(gpio_pin_get, rcc_gpio_pin_get), 359286908Sloos DEVMETHOD(gpio_pin_set, rcc_gpio_pin_set), 360286908Sloos DEVMETHOD(gpio_pin_toggle, rcc_gpio_pin_toggle), 361286908Sloos 362286908Sloos DEVMETHOD_END 363286908Sloos}; 364286908Sloos 365286908Sloosstatic devclass_t rcc_gpio_devclass; 366286908Sloos 367286908Sloosstatic driver_t rcc_gpio_driver = { 368286908Sloos "gpio", 369286908Sloos rcc_gpio_methods, 370286908Sloos sizeof(struct rcc_gpio_softc), 371286908Sloos}; 372286908Sloos 373286908SloosDRIVER_MODULE(rcc_gpio, isa, rcc_gpio_driver, rcc_gpio_devclass, 0, 0); 374286908SloosMODULE_DEPEND(rcc_gpio, gpiobus, 1, 1, 1); 375