14063f925SOleksandr Tymoshenko /*- 24063f925SOleksandr Tymoshenko * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> 34063f925SOleksandr Tymoshenko * Copyright (c) 2012 Luiz Otavio O Souza. 44063f925SOleksandr Tymoshenko * All rights reserved. 54063f925SOleksandr Tymoshenko * 64063f925SOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without 74063f925SOleksandr Tymoshenko * modification, are permitted provided that the following conditions 84063f925SOleksandr Tymoshenko * are met: 94063f925SOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright 104063f925SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer. 114063f925SOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright 124063f925SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the 134063f925SOleksandr Tymoshenko * documentation and/or other materials provided with the distribution. 144063f925SOleksandr Tymoshenko * 154063f925SOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 164063f925SOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 174063f925SOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 184063f925SOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 194063f925SOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 204063f925SOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 214063f925SOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 224063f925SOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 234063f925SOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 244063f925SOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 254063f925SOleksandr Tymoshenko * SUCH DAMAGE. 264063f925SOleksandr Tymoshenko * 274063f925SOleksandr Tymoshenko */ 284063f925SOleksandr Tymoshenko #include <sys/cdefs.h> 294063f925SOleksandr Tymoshenko __FBSDID("$FreeBSD$"); 304063f925SOleksandr Tymoshenko 314063f925SOleksandr Tymoshenko #include <sys/param.h> 324063f925SOleksandr Tymoshenko #include <sys/systm.h> 334063f925SOleksandr Tymoshenko #include <sys/bus.h> 344063f925SOleksandr Tymoshenko 354063f925SOleksandr Tymoshenko #include <sys/kernel.h> 364063f925SOleksandr Tymoshenko #include <sys/module.h> 374063f925SOleksandr Tymoshenko #include <sys/rman.h> 384063f925SOleksandr Tymoshenko #include <sys/lock.h> 394063f925SOleksandr Tymoshenko #include <sys/mutex.h> 404063f925SOleksandr Tymoshenko #include <sys/gpio.h> 4190576f54SOleksandr Tymoshenko #include <sys/sysctl.h> 424063f925SOleksandr Tymoshenko 434063f925SOleksandr Tymoshenko #include <machine/bus.h> 444063f925SOleksandr Tymoshenko #include <machine/cpu.h> 454063f925SOleksandr Tymoshenko #include <machine/cpufunc.h> 464063f925SOleksandr Tymoshenko #include <machine/resource.h> 474063f925SOleksandr Tymoshenko #include <machine/fdt.h> 484063f925SOleksandr Tymoshenko #include <machine/intr.h> 494063f925SOleksandr Tymoshenko 504063f925SOleksandr Tymoshenko #include <dev/fdt/fdt_common.h> 514063f925SOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h> 524063f925SOleksandr Tymoshenko #include <dev/ofw/ofw_bus_subr.h> 534063f925SOleksandr Tymoshenko 5444d06d8dSLuiz Otavio O Souza #include <arm/broadcom/bcm2835/bcm2835_gpio.h> 5544d06d8dSLuiz Otavio O Souza 564063f925SOleksandr Tymoshenko #include "gpio_if.h" 574063f925SOleksandr Tymoshenko 584063f925SOleksandr Tymoshenko #ifdef DEBUG 594063f925SOleksandr Tymoshenko #define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ 604063f925SOleksandr Tymoshenko printf(fmt,##args); } while (0) 614063f925SOleksandr Tymoshenko #else 624063f925SOleksandr Tymoshenko #define dprintf(fmt, args...) 634063f925SOleksandr Tymoshenko #endif 644063f925SOleksandr Tymoshenko 654063f925SOleksandr Tymoshenko #define BCM_GPIO_PINS 54 664063f925SOleksandr Tymoshenko #define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ 674063f925SOleksandr Tymoshenko GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) 684063f925SOleksandr Tymoshenko 6990576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl { 7090576f54SOleksandr Tymoshenko struct bcm_gpio_softc *sc; 7190576f54SOleksandr Tymoshenko uint32_t pin; 7290576f54SOleksandr Tymoshenko }; 7390576f54SOleksandr Tymoshenko 744063f925SOleksandr Tymoshenko struct bcm_gpio_softc { 754063f925SOleksandr Tymoshenko device_t sc_dev; 764063f925SOleksandr Tymoshenko struct mtx sc_mtx; 774063f925SOleksandr Tymoshenko struct resource * sc_mem_res; 784063f925SOleksandr Tymoshenko struct resource * sc_irq_res; 794063f925SOleksandr Tymoshenko bus_space_tag_t sc_bst; 804063f925SOleksandr Tymoshenko bus_space_handle_t sc_bsh; 814063f925SOleksandr Tymoshenko void * sc_intrhand; 824063f925SOleksandr Tymoshenko int sc_gpio_npins; 834063f925SOleksandr Tymoshenko int sc_ro_npins; 844063f925SOleksandr Tymoshenko int sc_ro_pins[BCM_GPIO_PINS]; 854063f925SOleksandr Tymoshenko struct gpio_pin sc_gpio_pins[BCM_GPIO_PINS]; 8690576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl sc_sysctl[BCM_GPIO_PINS]; 874063f925SOleksandr Tymoshenko }; 884063f925SOleksandr Tymoshenko 894063f925SOleksandr Tymoshenko enum bcm_gpio_pud { 904063f925SOleksandr Tymoshenko BCM_GPIO_NONE, 914063f925SOleksandr Tymoshenko BCM_GPIO_PULLDOWN, 924063f925SOleksandr Tymoshenko BCM_GPIO_PULLUP, 934063f925SOleksandr Tymoshenko }; 944063f925SOleksandr Tymoshenko 954063f925SOleksandr Tymoshenko #define BCM_GPIO_LOCK(_sc) mtx_lock(&_sc->sc_mtx) 964063f925SOleksandr Tymoshenko #define BCM_GPIO_UNLOCK(_sc) mtx_unlock(&_sc->sc_mtx) 9790576f54SOleksandr Tymoshenko #define BCM_GPIO_LOCK_ASSERT(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED) 984063f925SOleksandr Tymoshenko 994063f925SOleksandr Tymoshenko #define BCM_GPIO_GPFSEL(_bank) 0x00 + _bank * 4 1004063f925SOleksandr Tymoshenko #define BCM_GPIO_GPSET(_bank) 0x1c + _bank * 4 1014063f925SOleksandr Tymoshenko #define BCM_GPIO_GPCLR(_bank) 0x28 + _bank * 4 1024063f925SOleksandr Tymoshenko #define BCM_GPIO_GPLEV(_bank) 0x34 + _bank * 4 1034063f925SOleksandr Tymoshenko #define BCM_GPIO_GPPUD(_bank) 0x94 1044063f925SOleksandr Tymoshenko #define BCM_GPIO_GPPUDCLK(_bank) 0x98 + _bank * 4 1054063f925SOleksandr Tymoshenko 1064063f925SOleksandr Tymoshenko #define BCM_GPIO_WRITE(_sc, _off, _val) \ 1074063f925SOleksandr Tymoshenko bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) 1084063f925SOleksandr Tymoshenko #define BCM_GPIO_READ(_sc, _off) \ 1094063f925SOleksandr Tymoshenko bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off) 1104063f925SOleksandr Tymoshenko 1114063f925SOleksandr Tymoshenko static int 1124063f925SOleksandr Tymoshenko bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin) 1134063f925SOleksandr Tymoshenko { 1144063f925SOleksandr Tymoshenko int i; 1154063f925SOleksandr Tymoshenko 1164063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_ro_npins; i++) 1174063f925SOleksandr Tymoshenko if (pin == sc->sc_ro_pins[i]) 1184063f925SOleksandr Tymoshenko return (1); 1194063f925SOleksandr Tymoshenko return (0); 1204063f925SOleksandr Tymoshenko } 1214063f925SOleksandr Tymoshenko 1224063f925SOleksandr Tymoshenko static uint32_t 1234063f925SOleksandr Tymoshenko bcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin) 1244063f925SOleksandr Tymoshenko { 12590576f54SOleksandr Tymoshenko uint32_t bank, func, offset; 1264063f925SOleksandr Tymoshenko 1274063f925SOleksandr Tymoshenko /* Five banks, 10 pins per bank, 3 bits per pin. */ 1284063f925SOleksandr Tymoshenko bank = pin / 10; 1294063f925SOleksandr Tymoshenko offset = (pin - bank * 10) * 3; 1304063f925SOleksandr Tymoshenko 1314063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 13290576f54SOleksandr Tymoshenko func = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7; 1334063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 1344063f925SOleksandr Tymoshenko 13590576f54SOleksandr Tymoshenko return (func); 13690576f54SOleksandr Tymoshenko } 13790576f54SOleksandr Tymoshenko 13890576f54SOleksandr Tymoshenko static void 13990576f54SOleksandr Tymoshenko bcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize) 14090576f54SOleksandr Tymoshenko { 14190576f54SOleksandr Tymoshenko 14290576f54SOleksandr Tymoshenko switch (nfunc) { 1434063f925SOleksandr Tymoshenko case BCM_GPIO_INPUT: 14490576f54SOleksandr Tymoshenko strncpy(buf, "input", bufsize); 1454063f925SOleksandr Tymoshenko break; 1464063f925SOleksandr Tymoshenko case BCM_GPIO_OUTPUT: 14790576f54SOleksandr Tymoshenko strncpy(buf, "output", bufsize); 1484063f925SOleksandr Tymoshenko break; 1494063f925SOleksandr Tymoshenko case BCM_GPIO_ALT0: 15090576f54SOleksandr Tymoshenko strncpy(buf, "alt0", bufsize); 1514063f925SOleksandr Tymoshenko break; 1524063f925SOleksandr Tymoshenko case BCM_GPIO_ALT1: 15390576f54SOleksandr Tymoshenko strncpy(buf, "alt1", bufsize); 1544063f925SOleksandr Tymoshenko break; 1554063f925SOleksandr Tymoshenko case BCM_GPIO_ALT2: 15690576f54SOleksandr Tymoshenko strncpy(buf, "alt2", bufsize); 1574063f925SOleksandr Tymoshenko break; 1584063f925SOleksandr Tymoshenko case BCM_GPIO_ALT3: 15990576f54SOleksandr Tymoshenko strncpy(buf, "alt3", bufsize); 1604063f925SOleksandr Tymoshenko break; 1614063f925SOleksandr Tymoshenko case BCM_GPIO_ALT4: 16290576f54SOleksandr Tymoshenko strncpy(buf, "alt4", bufsize); 1634063f925SOleksandr Tymoshenko break; 1644063f925SOleksandr Tymoshenko case BCM_GPIO_ALT5: 16590576f54SOleksandr Tymoshenko strncpy(buf, "alt5", bufsize); 1664063f925SOleksandr Tymoshenko break; 16790576f54SOleksandr Tymoshenko default: 16890576f54SOleksandr Tymoshenko strncpy(buf, "invalid", bufsize); 1694063f925SOleksandr Tymoshenko } 17090576f54SOleksandr Tymoshenko } 1714063f925SOleksandr Tymoshenko 17290576f54SOleksandr Tymoshenko static int 17390576f54SOleksandr Tymoshenko bcm_gpio_str_func(char *func, uint32_t *nfunc) 17490576f54SOleksandr Tymoshenko { 17590576f54SOleksandr Tymoshenko 17690576f54SOleksandr Tymoshenko if (strcasecmp(func, "input") == 0) 17790576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_INPUT; 17890576f54SOleksandr Tymoshenko else if (strcasecmp(func, "output") == 0) 17990576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_OUTPUT; 18090576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt0") == 0) 18190576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT0; 18290576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt1") == 0) 18390576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT1; 18490576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt2") == 0) 18590576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT2; 18690576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt3") == 0) 18790576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT3; 18890576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt4") == 0) 18990576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT4; 19090576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt5") == 0) 19190576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT5; 19290576f54SOleksandr Tymoshenko else 19390576f54SOleksandr Tymoshenko return (-1); 19490576f54SOleksandr Tymoshenko 19590576f54SOleksandr Tymoshenko return (0); 19690576f54SOleksandr Tymoshenko } 19790576f54SOleksandr Tymoshenko 19890576f54SOleksandr Tymoshenko static uint32_t 19990576f54SOleksandr Tymoshenko bcm_gpio_func_flag(uint32_t nfunc) 20090576f54SOleksandr Tymoshenko { 20190576f54SOleksandr Tymoshenko 20290576f54SOleksandr Tymoshenko switch (nfunc) { 2034063f925SOleksandr Tymoshenko case BCM_GPIO_INPUT: 2044063f925SOleksandr Tymoshenko return (GPIO_PIN_INPUT); 2054063f925SOleksandr Tymoshenko case BCM_GPIO_OUTPUT: 2064063f925SOleksandr Tymoshenko return (GPIO_PIN_OUTPUT); 2074063f925SOleksandr Tymoshenko } 2084063f925SOleksandr Tymoshenko return (0); 2094063f925SOleksandr Tymoshenko } 2104063f925SOleksandr Tymoshenko 2114063f925SOleksandr Tymoshenko static void 2124063f925SOleksandr Tymoshenko bcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f) 2134063f925SOleksandr Tymoshenko { 2144063f925SOleksandr Tymoshenko uint32_t bank, data, offset; 2154063f925SOleksandr Tymoshenko 21690576f54SOleksandr Tymoshenko /* Must be called with lock held. */ 21790576f54SOleksandr Tymoshenko BCM_GPIO_LOCK_ASSERT(sc); 21890576f54SOleksandr Tymoshenko 2194063f925SOleksandr Tymoshenko /* Five banks, 10 pins per bank, 3 bits per pin. */ 2204063f925SOleksandr Tymoshenko bank = pin / 10; 2214063f925SOleksandr Tymoshenko offset = (pin - bank * 10) * 3; 2224063f925SOleksandr Tymoshenko 2234063f925SOleksandr Tymoshenko data = BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)); 2244063f925SOleksandr Tymoshenko data &= ~(7 << offset); 2254063f925SOleksandr Tymoshenko data |= (f << offset); 2264063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPFSEL(bank), data); 2274063f925SOleksandr Tymoshenko } 2284063f925SOleksandr Tymoshenko 2294063f925SOleksandr Tymoshenko static void 2304063f925SOleksandr Tymoshenko bcm_gpio_set_pud(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t state) 2314063f925SOleksandr Tymoshenko { 2324063f925SOleksandr Tymoshenko uint32_t bank, offset; 2334063f925SOleksandr Tymoshenko 23490576f54SOleksandr Tymoshenko /* Must be called with lock held. */ 23590576f54SOleksandr Tymoshenko BCM_GPIO_LOCK_ASSERT(sc); 23690576f54SOleksandr Tymoshenko 2374063f925SOleksandr Tymoshenko bank = pin / 32; 2384063f925SOleksandr Tymoshenko offset = pin - 32 * bank; 2394063f925SOleksandr Tymoshenko 2404063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state); 2414063f925SOleksandr Tymoshenko DELAY(10); 2424063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), (1 << offset)); 2434063f925SOleksandr Tymoshenko DELAY(10); 2444063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0); 2454063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0); 2464063f925SOleksandr Tymoshenko } 2474063f925SOleksandr Tymoshenko 24844d06d8dSLuiz Otavio O Souza void 24944d06d8dSLuiz Otavio O Souza bcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc) 25044d06d8dSLuiz Otavio O Souza { 25144d06d8dSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 25244d06d8dSLuiz Otavio O Souza int i; 25344d06d8dSLuiz Otavio O Souza 25444d06d8dSLuiz Otavio O Souza sc = device_get_softc(dev); 25544d06d8dSLuiz Otavio O Souza BCM_GPIO_LOCK(sc); 25644d06d8dSLuiz Otavio O Souza 25744d06d8dSLuiz Otavio O Souza /* Disable pull-up or pull-down on pin. */ 25844d06d8dSLuiz Otavio O Souza bcm_gpio_set_pud(sc, pin, BCM_GPIO_NONE); 25944d06d8dSLuiz Otavio O Souza 26044d06d8dSLuiz Otavio O Souza /* And now set the pin function. */ 26144d06d8dSLuiz Otavio O Souza bcm_gpio_set_function(sc, pin, nfunc); 26244d06d8dSLuiz Otavio O Souza 26344d06d8dSLuiz Otavio O Souza /* Update the pin flags. */ 26444d06d8dSLuiz Otavio O Souza for (i = 0; i < sc->sc_gpio_npins; i++) { 26544d06d8dSLuiz Otavio O Souza if (sc->sc_gpio_pins[i].gp_pin == pin) 26644d06d8dSLuiz Otavio O Souza break; 26744d06d8dSLuiz Otavio O Souza } 26844d06d8dSLuiz Otavio O Souza if (i < sc->sc_gpio_npins) 26944d06d8dSLuiz Otavio O Souza sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc); 27044d06d8dSLuiz Otavio O Souza 27144d06d8dSLuiz Otavio O Souza BCM_GPIO_UNLOCK(sc); 27244d06d8dSLuiz Otavio O Souza } 27344d06d8dSLuiz Otavio O Souza 2744063f925SOleksandr Tymoshenko static void 2754063f925SOleksandr Tymoshenko bcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin, 2764063f925SOleksandr Tymoshenko unsigned int flags) 2774063f925SOleksandr Tymoshenko { 2784063f925SOleksandr Tymoshenko 27990576f54SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 28090576f54SOleksandr Tymoshenko 2814063f925SOleksandr Tymoshenko /* 2824063f925SOleksandr Tymoshenko * Manage input/output. 2834063f925SOleksandr Tymoshenko */ 2844063f925SOleksandr Tymoshenko if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { 2854063f925SOleksandr Tymoshenko pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); 2864063f925SOleksandr Tymoshenko if (flags & GPIO_PIN_OUTPUT) { 2874063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_OUTPUT; 2884063f925SOleksandr Tymoshenko bcm_gpio_set_function(sc, pin->gp_pin, 2894063f925SOleksandr Tymoshenko BCM_GPIO_OUTPUT); 2904063f925SOleksandr Tymoshenko } else { 2914063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_INPUT; 2924063f925SOleksandr Tymoshenko bcm_gpio_set_function(sc, pin->gp_pin, 2934063f925SOleksandr Tymoshenko BCM_GPIO_INPUT); 2944063f925SOleksandr Tymoshenko } 2954063f925SOleksandr Tymoshenko } 2964063f925SOleksandr Tymoshenko 2974063f925SOleksandr Tymoshenko /* Manage Pull-up/pull-down. */ 2984063f925SOleksandr Tymoshenko pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN); 2994063f925SOleksandr Tymoshenko if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) { 3004063f925SOleksandr Tymoshenko if (flags & GPIO_PIN_PULLUP) { 3014063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_PULLUP; 3024063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLUP); 3034063f925SOleksandr Tymoshenko } else { 3044063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_PULLDOWN; 3054063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLDOWN); 3064063f925SOleksandr Tymoshenko } 3074063f925SOleksandr Tymoshenko } else 3084063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE); 30990576f54SOleksandr Tymoshenko 31090576f54SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 3114063f925SOleksandr Tymoshenko } 3124063f925SOleksandr Tymoshenko 3134063f925SOleksandr Tymoshenko static int 3144063f925SOleksandr Tymoshenko bcm_gpio_pin_max(device_t dev, int *maxpin) 3154063f925SOleksandr Tymoshenko { 3164063f925SOleksandr Tymoshenko 3174063f925SOleksandr Tymoshenko *maxpin = BCM_GPIO_PINS - 1; 3184063f925SOleksandr Tymoshenko return (0); 3194063f925SOleksandr Tymoshenko } 3204063f925SOleksandr Tymoshenko 3214063f925SOleksandr Tymoshenko static int 3224063f925SOleksandr Tymoshenko bcm_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 3234063f925SOleksandr Tymoshenko { 3244063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 3254063f925SOleksandr Tymoshenko int i; 3264063f925SOleksandr Tymoshenko 3274063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 3284063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 3294063f925SOleksandr Tymoshenko break; 3304063f925SOleksandr Tymoshenko } 3314063f925SOleksandr Tymoshenko 3324063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 3334063f925SOleksandr Tymoshenko return (EINVAL); 3344063f925SOleksandr Tymoshenko 3354063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 3364063f925SOleksandr Tymoshenko *caps = sc->sc_gpio_pins[i].gp_caps; 3374063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 3384063f925SOleksandr Tymoshenko 3394063f925SOleksandr Tymoshenko return (0); 3404063f925SOleksandr Tymoshenko } 3414063f925SOleksandr Tymoshenko 3424063f925SOleksandr Tymoshenko static int 3434063f925SOleksandr Tymoshenko bcm_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 3444063f925SOleksandr Tymoshenko { 3454063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 3464063f925SOleksandr Tymoshenko int i; 3474063f925SOleksandr Tymoshenko 3484063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 3494063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 3504063f925SOleksandr Tymoshenko break; 3514063f925SOleksandr Tymoshenko } 3524063f925SOleksandr Tymoshenko 3534063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 3544063f925SOleksandr Tymoshenko return (EINVAL); 3554063f925SOleksandr Tymoshenko 3564063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 3574063f925SOleksandr Tymoshenko *flags = sc->sc_gpio_pins[i].gp_flags; 3584063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 3594063f925SOleksandr Tymoshenko 3604063f925SOleksandr Tymoshenko return (0); 3614063f925SOleksandr Tymoshenko } 3624063f925SOleksandr Tymoshenko 3634063f925SOleksandr Tymoshenko static int 3644063f925SOleksandr Tymoshenko bcm_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 3654063f925SOleksandr Tymoshenko { 3664063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 3674063f925SOleksandr Tymoshenko int i; 3684063f925SOleksandr Tymoshenko 3694063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 3704063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 3714063f925SOleksandr Tymoshenko break; 3724063f925SOleksandr Tymoshenko } 3734063f925SOleksandr Tymoshenko 3744063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 3754063f925SOleksandr Tymoshenko return (EINVAL); 3764063f925SOleksandr Tymoshenko 3774063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 3784063f925SOleksandr Tymoshenko memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); 3794063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 3804063f925SOleksandr Tymoshenko 3814063f925SOleksandr Tymoshenko return (0); 3824063f925SOleksandr Tymoshenko } 3834063f925SOleksandr Tymoshenko 3844063f925SOleksandr Tymoshenko static int 3854063f925SOleksandr Tymoshenko bcm_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 3864063f925SOleksandr Tymoshenko { 3874063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 3884063f925SOleksandr Tymoshenko int i; 3894063f925SOleksandr Tymoshenko 3904063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 3914063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 3924063f925SOleksandr Tymoshenko break; 3934063f925SOleksandr Tymoshenko } 3944063f925SOleksandr Tymoshenko 3954063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 3964063f925SOleksandr Tymoshenko return (EINVAL); 3974063f925SOleksandr Tymoshenko 3984063f925SOleksandr Tymoshenko /* We never touch on read-only/reserved pins. */ 3994063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 4004063f925SOleksandr Tymoshenko return (EINVAL); 4014063f925SOleksandr Tymoshenko 40227e644a8SDimitry Andric /* Check for unwanted flags. */ 40327e644a8SDimitry Andric if ((flags & sc->sc_gpio_pins[i].gp_caps) != flags) 4044063f925SOleksandr Tymoshenko return (EINVAL); 4054063f925SOleksandr Tymoshenko 4064063f925SOleksandr Tymoshenko /* Can't mix input/output together. */ 4074063f925SOleksandr Tymoshenko if ((flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) == 4084063f925SOleksandr Tymoshenko (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) 4094063f925SOleksandr Tymoshenko return (EINVAL); 4104063f925SOleksandr Tymoshenko 4114063f925SOleksandr Tymoshenko /* Can't mix pull-up/pull-down together. */ 4124063f925SOleksandr Tymoshenko if ((flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) == 4134063f925SOleksandr Tymoshenko (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) 4144063f925SOleksandr Tymoshenko return (EINVAL); 4154063f925SOleksandr Tymoshenko 4164063f925SOleksandr Tymoshenko bcm_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); 4174063f925SOleksandr Tymoshenko 4184063f925SOleksandr Tymoshenko return (0); 4194063f925SOleksandr Tymoshenko } 4204063f925SOleksandr Tymoshenko 4214063f925SOleksandr Tymoshenko static int 4224063f925SOleksandr Tymoshenko bcm_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) 4234063f925SOleksandr Tymoshenko { 4244063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4254063f925SOleksandr Tymoshenko uint32_t bank, offset; 4264063f925SOleksandr Tymoshenko int i; 4274063f925SOleksandr Tymoshenko 4284063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4294063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4304063f925SOleksandr Tymoshenko break; 4314063f925SOleksandr Tymoshenko } 4324063f925SOleksandr Tymoshenko 4334063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4344063f925SOleksandr Tymoshenko return (EINVAL); 4354063f925SOleksandr Tymoshenko 4364063f925SOleksandr Tymoshenko /* We never write to read-only/reserved pins. */ 4374063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 4384063f925SOleksandr Tymoshenko return (EINVAL); 4394063f925SOleksandr Tymoshenko 4404063f925SOleksandr Tymoshenko bank = pin / 32; 4414063f925SOleksandr Tymoshenko offset = pin - 32 * bank; 4424063f925SOleksandr Tymoshenko 4434063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4444063f925SOleksandr Tymoshenko if (value) 4454063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPSET(bank), (1 << offset)); 4464063f925SOleksandr Tymoshenko else 4474063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPCLR(bank), (1 << offset)); 4484063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4494063f925SOleksandr Tymoshenko 4504063f925SOleksandr Tymoshenko return (0); 4514063f925SOleksandr Tymoshenko } 4524063f925SOleksandr Tymoshenko 4534063f925SOleksandr Tymoshenko static int 4544063f925SOleksandr Tymoshenko bcm_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) 4554063f925SOleksandr Tymoshenko { 4564063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4574063f925SOleksandr Tymoshenko uint32_t bank, offset, reg_data; 4584063f925SOleksandr Tymoshenko int i; 4594063f925SOleksandr Tymoshenko 4604063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4614063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4624063f925SOleksandr Tymoshenko break; 4634063f925SOleksandr Tymoshenko } 4644063f925SOleksandr Tymoshenko 4654063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4664063f925SOleksandr Tymoshenko return (EINVAL); 4674063f925SOleksandr Tymoshenko 4684063f925SOleksandr Tymoshenko bank = pin / 32; 4694063f925SOleksandr Tymoshenko offset = pin - 32 * bank; 4704063f925SOleksandr Tymoshenko 4714063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4724063f925SOleksandr Tymoshenko reg_data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); 4734063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4744063f925SOleksandr Tymoshenko *val = (reg_data & (1 << offset)) ? 1 : 0; 4754063f925SOleksandr Tymoshenko 4764063f925SOleksandr Tymoshenko return (0); 4774063f925SOleksandr Tymoshenko } 4784063f925SOleksandr Tymoshenko 4794063f925SOleksandr Tymoshenko static int 4804063f925SOleksandr Tymoshenko bcm_gpio_pin_toggle(device_t dev, uint32_t pin) 4814063f925SOleksandr Tymoshenko { 4824063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4834063f925SOleksandr Tymoshenko uint32_t bank, data, offset; 4844063f925SOleksandr Tymoshenko int i; 4854063f925SOleksandr Tymoshenko 4864063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4874063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4884063f925SOleksandr Tymoshenko break; 4894063f925SOleksandr Tymoshenko } 4904063f925SOleksandr Tymoshenko 4914063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4924063f925SOleksandr Tymoshenko return (EINVAL); 4934063f925SOleksandr Tymoshenko 4944063f925SOleksandr Tymoshenko /* We never write to read-only/reserved pins. */ 4954063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 4964063f925SOleksandr Tymoshenko return (EINVAL); 4974063f925SOleksandr Tymoshenko 4984063f925SOleksandr Tymoshenko bank = pin / 32; 4994063f925SOleksandr Tymoshenko offset = pin - 32 * bank; 5004063f925SOleksandr Tymoshenko 5014063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 5024063f925SOleksandr Tymoshenko data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); 5034063f925SOleksandr Tymoshenko if (data & (1 << offset)) 5044063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPCLR(bank), (1 << offset)); 5054063f925SOleksandr Tymoshenko else 5064063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPSET(bank), (1 << offset)); 5074063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 5084063f925SOleksandr Tymoshenko 5094063f925SOleksandr Tymoshenko return (0); 5104063f925SOleksandr Tymoshenko } 5114063f925SOleksandr Tymoshenko 5124063f925SOleksandr Tymoshenko static int 5134063f925SOleksandr Tymoshenko bcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc) 5144063f925SOleksandr Tymoshenko { 5154063f925SOleksandr Tymoshenko int i, len; 5164063f925SOleksandr Tymoshenko pcell_t pins[BCM_GPIO_PINS]; 5174063f925SOleksandr Tymoshenko phandle_t gpio; 5184063f925SOleksandr Tymoshenko 5194063f925SOleksandr Tymoshenko /* Find the gpio node to start. */ 5204063f925SOleksandr Tymoshenko gpio = ofw_bus_get_node(sc->sc_dev); 5214063f925SOleksandr Tymoshenko 5224063f925SOleksandr Tymoshenko len = OF_getproplen(gpio, "broadcom,read-only"); 5234063f925SOleksandr Tymoshenko if (len < 0 || len > sizeof(pins)) 5244063f925SOleksandr Tymoshenko return (-1); 5254063f925SOleksandr Tymoshenko 5264063f925SOleksandr Tymoshenko if (OF_getprop(gpio, "broadcom,read-only", &pins, len) < 0) 5274063f925SOleksandr Tymoshenko return (-1); 5284063f925SOleksandr Tymoshenko 5294063f925SOleksandr Tymoshenko sc->sc_ro_npins = len / sizeof(pcell_t); 5304063f925SOleksandr Tymoshenko 5314063f925SOleksandr Tymoshenko device_printf(sc->sc_dev, "read-only pins: "); 5324063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_ro_npins; i++) { 5334063f925SOleksandr Tymoshenko sc->sc_ro_pins[i] = fdt32_to_cpu(pins[i]); 5344063f925SOleksandr Tymoshenko if (i > 0) 5354063f925SOleksandr Tymoshenko printf(","); 5364063f925SOleksandr Tymoshenko printf("%d", sc->sc_ro_pins[i]); 5374063f925SOleksandr Tymoshenko } 5384063f925SOleksandr Tymoshenko if (i > 0) 5394063f925SOleksandr Tymoshenko printf("."); 5404063f925SOleksandr Tymoshenko printf("\n"); 5414063f925SOleksandr Tymoshenko 5424063f925SOleksandr Tymoshenko return (0); 5434063f925SOleksandr Tymoshenko } 5444063f925SOleksandr Tymoshenko 5454063f925SOleksandr Tymoshenko static int 54690576f54SOleksandr Tymoshenko bcm_gpio_func_proc(SYSCTL_HANDLER_ARGS) 54790576f54SOleksandr Tymoshenko { 54890576f54SOleksandr Tymoshenko char buf[16]; 54990576f54SOleksandr Tymoshenko struct bcm_gpio_softc *sc; 55090576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl *sc_sysctl; 55190576f54SOleksandr Tymoshenko uint32_t nfunc; 55244d06d8dSLuiz Otavio O Souza int error; 55390576f54SOleksandr Tymoshenko 55490576f54SOleksandr Tymoshenko sc_sysctl = arg1; 55590576f54SOleksandr Tymoshenko sc = sc_sysctl->sc; 55690576f54SOleksandr Tymoshenko 55790576f54SOleksandr Tymoshenko /* Get the current pin function. */ 55890576f54SOleksandr Tymoshenko nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin); 55990576f54SOleksandr Tymoshenko bcm_gpio_func_str(nfunc, buf, sizeof(buf)); 56090576f54SOleksandr Tymoshenko 56190576f54SOleksandr Tymoshenko error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 56290576f54SOleksandr Tymoshenko if (error != 0 || req->newptr == NULL) 56390576f54SOleksandr Tymoshenko return (error); 56490576f54SOleksandr Tymoshenko 56590576f54SOleksandr Tymoshenko /* Parse the user supplied string and check for a valid pin function. */ 56690576f54SOleksandr Tymoshenko if (bcm_gpio_str_func(buf, &nfunc) != 0) 56790576f54SOleksandr Tymoshenko return (EINVAL); 56890576f54SOleksandr Tymoshenko 56944d06d8dSLuiz Otavio O Souza /* Update the pin alternate function. */ 57044d06d8dSLuiz Otavio O Souza bcm_gpio_set_alternate(sc->sc_dev, sc_sysctl->pin, nfunc); 57190576f54SOleksandr Tymoshenko 57290576f54SOleksandr Tymoshenko return (0); 57390576f54SOleksandr Tymoshenko } 57490576f54SOleksandr Tymoshenko 57590576f54SOleksandr Tymoshenko static void 57690576f54SOleksandr Tymoshenko bcm_gpio_sysctl_init(struct bcm_gpio_softc *sc) 57790576f54SOleksandr Tymoshenko { 57890576f54SOleksandr Tymoshenko char pinbuf[3]; 57990576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl *sc_sysctl; 58090576f54SOleksandr Tymoshenko struct sysctl_ctx_list *ctx; 58190576f54SOleksandr Tymoshenko struct sysctl_oid *tree_node, *pin_node, *pinN_node; 58290576f54SOleksandr Tymoshenko struct sysctl_oid_list *tree, *pin_tree, *pinN_tree; 58390576f54SOleksandr Tymoshenko int i; 58490576f54SOleksandr Tymoshenko 58590576f54SOleksandr Tymoshenko /* 58690576f54SOleksandr Tymoshenko * Add per-pin sysctl tree/handlers. 58790576f54SOleksandr Tymoshenko */ 58890576f54SOleksandr Tymoshenko ctx = device_get_sysctl_ctx(sc->sc_dev); 58990576f54SOleksandr Tymoshenko tree_node = device_get_sysctl_tree(sc->sc_dev); 59090576f54SOleksandr Tymoshenko tree = SYSCTL_CHILDREN(tree_node); 59190576f54SOleksandr Tymoshenko pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin", 5921fbabb48SLuiz Otavio O Souza CTLFLAG_RD, NULL, "GPIO Pins"); 59390576f54SOleksandr Tymoshenko pin_tree = SYSCTL_CHILDREN(pin_node); 59490576f54SOleksandr Tymoshenko 59590576f54SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 59690576f54SOleksandr Tymoshenko 59790576f54SOleksandr Tymoshenko snprintf(pinbuf, sizeof(pinbuf), "%d", i); 59890576f54SOleksandr Tymoshenko pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf, 59990576f54SOleksandr Tymoshenko CTLFLAG_RD, NULL, "GPIO Pin"); 60090576f54SOleksandr Tymoshenko pinN_tree = SYSCTL_CHILDREN(pinN_node); 60190576f54SOleksandr Tymoshenko 60290576f54SOleksandr Tymoshenko sc->sc_sysctl[i].sc = sc; 60390576f54SOleksandr Tymoshenko sc_sysctl = &sc->sc_sysctl[i]; 60490576f54SOleksandr Tymoshenko sc_sysctl->sc = sc; 60590576f54SOleksandr Tymoshenko sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin; 60690576f54SOleksandr Tymoshenko SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function", 60790576f54SOleksandr Tymoshenko CTLFLAG_RW | CTLTYPE_STRING, sc_sysctl, 60890576f54SOleksandr Tymoshenko sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc, 60990576f54SOleksandr Tymoshenko "A", "Pin Function"); 61090576f54SOleksandr Tymoshenko } 61190576f54SOleksandr Tymoshenko } 61290576f54SOleksandr Tymoshenko 61390576f54SOleksandr Tymoshenko static int 6144063f925SOleksandr Tymoshenko bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc) 6154063f925SOleksandr Tymoshenko { 6164063f925SOleksandr Tymoshenko int i, j, len, npins; 6174063f925SOleksandr Tymoshenko pcell_t pins[BCM_GPIO_PINS]; 6184063f925SOleksandr Tymoshenko phandle_t gpio, node, reserved; 6194063f925SOleksandr Tymoshenko char name[32]; 6204063f925SOleksandr Tymoshenko 6214063f925SOleksandr Tymoshenko /* Get read-only pins. */ 6224063f925SOleksandr Tymoshenko if (bcm_gpio_get_ro_pins(sc) != 0) 6234063f925SOleksandr Tymoshenko return (-1); 6244063f925SOleksandr Tymoshenko 6254063f925SOleksandr Tymoshenko /* Find the gpio/reserved pins node to start. */ 6264063f925SOleksandr Tymoshenko gpio = ofw_bus_get_node(sc->sc_dev); 6274063f925SOleksandr Tymoshenko node = OF_child(gpio); 6284063f925SOleksandr Tymoshenko 6294063f925SOleksandr Tymoshenko /* 6304063f925SOleksandr Tymoshenko * Find reserved node 6314063f925SOleksandr Tymoshenko */ 6324063f925SOleksandr Tymoshenko reserved = 0; 6334063f925SOleksandr Tymoshenko while ((node != 0) && (reserved == 0)) { 6344063f925SOleksandr Tymoshenko len = OF_getprop(node, "name", name, 6354063f925SOleksandr Tymoshenko sizeof(name) - 1); 6364063f925SOleksandr Tymoshenko name[len] = 0; 6374063f925SOleksandr Tymoshenko if (strcmp(name, "reserved") == 0) 6384063f925SOleksandr Tymoshenko reserved = node; 6394063f925SOleksandr Tymoshenko node = OF_peer(node); 6404063f925SOleksandr Tymoshenko } 6414063f925SOleksandr Tymoshenko 6424063f925SOleksandr Tymoshenko if (reserved == 0) 6434063f925SOleksandr Tymoshenko return (-1); 6444063f925SOleksandr Tymoshenko 6454063f925SOleksandr Tymoshenko /* Get the reserved pins. */ 6464063f925SOleksandr Tymoshenko len = OF_getproplen(reserved, "broadcom,pins"); 6474063f925SOleksandr Tymoshenko if (len < 0 || len > sizeof(pins)) 6484063f925SOleksandr Tymoshenko return (-1); 6494063f925SOleksandr Tymoshenko 6504063f925SOleksandr Tymoshenko if (OF_getprop(reserved, "broadcom,pins", &pins, len) < 0) 6514063f925SOleksandr Tymoshenko return (-1); 6524063f925SOleksandr Tymoshenko 6534063f925SOleksandr Tymoshenko npins = len / sizeof(pcell_t); 6544063f925SOleksandr Tymoshenko 6554063f925SOleksandr Tymoshenko j = 0; 6564063f925SOleksandr Tymoshenko device_printf(sc->sc_dev, "reserved pins: "); 6574063f925SOleksandr Tymoshenko for (i = 0; i < npins; i++) { 6584063f925SOleksandr Tymoshenko if (i > 0) 6594063f925SOleksandr Tymoshenko printf(","); 6604063f925SOleksandr Tymoshenko printf("%d", fdt32_to_cpu(pins[i])); 6614063f925SOleksandr Tymoshenko /* Some pins maybe already on the list of read-only pins. */ 6624063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, fdt32_to_cpu(pins[i]))) 6634063f925SOleksandr Tymoshenko continue; 6644063f925SOleksandr Tymoshenko sc->sc_ro_pins[j++ + sc->sc_ro_npins] = fdt32_to_cpu(pins[i]); 6654063f925SOleksandr Tymoshenko } 6664063f925SOleksandr Tymoshenko sc->sc_ro_npins += j; 6674063f925SOleksandr Tymoshenko if (i > 0) 6684063f925SOleksandr Tymoshenko printf("."); 6694063f925SOleksandr Tymoshenko printf("\n"); 6704063f925SOleksandr Tymoshenko 6714063f925SOleksandr Tymoshenko return (0); 6724063f925SOleksandr Tymoshenko } 6734063f925SOleksandr Tymoshenko 6744063f925SOleksandr Tymoshenko static int 6754063f925SOleksandr Tymoshenko bcm_gpio_probe(device_t dev) 6764063f925SOleksandr Tymoshenko { 677*add35ed5SIan Lepore 678*add35ed5SIan Lepore if (!ofw_bus_status_okay(dev)) 679*add35ed5SIan Lepore return (ENXIO); 680*add35ed5SIan Lepore 6814063f925SOleksandr Tymoshenko if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-gpio")) 6824063f925SOleksandr Tymoshenko return (ENXIO); 6834063f925SOleksandr Tymoshenko 6844063f925SOleksandr Tymoshenko device_set_desc(dev, "BCM2708/2835 GPIO controller"); 6854063f925SOleksandr Tymoshenko return (BUS_PROBE_DEFAULT); 6864063f925SOleksandr Tymoshenko } 6874063f925SOleksandr Tymoshenko 6884063f925SOleksandr Tymoshenko static int 6894063f925SOleksandr Tymoshenko bcm_gpio_attach(device_t dev) 6904063f925SOleksandr Tymoshenko { 6914063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 69290576f54SOleksandr Tymoshenko uint32_t func; 6934063f925SOleksandr Tymoshenko int i, j, rid; 6944063f925SOleksandr Tymoshenko phandle_t gpio; 6954063f925SOleksandr Tymoshenko 6964063f925SOleksandr Tymoshenko sc->sc_dev = dev; 6974063f925SOleksandr Tymoshenko 6984063f925SOleksandr Tymoshenko mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_DEF); 6994063f925SOleksandr Tymoshenko 7004063f925SOleksandr Tymoshenko rid = 0; 7014063f925SOleksandr Tymoshenko sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 7024063f925SOleksandr Tymoshenko RF_ACTIVE); 7034063f925SOleksandr Tymoshenko if (!sc->sc_mem_res) { 7044063f925SOleksandr Tymoshenko device_printf(dev, "cannot allocate memory window\n"); 7054063f925SOleksandr Tymoshenko return (ENXIO); 7064063f925SOleksandr Tymoshenko } 7074063f925SOleksandr Tymoshenko 7084063f925SOleksandr Tymoshenko sc->sc_bst = rman_get_bustag(sc->sc_mem_res); 7094063f925SOleksandr Tymoshenko sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); 7104063f925SOleksandr Tymoshenko 7114063f925SOleksandr Tymoshenko rid = 0; 7124063f925SOleksandr Tymoshenko sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, 7134063f925SOleksandr Tymoshenko RF_ACTIVE); 7144063f925SOleksandr Tymoshenko if (!sc->sc_irq_res) { 7154063f925SOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 7164063f925SOleksandr Tymoshenko device_printf(dev, "cannot allocate interrupt\n"); 7174063f925SOleksandr Tymoshenko return (ENXIO); 7184063f925SOleksandr Tymoshenko } 7194063f925SOleksandr Tymoshenko 7204063f925SOleksandr Tymoshenko /* Find our node. */ 7214063f925SOleksandr Tymoshenko gpio = ofw_bus_get_node(sc->sc_dev); 7224063f925SOleksandr Tymoshenko 7234063f925SOleksandr Tymoshenko if (!OF_hasprop(gpio, "gpio-controller")) 7244063f925SOleksandr Tymoshenko /* Node is not a GPIO controller. */ 7254063f925SOleksandr Tymoshenko goto fail; 7264063f925SOleksandr Tymoshenko 7274063f925SOleksandr Tymoshenko /* 7284063f925SOleksandr Tymoshenko * Find the read-only pins. These are pins we never touch or bad 7294063f925SOleksandr Tymoshenko * things could happen. 7304063f925SOleksandr Tymoshenko */ 7314063f925SOleksandr Tymoshenko if (bcm_gpio_get_reserved_pins(sc) == -1) 7324063f925SOleksandr Tymoshenko goto fail; 7334063f925SOleksandr Tymoshenko 7344063f925SOleksandr Tymoshenko /* Initialize the software controlled pins. */ 7358d900240SLuiz Otavio O Souza for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) { 7364063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, j)) 7374063f925SOleksandr Tymoshenko continue; 7384063f925SOleksandr Tymoshenko snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, 7394063f925SOleksandr Tymoshenko "pin %d", j); 74090576f54SOleksandr Tymoshenko func = bcm_gpio_get_function(sc, j); 7414063f925SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_pin = j; 7424063f925SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS; 74390576f54SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func); 7444063f925SOleksandr Tymoshenko i++; 7454063f925SOleksandr Tymoshenko } 7464063f925SOleksandr Tymoshenko sc->sc_gpio_npins = i; 7474063f925SOleksandr Tymoshenko 74890576f54SOleksandr Tymoshenko bcm_gpio_sysctl_init(sc); 74990576f54SOleksandr Tymoshenko 7504063f925SOleksandr Tymoshenko device_add_child(dev, "gpioc", device_get_unit(dev)); 7514063f925SOleksandr Tymoshenko device_add_child(dev, "gpiobus", device_get_unit(dev)); 7524063f925SOleksandr Tymoshenko return (bus_generic_attach(dev)); 7534063f925SOleksandr Tymoshenko 7544063f925SOleksandr Tymoshenko fail: 7554063f925SOleksandr Tymoshenko if (sc->sc_irq_res) 7564063f925SOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); 7574063f925SOleksandr Tymoshenko if (sc->sc_mem_res) 7584063f925SOleksandr Tymoshenko bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 7594063f925SOleksandr Tymoshenko return (ENXIO); 7604063f925SOleksandr Tymoshenko } 7614063f925SOleksandr Tymoshenko 7624063f925SOleksandr Tymoshenko static int 7634063f925SOleksandr Tymoshenko bcm_gpio_detach(device_t dev) 7644063f925SOleksandr Tymoshenko { 7654063f925SOleksandr Tymoshenko 7664063f925SOleksandr Tymoshenko return (EBUSY); 7674063f925SOleksandr Tymoshenko } 7684063f925SOleksandr Tymoshenko 7694063f925SOleksandr Tymoshenko static device_method_t bcm_gpio_methods[] = { 7704063f925SOleksandr Tymoshenko /* Device interface */ 7714063f925SOleksandr Tymoshenko DEVMETHOD(device_probe, bcm_gpio_probe), 7724063f925SOleksandr Tymoshenko DEVMETHOD(device_attach, bcm_gpio_attach), 7734063f925SOleksandr Tymoshenko DEVMETHOD(device_detach, bcm_gpio_detach), 7744063f925SOleksandr Tymoshenko 7754063f925SOleksandr Tymoshenko /* GPIO protocol */ 7764063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_max, bcm_gpio_pin_max), 7774063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getname, bcm_gpio_pin_getname), 7784063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getflags, bcm_gpio_pin_getflags), 7794063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getcaps, bcm_gpio_pin_getcaps), 7804063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_setflags, bcm_gpio_pin_setflags), 7814063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_get, bcm_gpio_pin_get), 7824063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set), 7834063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle), 7844063f925SOleksandr Tymoshenko 7854063f925SOleksandr Tymoshenko DEVMETHOD_END 7864063f925SOleksandr Tymoshenko }; 7874063f925SOleksandr Tymoshenko 7884063f925SOleksandr Tymoshenko static devclass_t bcm_gpio_devclass; 7894063f925SOleksandr Tymoshenko 7904063f925SOleksandr Tymoshenko static driver_t bcm_gpio_driver = { 7914063f925SOleksandr Tymoshenko "gpio", 7924063f925SOleksandr Tymoshenko bcm_gpio_methods, 7934063f925SOleksandr Tymoshenko sizeof(struct bcm_gpio_softc), 7944063f925SOleksandr Tymoshenko }; 7954063f925SOleksandr Tymoshenko 7964063f925SOleksandr Tymoshenko DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0); 797