1a9caca6aSWojciech A. Koszek /*- 24d846d26SWarner Losh * SPDX-License-Identifier: BSD-2-Clause 3af3dc4a7SPedro F. Giffuni * 440713190SWojciech A. Koszek * Copyright (c) 2013 Thomas Skibo 5a9caca6aSWojciech A. Koszek * All rights reserved. 6a9caca6aSWojciech A. Koszek * 7a9caca6aSWojciech A. Koszek * Redistribution and use in source and binary forms, with or without 840713190SWojciech A. Koszek * modification, are permitted provided that the following conditions 940713190SWojciech A. Koszek * are met: 1040713190SWojciech A. Koszek * 1. Redistributions of source code must retain the above copyright 11a9caca6aSWojciech A. Koszek * notice, this list of conditions and the following disclaimer. 1240713190SWojciech A. Koszek * 2. Redistributions in binary form must reproduce the above copyright 13a9caca6aSWojciech A. Koszek * notice, this list of conditions and the following disclaimer in the 14a9caca6aSWojciech A. Koszek * documentation and/or other materials provided with the distribution. 15a9caca6aSWojciech A. Koszek * 1640713190SWojciech A. Koszek * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1740713190SWojciech A. Koszek * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18a9caca6aSWojciech A. Koszek * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1940713190SWojciech A. Koszek * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 2040713190SWojciech A. Koszek * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2140713190SWojciech A. Koszek * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2240713190SWojciech A. Koszek * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2340713190SWojciech A. Koszek * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24a9caca6aSWojciech A. Koszek * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2540713190SWojciech A. Koszek * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2640713190SWojciech A. Koszek * SUCH DAMAGE. 27a9caca6aSWojciech A. Koszek */ 28a9caca6aSWojciech A. Koszek 2940713190SWojciech A. Koszek /* 3040713190SWojciech A. Koszek * A GPIO driver for Xilinx Zynq-7000. 31a9caca6aSWojciech A. Koszek * 32a9caca6aSWojciech A. Koszek * The GPIO peripheral on Zynq allows controlling 114 general purpose I/Os. 33a9caca6aSWojciech A. Koszek * 34a9caca6aSWojciech A. Koszek * Pins 53-0 are sent to the MIO. Any MIO pins not used by a PS peripheral are 35a9caca6aSWojciech A. Koszek * available as a GPIO pin. Pins 64-127 are sent to the PL (FPGA) section of 36a9caca6aSWojciech A. Koszek * Zynq as EMIO signals. 37a9caca6aSWojciech A. Koszek * 38a9caca6aSWojciech A. Koszek * The hardware provides a way to use IOs as interrupt sources but the 39a9caca6aSWojciech A. Koszek * gpio framework doesn't seem to have hooks for this. 40a9caca6aSWojciech A. Koszek * 41a9caca6aSWojciech A. Koszek * Reference: Zynq-7000 All Programmable SoC Technical Reference Manual. 42a9caca6aSWojciech A. Koszek * (v1.4) November 16, 2012. Xilinx doc UG585. GPIO is covered in 43a9caca6aSWojciech A. Koszek * chater 14. Register definitions are in appendix B.19. 44a9caca6aSWojciech A. Koszek */ 45a9caca6aSWojciech A. Koszek 46a9caca6aSWojciech A. Koszek #include <sys/param.h> 47a9caca6aSWojciech A. Koszek #include <sys/systm.h> 48a9caca6aSWojciech A. Koszek #include <sys/conf.h> 49a9caca6aSWojciech A. Koszek #include <sys/bus.h> 50a9caca6aSWojciech A. Koszek #include <sys/kernel.h> 51a9caca6aSWojciech A. Koszek #include <sys/module.h> 52a9caca6aSWojciech A. Koszek #include <sys/lock.h> 53a9caca6aSWojciech A. Koszek #include <sys/mutex.h> 54a9caca6aSWojciech A. Koszek #include <sys/resource.h> 55a9caca6aSWojciech A. Koszek #include <sys/rman.h> 56a9caca6aSWojciech A. Koszek #include <sys/gpio.h> 57a9caca6aSWojciech A. Koszek 58a9caca6aSWojciech A. Koszek #include <machine/bus.h> 59a9caca6aSWojciech A. Koszek #include <machine/resource.h> 60a9caca6aSWojciech A. Koszek #include <machine/stdarg.h> 61a9caca6aSWojciech A. Koszek 627836352bSLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h> 63a9caca6aSWojciech A. Koszek #include <dev/ofw/ofw_bus.h> 64a9caca6aSWojciech A. Koszek #include <dev/ofw/ofw_bus_subr.h> 65a9caca6aSWojciech A. Koszek 66a9caca6aSWojciech A. Koszek #include "gpio_if.h" 67a9caca6aSWojciech A. Koszek 68*c15106daSEmmanuel Vadot #define ZYNQ7_MAX_BANK 4 69*c15106daSEmmanuel Vadot #define ZYNQMP_MAX_BANK 6 70a9caca6aSWojciech A. Koszek 71b07fed81SEmmanuel Vadot /* Zynq 7000 */ 72*c15106daSEmmanuel Vadot #define ZYNQ7_BANK0_PIN_MIN 0 73*c15106daSEmmanuel Vadot #define ZYNQ7_BANK0_NPIN 32 74*c15106daSEmmanuel Vadot #define ZYNQ7_BANK1_PIN_MIN 32 75*c15106daSEmmanuel Vadot #define ZYNQ7_BANK1_NPIN 22 76*c15106daSEmmanuel Vadot #define ZYNQ7_BANK2_PIN_MIN 64 77*c15106daSEmmanuel Vadot #define ZYNQ7_BANK2_NPIN 32 78*c15106daSEmmanuel Vadot #define ZYNQ7_BANK3_PIN_MIN 96 79*c15106daSEmmanuel Vadot #define ZYNQ7_BANK3_NPIN 32 80*c15106daSEmmanuel Vadot #define ZYNQ7_PIN_MIO_MIN 0 81*c15106daSEmmanuel Vadot #define ZYNQ7_PIN_MIO_MAX 54 82*c15106daSEmmanuel Vadot #define ZYNQ7_PIN_EMIO_MIN 64 83*c15106daSEmmanuel Vadot #define ZYNQ7_PIN_EMIO_MAX 118 84a9caca6aSWojciech A. Koszek 85*c15106daSEmmanuel Vadot /* ZynqMP */ 86*c15106daSEmmanuel Vadot #define ZYNQMP_BANK0_PIN_MIN 0 87*c15106daSEmmanuel Vadot #define ZYNQMP_BANK0_NPIN 26 88*c15106daSEmmanuel Vadot #define ZYNQMP_BANK1_PIN_MIN 26 89*c15106daSEmmanuel Vadot #define ZYNQMP_BANK1_NPIN 26 90*c15106daSEmmanuel Vadot #define ZYNQMP_BANK2_PIN_MIN 52 91*c15106daSEmmanuel Vadot #define ZYNQMP_BANK2_NPIN 26 92*c15106daSEmmanuel Vadot #define ZYNQMP_BANK3_PIN_MIN 78 93*c15106daSEmmanuel Vadot #define ZYNQMP_BANK3_NPIN 32 94*c15106daSEmmanuel Vadot #define ZYNQMP_BANK4_PIN_MIN 110 95*c15106daSEmmanuel Vadot #define ZYNQMP_BANK4_NPIN 32 96*c15106daSEmmanuel Vadot #define ZYNQMP_BANK5_PIN_MIN 142 97*c15106daSEmmanuel Vadot #define ZYNQMP_BANK5_NPIN 32 98*c15106daSEmmanuel Vadot #define ZYNQMP_PIN_MIO_MIN 0 99*c15106daSEmmanuel Vadot #define ZYNQMP_PIN_MIO_MAX 77 100*c15106daSEmmanuel Vadot #define ZYNQMP_PIN_EMIO_MIN 78 101*c15106daSEmmanuel Vadot #define ZYNQMP_PIN_EMIO_MAX 174 102b07fed81SEmmanuel Vadot 103*c15106daSEmmanuel Vadot #define ZYNQ_BANK_NPIN(type, bank) (ZYNQ##type##_BANK##bank##_NPIN) 104*c15106daSEmmanuel Vadot #define ZYNQ_BANK_PIN_MIN(type, bank) (ZYNQ##type##_BANK##bank##_PIN_MIN) 105*c15106daSEmmanuel Vadot #define ZYNQ_BANK_PIN_MAX(type, bank) (ZYNQ##type##_BANK##bank##_PIN_MIN + ZYNQ##type##_BANK##bank##_NPIN - 1) 106*c15106daSEmmanuel Vadot 107*c15106daSEmmanuel Vadot #define ZYNQ_PIN_IS_MIO(type, pin) (pin >= ZYNQ##type##_PIN_MIO_MIN && \ 108*c15106daSEmmanuel Vadot pin <= ZYNQ##type##_PIN_MIO_MAX) 109*c15106daSEmmanuel Vadot #define ZYNQ_PIN_IS_EMIO(type, pin) (pin >= ZYNQ##type##_PIN_EMIO_MIN && \ 110*c15106daSEmmanuel Vadot pin <= ZYNQ##type##_PIN_EMIO_MAX) 111a9caca6aSWojciech A. Koszek 112a9caca6aSWojciech A. Koszek #define ZGPIO_LOCK(sc) mtx_lock(&(sc)->sc_mtx) 113a9caca6aSWojciech A. Koszek #define ZGPIO_UNLOCK(sc) mtx_unlock(&(sc)->sc_mtx) 114a9caca6aSWojciech A. Koszek #define ZGPIO_LOCK_INIT(sc) \ 115a9caca6aSWojciech A. Koszek mtx_init(&(sc)->sc_mtx, device_get_nameunit((sc)->dev), \ 116a9caca6aSWojciech A. Koszek "gpio", MTX_DEF) 117a9caca6aSWojciech A. Koszek #define ZGPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); 118a9caca6aSWojciech A. Koszek 119*c15106daSEmmanuel Vadot enum zynq_gpio_type { 120*c15106daSEmmanuel Vadot ZYNQ_7000 = 0, 121*c15106daSEmmanuel Vadot ZYNQMP, 122*c15106daSEmmanuel Vadot }; 123*c15106daSEmmanuel Vadot 124b07fed81SEmmanuel Vadot struct zynq_gpio_conf { 125b07fed81SEmmanuel Vadot char *name; 126*c15106daSEmmanuel Vadot enum zynq_gpio_type type; 127b07fed81SEmmanuel Vadot uint32_t nbanks; 128b07fed81SEmmanuel Vadot uint32_t maxpin; 129*c15106daSEmmanuel Vadot uint32_t bank_min[ZYNQMP_MAX_BANK]; 130*c15106daSEmmanuel Vadot uint32_t bank_max[ZYNQMP_MAX_BANK]; 131b07fed81SEmmanuel Vadot }; 132b07fed81SEmmanuel Vadot 133a9caca6aSWojciech A. Koszek struct zy7_gpio_softc { 134a9caca6aSWojciech A. Koszek device_t dev; 1357836352bSLuiz Otavio O Souza device_t busdev; 136a9caca6aSWojciech A. Koszek struct mtx sc_mtx; 137a9caca6aSWojciech A. Koszek struct resource *mem_res; /* Memory resource */ 138b07fed81SEmmanuel Vadot struct zynq_gpio_conf *conf; 139b07fed81SEmmanuel Vadot }; 140b07fed81SEmmanuel Vadot 141b07fed81SEmmanuel Vadot static struct zynq_gpio_conf z7_gpio_conf = { 142b07fed81SEmmanuel Vadot .name = "Zynq-7000 GPIO Controller", 143*c15106daSEmmanuel Vadot .type = ZYNQ_7000, 144*c15106daSEmmanuel Vadot .nbanks = ZYNQ7_MAX_BANK, 145*c15106daSEmmanuel Vadot .maxpin = ZYNQ7_PIN_EMIO_MAX, 146*c15106daSEmmanuel Vadot .bank_min[0] = ZYNQ_BANK_PIN_MIN(7, 0), 147*c15106daSEmmanuel Vadot .bank_max[0] = ZYNQ_BANK_PIN_MAX(7, 0), 148*c15106daSEmmanuel Vadot .bank_min[1] = ZYNQ_BANK_PIN_MIN(7, 1), 149*c15106daSEmmanuel Vadot .bank_max[1] = ZYNQ_BANK_PIN_MAX(7, 1), 150*c15106daSEmmanuel Vadot .bank_min[2] = ZYNQ_BANK_PIN_MIN(7, 2), 151*c15106daSEmmanuel Vadot .bank_max[2] = ZYNQ_BANK_PIN_MAX(7, 2), 152*c15106daSEmmanuel Vadot .bank_min[3] = ZYNQ_BANK_PIN_MIN(7, 3), 153*c15106daSEmmanuel Vadot .bank_max[3] = ZYNQ_BANK_PIN_MAX(7, 3), 154*c15106daSEmmanuel Vadot }; 155*c15106daSEmmanuel Vadot 156*c15106daSEmmanuel Vadot static struct zynq_gpio_conf zynqmp_gpio_conf = { 157*c15106daSEmmanuel Vadot .name = "ZynqMP GPIO Controller", 158*c15106daSEmmanuel Vadot .type = ZYNQMP, 159*c15106daSEmmanuel Vadot .nbanks = ZYNQMP_MAX_BANK, 160*c15106daSEmmanuel Vadot .maxpin = ZYNQMP_PIN_EMIO_MAX, 161*c15106daSEmmanuel Vadot .bank_min[0] = ZYNQ_BANK_PIN_MIN(MP, 0), 162*c15106daSEmmanuel Vadot .bank_max[0] = ZYNQ_BANK_PIN_MAX(MP, 0), 163*c15106daSEmmanuel Vadot .bank_min[1] = ZYNQ_BANK_PIN_MIN(MP, 1), 164*c15106daSEmmanuel Vadot .bank_max[1] = ZYNQ_BANK_PIN_MAX(MP, 1), 165*c15106daSEmmanuel Vadot .bank_min[2] = ZYNQ_BANK_PIN_MIN(MP, 2), 166*c15106daSEmmanuel Vadot .bank_max[2] = ZYNQ_BANK_PIN_MAX(MP, 2), 167*c15106daSEmmanuel Vadot .bank_min[3] = ZYNQ_BANK_PIN_MIN(MP, 3), 168*c15106daSEmmanuel Vadot .bank_max[3] = ZYNQ_BANK_PIN_MAX(MP, 3), 169*c15106daSEmmanuel Vadot .bank_min[4] = ZYNQ_BANK_PIN_MIN(MP, 4), 170*c15106daSEmmanuel Vadot .bank_max[4] = ZYNQ_BANK_PIN_MAX(MP, 4), 171*c15106daSEmmanuel Vadot .bank_min[5] = ZYNQ_BANK_PIN_MIN(MP, 5), 172*c15106daSEmmanuel Vadot .bank_max[5] = ZYNQ_BANK_PIN_MAX(MP, 5), 173b07fed81SEmmanuel Vadot }; 174b07fed81SEmmanuel Vadot 175b07fed81SEmmanuel Vadot static struct ofw_compat_data compat_data[] = { 176b07fed81SEmmanuel Vadot {"xlnx,zy7_gpio", (uintptr_t)&z7_gpio_conf}, 177*c15106daSEmmanuel Vadot {"xlnx,zynqmp-gpio-1.0", (uintptr_t)&zynqmp_gpio_conf}, 178b07fed81SEmmanuel Vadot {NULL, 0}, 179a9caca6aSWojciech A. Koszek }; 180a9caca6aSWojciech A. Koszek 181a9caca6aSWojciech A. Koszek #define WR4(sc, off, val) bus_write_4((sc)->mem_res, (off), (val)) 182a9caca6aSWojciech A. Koszek #define RD4(sc, off) bus_read_4((sc)->mem_res, (off)) 183a9caca6aSWojciech A. Koszek 184a9caca6aSWojciech A. Koszek /* Xilinx Zynq-7000 GPIO register definitions: 185a9caca6aSWojciech A. Koszek */ 186a9caca6aSWojciech A. Koszek #define ZY7_GPIO_MASK_DATA_LSW(b) (0x0000+8*(b)) /* maskable wr lo */ 187a9caca6aSWojciech A. Koszek #define ZY7_GPIO_MASK_DATA_MSW(b) (0x0004+8*(b)) /* maskable wr hi */ 188a9caca6aSWojciech A. Koszek #define ZY7_GPIO_DATA(b) (0x0040+4*(b)) /* in/out data */ 189a9caca6aSWojciech A. Koszek #define ZY7_GPIO_DATA_RO(b) (0x0060+4*(b)) /* input data */ 190a9caca6aSWojciech A. Koszek 191a9caca6aSWojciech A. Koszek #define ZY7_GPIO_DIRM(b) (0x0204+0x40*(b)) /* direction mode */ 192a9caca6aSWojciech A. Koszek #define ZY7_GPIO_OEN(b) (0x0208+0x40*(b)) /* output enable */ 193a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_MASK(b) (0x020c+0x40*(b)) /* int mask */ 194a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_EN(b) (0x0210+0x40*(b)) /* int enable */ 195a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_DIS(b) (0x0214+0x40*(b)) /* int disable */ 196a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_STAT(b) (0x0218+0x40*(b)) /* int status */ 197a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_TYPE(b) (0x021c+0x40*(b)) /* int type */ 198a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_POLARITY(b) (0x0220+0x40*(b)) /* int polarity */ 199a9caca6aSWojciech A. Koszek #define ZY7_GPIO_INT_ANY(b) (0x0224+0x40*(b)) /* any edge */ 200a9caca6aSWojciech A. Koszek 2017836352bSLuiz Otavio O Souza static device_t 2027836352bSLuiz Otavio O Souza zy7_gpio_get_bus(device_t dev) 2037836352bSLuiz Otavio O Souza { 2047836352bSLuiz Otavio O Souza struct zy7_gpio_softc *sc; 2057836352bSLuiz Otavio O Souza 2067836352bSLuiz Otavio O Souza sc = device_get_softc(dev); 2077836352bSLuiz Otavio O Souza 2087836352bSLuiz Otavio O Souza return (sc->busdev); 2097836352bSLuiz Otavio O Souza } 210a9caca6aSWojciech A. Koszek 211a9caca6aSWojciech A. Koszek static int 212a9caca6aSWojciech A. Koszek zy7_gpio_pin_max(device_t dev, int *maxpin) 213a9caca6aSWojciech A. Koszek { 214b07fed81SEmmanuel Vadot struct zy7_gpio_softc *sc; 215a9caca6aSWojciech A. Koszek 216b07fed81SEmmanuel Vadot sc = device_get_softc(dev); 217b07fed81SEmmanuel Vadot *maxpin = sc->conf->maxpin; 218a9caca6aSWojciech A. Koszek return (0); 219a9caca6aSWojciech A. Koszek } 220a9caca6aSWojciech A. Koszek 221b07fed81SEmmanuel Vadot static inline bool 222b07fed81SEmmanuel Vadot zy7_pin_valid(device_t dev, uint32_t pin) 223b07fed81SEmmanuel Vadot { 224b07fed81SEmmanuel Vadot struct zy7_gpio_softc *sc; 225b07fed81SEmmanuel Vadot int i; 226b07fed81SEmmanuel Vadot bool found = false; 227b07fed81SEmmanuel Vadot 228b07fed81SEmmanuel Vadot sc = device_get_softc(dev); 229b07fed81SEmmanuel Vadot for (i = 0; i < sc->conf->nbanks; i++) { 230b07fed81SEmmanuel Vadot if (pin >= sc->conf->bank_min[i] && pin <= sc->conf->bank_max[i]) { 231b07fed81SEmmanuel Vadot found = true; 232b07fed81SEmmanuel Vadot break; 233b07fed81SEmmanuel Vadot } 234b07fed81SEmmanuel Vadot } 235b07fed81SEmmanuel Vadot 236b07fed81SEmmanuel Vadot return (found); 237b07fed81SEmmanuel Vadot } 238b07fed81SEmmanuel Vadot 239a9caca6aSWojciech A. Koszek /* Get a specific pin's capabilities. */ 240a9caca6aSWojciech A. Koszek static int 241a9caca6aSWojciech A. Koszek zy7_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 242a9caca6aSWojciech A. Koszek { 243a9caca6aSWojciech A. Koszek 244b07fed81SEmmanuel Vadot if (!zy7_pin_valid(dev, pin)) 245a9caca6aSWojciech A. Koszek return (EINVAL); 246a9caca6aSWojciech A. Koszek 247a9caca6aSWojciech A. Koszek *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE); 248a9caca6aSWojciech A. Koszek 249a9caca6aSWojciech A. Koszek return (0); 250a9caca6aSWojciech A. Koszek } 251a9caca6aSWojciech A. Koszek 252a9caca6aSWojciech A. Koszek /* Get a specific pin's name. */ 253a9caca6aSWojciech A. Koszek static int 254a9caca6aSWojciech A. Koszek zy7_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 255a9caca6aSWojciech A. Koszek { 256*c15106daSEmmanuel Vadot struct zy7_gpio_softc *sc; 257*c15106daSEmmanuel Vadot uint32_t emio_min; 258*c15106daSEmmanuel Vadot bool is_mio; 259a9caca6aSWojciech A. Koszek 260*c15106daSEmmanuel Vadot sc = device_get_softc(dev); 261b07fed81SEmmanuel Vadot if (!zy7_pin_valid(dev, pin)) 262a9caca6aSWojciech A. Koszek return (EINVAL); 263a9caca6aSWojciech A. Koszek 264*c15106daSEmmanuel Vadot switch (sc->conf->type) { 265*c15106daSEmmanuel Vadot case ZYNQ_7000: 266*c15106daSEmmanuel Vadot is_mio = ZYNQ_PIN_IS_MIO(7, pin); 267*c15106daSEmmanuel Vadot emio_min = ZYNQ7_PIN_EMIO_MIN; 268*c15106daSEmmanuel Vadot break; 269*c15106daSEmmanuel Vadot case ZYNQMP: 270*c15106daSEmmanuel Vadot is_mio = ZYNQ_PIN_IS_MIO(MP, pin); 271*c15106daSEmmanuel Vadot emio_min = ZYNQMP_PIN_EMIO_MIN; 272*c15106daSEmmanuel Vadot break; 273*c15106daSEmmanuel Vadot default: 274*c15106daSEmmanuel Vadot return (EINVAL); 275*c15106daSEmmanuel Vadot } 276*c15106daSEmmanuel Vadot if (is_mio) { 277a9caca6aSWojciech A. Koszek snprintf(name, GPIOMAXNAME, "MIO_%d", pin); 278a9caca6aSWojciech A. Koszek name[GPIOMAXNAME - 1] = '\0'; 279a9caca6aSWojciech A. Koszek } else { 280*c15106daSEmmanuel Vadot snprintf(name, GPIOMAXNAME, "EMIO_%d", pin - emio_min); 281a9caca6aSWojciech A. Koszek name[GPIOMAXNAME - 1] = '\0'; 282a9caca6aSWojciech A. Koszek } 283a9caca6aSWojciech A. Koszek 284a9caca6aSWojciech A. Koszek return (0); 285a9caca6aSWojciech A. Koszek } 286a9caca6aSWojciech A. Koszek 287a9caca6aSWojciech A. Koszek /* Get a specific pin's current in/out/tri state. */ 288a9caca6aSWojciech A. Koszek static int 289a9caca6aSWojciech A. Koszek zy7_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 290a9caca6aSWojciech A. Koszek { 291a9caca6aSWojciech A. Koszek struct zy7_gpio_softc *sc = device_get_softc(dev); 292a9caca6aSWojciech A. Koszek 293b07fed81SEmmanuel Vadot if (!zy7_pin_valid(dev, pin)) 294a9caca6aSWojciech A. Koszek return (EINVAL); 295a9caca6aSWojciech A. Koszek 296a9caca6aSWojciech A. Koszek ZGPIO_LOCK(sc); 297a9caca6aSWojciech A. Koszek 298a9caca6aSWojciech A. Koszek if ((RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) & (1 << (pin & 31))) != 0) { 299a9caca6aSWojciech A. Koszek /* output */ 300a9caca6aSWojciech A. Koszek if ((RD4(sc, ZY7_GPIO_OEN(pin >> 5)) & (1 << (pin & 31))) == 0) 301a9caca6aSWojciech A. Koszek *flags = (GPIO_PIN_OUTPUT | GPIO_PIN_TRISTATE); 302a9caca6aSWojciech A. Koszek else 303a9caca6aSWojciech A. Koszek *flags = GPIO_PIN_OUTPUT; 304a9caca6aSWojciech A. Koszek } else 305a9caca6aSWojciech A. Koszek /* input */ 306a9caca6aSWojciech A. Koszek *flags = GPIO_PIN_INPUT; 307a9caca6aSWojciech A. Koszek 308a9caca6aSWojciech A. Koszek ZGPIO_UNLOCK(sc); 309a9caca6aSWojciech A. Koszek 310a9caca6aSWojciech A. Koszek return (0); 311a9caca6aSWojciech A. Koszek } 312a9caca6aSWojciech A. Koszek 313a9caca6aSWojciech A. Koszek /* Set a specific pin's in/out/tri state. */ 314a9caca6aSWojciech A. Koszek static int 315a9caca6aSWojciech A. Koszek zy7_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 316a9caca6aSWojciech A. Koszek { 317a9caca6aSWojciech A. Koszek struct zy7_gpio_softc *sc = device_get_softc(dev); 318a9caca6aSWojciech A. Koszek 319b07fed81SEmmanuel Vadot if (!zy7_pin_valid(dev, pin)) 320a9caca6aSWojciech A. Koszek return (EINVAL); 321a9caca6aSWojciech A. Koszek 322a9caca6aSWojciech A. Koszek ZGPIO_LOCK(sc); 323a9caca6aSWojciech A. Koszek 324a9caca6aSWojciech A. Koszek if ((flags & GPIO_PIN_OUTPUT) != 0) { 325a9caca6aSWojciech A. Koszek /* Output. Set or reset OEN too. */ 326a9caca6aSWojciech A. Koszek WR4(sc, ZY7_GPIO_DIRM(pin >> 5), 327a9caca6aSWojciech A. Koszek RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) | (1 << (pin & 31))); 328a9caca6aSWojciech A. Koszek 329a9caca6aSWojciech A. Koszek if ((flags & GPIO_PIN_TRISTATE) != 0) 330a9caca6aSWojciech A. Koszek WR4(sc, ZY7_GPIO_OEN(pin >> 5), 331a9caca6aSWojciech A. Koszek RD4(sc, ZY7_GPIO_OEN(pin >> 5)) & 332a9caca6aSWojciech A. Koszek ~(1 << (pin & 31))); 333a9caca6aSWojciech A. Koszek else 334a9caca6aSWojciech A. Koszek WR4(sc, ZY7_GPIO_OEN(pin >> 5), 335a9caca6aSWojciech A. Koszek RD4(sc, ZY7_GPIO_OEN(pin >> 5)) | 336a9caca6aSWojciech A. Koszek (1 << (pin & 31))); 337a9caca6aSWojciech A. Koszek } else { 338a9caca6aSWojciech A. Koszek /* Input. Turn off OEN. */ 339a9caca6aSWojciech A. Koszek WR4(sc, ZY7_GPIO_DIRM(pin >> 5), 340a9caca6aSWojciech A. Koszek RD4(sc, ZY7_GPIO_DIRM(pin >> 5)) & ~(1 << (pin & 31))); 341a9caca6aSWojciech A. Koszek WR4(sc, ZY7_GPIO_OEN(pin >> 5), 342a9caca6aSWojciech A. Koszek RD4(sc, ZY7_GPIO_OEN(pin >> 5)) & ~(1 << (pin & 31))); 343a9caca6aSWojciech A. Koszek } 344a9caca6aSWojciech A. Koszek 345a9caca6aSWojciech A. Koszek ZGPIO_UNLOCK(sc); 346a9caca6aSWojciech A. Koszek 347a9caca6aSWojciech A. Koszek return (0); 348a9caca6aSWojciech A. Koszek } 349a9caca6aSWojciech A. Koszek 350a9caca6aSWojciech A. Koszek /* Set a specific output pin's value. */ 351a9caca6aSWojciech A. Koszek static int 352a9caca6aSWojciech A. Koszek zy7_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) 353a9caca6aSWojciech A. Koszek { 354a9caca6aSWojciech A. Koszek struct zy7_gpio_softc *sc = device_get_softc(dev); 355a9caca6aSWojciech A. Koszek 356b07fed81SEmmanuel Vadot if (!zy7_pin_valid(dev, pin) || value > 1) 357a9caca6aSWojciech A. Koszek return (EINVAL); 358a9caca6aSWojciech A. Koszek 359a9caca6aSWojciech A. Koszek /* Fancy register tricks allow atomic set or reset. */ 360a9caca6aSWojciech A. Koszek if ((pin & 16) != 0) 361a9caca6aSWojciech A. Koszek WR4(sc, ZY7_GPIO_MASK_DATA_MSW(pin >> 5), 362a9caca6aSWojciech A. Koszek (0xffff0000 ^ (0x10000 << (pin & 15))) | 363a9caca6aSWojciech A. Koszek (value << (pin & 15))); 364a9caca6aSWojciech A. Koszek else 365a9caca6aSWojciech A. Koszek WR4(sc, ZY7_GPIO_MASK_DATA_LSW(pin >> 5), 366a9caca6aSWojciech A. Koszek (0xffff0000 ^ (0x10000 << (pin & 15))) | 367a9caca6aSWojciech A. Koszek (value << (pin & 15))); 368a9caca6aSWojciech A. Koszek 369a9caca6aSWojciech A. Koszek return (0); 370a9caca6aSWojciech A. Koszek } 371a9caca6aSWojciech A. Koszek 372a9caca6aSWojciech A. Koszek /* Get a specific pin's input value. */ 373a9caca6aSWojciech A. Koszek static int 374a9caca6aSWojciech A. Koszek zy7_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) 375a9caca6aSWojciech A. Koszek { 376a9caca6aSWojciech A. Koszek struct zy7_gpio_softc *sc = device_get_softc(dev); 377a9caca6aSWojciech A. Koszek 378b07fed81SEmmanuel Vadot if (!zy7_pin_valid(dev, pin)) 379a9caca6aSWojciech A. Koszek return (EINVAL); 380a9caca6aSWojciech A. Koszek 381a9caca6aSWojciech A. Koszek *value = (RD4(sc, ZY7_GPIO_DATA_RO(pin >> 5)) >> (pin & 31)) & 1; 382a9caca6aSWojciech A. Koszek 383a9caca6aSWojciech A. Koszek return (0); 384a9caca6aSWojciech A. Koszek } 385a9caca6aSWojciech A. Koszek 386a9caca6aSWojciech A. Koszek /* Toggle a pin's output value. */ 387a9caca6aSWojciech A. Koszek static int 388a9caca6aSWojciech A. Koszek zy7_gpio_pin_toggle(device_t dev, uint32_t pin) 389a9caca6aSWojciech A. Koszek { 390a9caca6aSWojciech A. Koszek struct zy7_gpio_softc *sc = device_get_softc(dev); 391a9caca6aSWojciech A. Koszek 392b07fed81SEmmanuel Vadot if (!zy7_pin_valid(dev, pin)) 393a9caca6aSWojciech A. Koszek return (EINVAL); 394a9caca6aSWojciech A. Koszek 395a9caca6aSWojciech A. Koszek ZGPIO_LOCK(sc); 396a9caca6aSWojciech A. Koszek 397a9caca6aSWojciech A. Koszek WR4(sc, ZY7_GPIO_DATA(pin >> 5), 398a9caca6aSWojciech A. Koszek RD4(sc, ZY7_GPIO_DATA(pin >> 5)) ^ (1 << (pin & 31))); 399a9caca6aSWojciech A. Koszek 400a9caca6aSWojciech A. Koszek ZGPIO_UNLOCK(sc); 401a9caca6aSWojciech A. Koszek 402a9caca6aSWojciech A. Koszek return (0); 403a9caca6aSWojciech A. Koszek } 404a9caca6aSWojciech A. Koszek 405a9caca6aSWojciech A. Koszek static int 406a9caca6aSWojciech A. Koszek zy7_gpio_probe(device_t dev) 407a9caca6aSWojciech A. Koszek { 408b07fed81SEmmanuel Vadot struct zynq_gpio_conf *conf; 409a9caca6aSWojciech A. Koszek 410add35ed5SIan Lepore if (!ofw_bus_status_okay(dev)) 411add35ed5SIan Lepore return (ENXIO); 412add35ed5SIan Lepore 413b07fed81SEmmanuel Vadot conf = (struct zynq_gpio_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; 414b07fed81SEmmanuel Vadot if (conf == 0) 415a9caca6aSWojciech A. Koszek return (ENXIO); 416a9caca6aSWojciech A. Koszek 417b07fed81SEmmanuel Vadot device_set_desc(dev, conf->name); 418a9caca6aSWojciech A. Koszek return (0); 419a9caca6aSWojciech A. Koszek } 420a9caca6aSWojciech A. Koszek 421a9caca6aSWojciech A. Koszek static int zy7_gpio_detach(device_t dev); 422a9caca6aSWojciech A. Koszek 423a9caca6aSWojciech A. Koszek static int 424a9caca6aSWojciech A. Koszek zy7_gpio_attach(device_t dev) 425a9caca6aSWojciech A. Koszek { 426a9caca6aSWojciech A. Koszek struct zy7_gpio_softc *sc = device_get_softc(dev); 427a9caca6aSWojciech A. Koszek int rid; 428a9caca6aSWojciech A. Koszek 429a9caca6aSWojciech A. Koszek sc->dev = dev; 430b07fed81SEmmanuel Vadot sc->conf = (struct zynq_gpio_conf *)ofw_bus_search_compatible(dev, compat_data)->ocd_data; 431a9caca6aSWojciech A. Koszek 432a9caca6aSWojciech A. Koszek ZGPIO_LOCK_INIT(sc); 433a9caca6aSWojciech A. Koszek 434a9caca6aSWojciech A. Koszek /* Allocate memory. */ 435a9caca6aSWojciech A. Koszek rid = 0; 436a9caca6aSWojciech A. Koszek sc->mem_res = bus_alloc_resource_any(dev, 437a9caca6aSWojciech A. Koszek SYS_RES_MEMORY, &rid, RF_ACTIVE); 438a9caca6aSWojciech A. Koszek if (sc->mem_res == NULL) { 439a9caca6aSWojciech A. Koszek device_printf(dev, "Can't allocate memory for device"); 440a9caca6aSWojciech A. Koszek zy7_gpio_detach(dev); 441a9caca6aSWojciech A. Koszek return (ENOMEM); 442a9caca6aSWojciech A. Koszek } 443a9caca6aSWojciech A. Koszek 4447836352bSLuiz Otavio O Souza sc->busdev = gpiobus_attach_bus(dev); 4457836352bSLuiz Otavio O Souza if (sc->busdev == NULL) { 4467836352bSLuiz Otavio O Souza zy7_gpio_detach(dev); 4477836352bSLuiz Otavio O Souza return (ENOMEM); 4487836352bSLuiz Otavio O Souza } 449a9caca6aSWojciech A. Koszek 4507836352bSLuiz Otavio O Souza return (0); 451a9caca6aSWojciech A. Koszek } 452a9caca6aSWojciech A. Koszek 453a9caca6aSWojciech A. Koszek static int 454a9caca6aSWojciech A. Koszek zy7_gpio_detach(device_t dev) 455a9caca6aSWojciech A. Koszek { 456a9caca6aSWojciech A. Koszek struct zy7_gpio_softc *sc = device_get_softc(dev); 457a9caca6aSWojciech A. Koszek 4587836352bSLuiz Otavio O Souza gpiobus_detach_bus(dev); 459a9caca6aSWojciech A. Koszek 460a9caca6aSWojciech A. Koszek if (sc->mem_res != NULL) { 461a9caca6aSWojciech A. Koszek /* Release memory resource. */ 462a9caca6aSWojciech A. Koszek bus_release_resource(dev, SYS_RES_MEMORY, 463a9caca6aSWojciech A. Koszek rman_get_rid(sc->mem_res), sc->mem_res); 464a9caca6aSWojciech A. Koszek } 465a9caca6aSWojciech A. Koszek 466a9caca6aSWojciech A. Koszek ZGPIO_LOCK_DESTROY(sc); 467a9caca6aSWojciech A. Koszek 468a9caca6aSWojciech A. Koszek return (0); 469a9caca6aSWojciech A. Koszek } 470a9caca6aSWojciech A. Koszek 471a9caca6aSWojciech A. Koszek static device_method_t zy7_gpio_methods[] = { 472a9caca6aSWojciech A. Koszek /* device_if */ 473a9caca6aSWojciech A. Koszek DEVMETHOD(device_probe, zy7_gpio_probe), 474a9caca6aSWojciech A. Koszek DEVMETHOD(device_attach, zy7_gpio_attach), 475a9caca6aSWojciech A. Koszek DEVMETHOD(device_detach, zy7_gpio_detach), 476a9caca6aSWojciech A. Koszek 477a9caca6aSWojciech A. Koszek /* GPIO protocol */ 4787836352bSLuiz Otavio O Souza DEVMETHOD(gpio_get_bus, zy7_gpio_get_bus), 479a9caca6aSWojciech A. Koszek DEVMETHOD(gpio_pin_max, zy7_gpio_pin_max), 480a9caca6aSWojciech A. Koszek DEVMETHOD(gpio_pin_getname, zy7_gpio_pin_getname), 481a9caca6aSWojciech A. Koszek DEVMETHOD(gpio_pin_getflags, zy7_gpio_pin_getflags), 482a9caca6aSWojciech A. Koszek DEVMETHOD(gpio_pin_getcaps, zy7_gpio_pin_getcaps), 483a9caca6aSWojciech A. Koszek DEVMETHOD(gpio_pin_setflags, zy7_gpio_pin_setflags), 484a9caca6aSWojciech A. Koszek DEVMETHOD(gpio_pin_get, zy7_gpio_pin_get), 485a9caca6aSWojciech A. Koszek DEVMETHOD(gpio_pin_set, zy7_gpio_pin_set), 486a9caca6aSWojciech A. Koszek DEVMETHOD(gpio_pin_toggle, zy7_gpio_pin_toggle), 487a9caca6aSWojciech A. Koszek 488a9caca6aSWojciech A. Koszek DEVMETHOD_END 489a9caca6aSWojciech A. Koszek }; 490a9caca6aSWojciech A. Koszek 491a9caca6aSWojciech A. Koszek static driver_t zy7_gpio_driver = { 492c67d5d66SLuiz Otavio O Souza "gpio", 493a9caca6aSWojciech A. Koszek zy7_gpio_methods, 494a9caca6aSWojciech A. Koszek sizeof(struct zy7_gpio_softc), 495a9caca6aSWojciech A. Koszek }; 496a9caca6aSWojciech A. Koszek 497b07fed81SEmmanuel Vadot EARLY_DRIVER_MODULE(zy7_gpio, simplebus, zy7_gpio_driver, 0, 0, 498b07fed81SEmmanuel Vadot BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); 499