14063f925SOleksandr Tymoshenko /*- 2af3dc4a7SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3af3dc4a7SPedro F. Giffuni * 45508fc88SLuiz Otavio O Souza * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@FreeBSD.org> 55508fc88SLuiz Otavio O Souza * Copyright (c) 2012-2015 Luiz Otavio O Souza <loos@FreeBSD.org> 64063f925SOleksandr Tymoshenko * All rights reserved. 74063f925SOleksandr Tymoshenko * 84063f925SOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without 94063f925SOleksandr Tymoshenko * modification, are permitted provided that the following conditions 104063f925SOleksandr Tymoshenko * are met: 114063f925SOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright 124063f925SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer. 134063f925SOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright 144063f925SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the 154063f925SOleksandr Tymoshenko * documentation and/or other materials provided with the distribution. 164063f925SOleksandr Tymoshenko * 174063f925SOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 184063f925SOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 194063f925SOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 204063f925SOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 214063f925SOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 224063f925SOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 234063f925SOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 244063f925SOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 254063f925SOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 264063f925SOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 274063f925SOleksandr Tymoshenko * SUCH DAMAGE. 284063f925SOleksandr Tymoshenko * 294063f925SOleksandr Tymoshenko */ 304063f925SOleksandr Tymoshenko #include <sys/cdefs.h> 314063f925SOleksandr Tymoshenko __FBSDID("$FreeBSD$"); 324063f925SOleksandr Tymoshenko 3389de2fb6SSvatopluk Kraus #include "opt_platform.h" 3489de2fb6SSvatopluk Kraus 354063f925SOleksandr Tymoshenko #include <sys/param.h> 364063f925SOleksandr Tymoshenko #include <sys/systm.h> 374063f925SOleksandr Tymoshenko #include <sys/bus.h> 384063f925SOleksandr Tymoshenko #include <sys/gpio.h> 3912471cecSLuiz Otavio O Souza #include <sys/interrupt.h> 40e74d6e2aSLuiz Otavio O Souza #include <sys/kernel.h> 41e74d6e2aSLuiz Otavio O Souza #include <sys/lock.h> 42e74d6e2aSLuiz Otavio O Souza #include <sys/module.h> 43e74d6e2aSLuiz Otavio O Souza #include <sys/mutex.h> 4489de2fb6SSvatopluk Kraus #include <sys/proc.h> 45e74d6e2aSLuiz Otavio O Souza #include <sys/rman.h> 4690576f54SOleksandr Tymoshenko #include <sys/sysctl.h> 474063f925SOleksandr Tymoshenko 484063f925SOleksandr Tymoshenko #include <machine/bus.h> 4989de2fb6SSvatopluk Kraus #include <machine/intr.h> 504063f925SOleksandr Tymoshenko 5191cc58afSOleksandr Tymoshenko #include <dev/fdt/fdt_pinctrl.h> 527836352bSLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h> 534063f925SOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h> 544063f925SOleksandr Tymoshenko 554063f925SOleksandr Tymoshenko #include "gpio_if.h" 564063f925SOleksandr Tymoshenko 5789de2fb6SSvatopluk Kraus #include "pic_if.h" 5889de2fb6SSvatopluk Kraus 594063f925SOleksandr Tymoshenko #ifdef DEBUG 604063f925SOleksandr Tymoshenko #define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ 614063f925SOleksandr Tymoshenko printf(fmt,##args); } while (0) 624063f925SOleksandr Tymoshenko #else 634063f925SOleksandr Tymoshenko #define dprintf(fmt, args...) 644063f925SOleksandr Tymoshenko #endif 654063f925SOleksandr Tymoshenko 66c2136589SLuiz Otavio O Souza #define BCM_GPIO_IRQS 4 674063f925SOleksandr Tymoshenko #define BCM_GPIO_PINS 54 6812471cecSLuiz Otavio O Souza #define BCM_GPIO_PINS_PER_BANK 32 69f7f2b2fbSSvatopluk Kraus 70f7f2b2fbSSvatopluk Kraus #define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ 71f7f2b2fbSSvatopluk Kraus GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_INTR_LEVEL_LOW | \ 72f7f2b2fbSSvatopluk Kraus GPIO_INTR_LEVEL_HIGH | GPIO_INTR_EDGE_RISING | \ 73f7f2b2fbSSvatopluk Kraus GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH) 744063f925SOleksandr Tymoshenko 7591cc58afSOleksandr Tymoshenko #define BCM2835_FSEL_GPIO_IN 0 7691cc58afSOleksandr Tymoshenko #define BCM2835_FSEL_GPIO_OUT 1 7791cc58afSOleksandr Tymoshenko #define BCM2835_FSEL_ALT5 2 7891cc58afSOleksandr Tymoshenko #define BCM2835_FSEL_ALT4 3 7991cc58afSOleksandr Tymoshenko #define BCM2835_FSEL_ALT0 4 8091cc58afSOleksandr Tymoshenko #define BCM2835_FSEL_ALT1 5 8191cc58afSOleksandr Tymoshenko #define BCM2835_FSEL_ALT2 6 8291cc58afSOleksandr Tymoshenko #define BCM2835_FSEL_ALT3 7 8391cc58afSOleksandr Tymoshenko 8491cc58afSOleksandr Tymoshenko #define BCM2835_PUD_OFF 0 8591cc58afSOleksandr Tymoshenko #define BCM2835_PUD_DOWN 1 8691cc58afSOleksandr Tymoshenko #define BCM2835_PUD_UP 2 8791cc58afSOleksandr Tymoshenko 88c2136589SLuiz Otavio O Souza static struct resource_spec bcm_gpio_res_spec[] = { 89c2136589SLuiz Otavio O Souza { SYS_RES_MEMORY, 0, RF_ACTIVE }, 9089de2fb6SSvatopluk Kraus { SYS_RES_IRQ, 0, RF_ACTIVE }, /* bank 0 interrupt */ 9189de2fb6SSvatopluk Kraus { SYS_RES_IRQ, 1, RF_ACTIVE }, /* bank 1 interrupt */ 92c2136589SLuiz Otavio O Souza { -1, 0, 0 } 93c2136589SLuiz Otavio O Souza }; 94c2136589SLuiz Otavio O Souza 9590576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl { 9690576f54SOleksandr Tymoshenko struct bcm_gpio_softc *sc; 9790576f54SOleksandr Tymoshenko uint32_t pin; 9890576f54SOleksandr Tymoshenko }; 9990576f54SOleksandr Tymoshenko 10089de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc { 10189de2fb6SSvatopluk Kraus struct intr_irqsrc bgi_isrc; 10289de2fb6SSvatopluk Kraus uint32_t bgi_irq; 103c28b681cSSvatopluk Kraus uint32_t bgi_mode; 10489de2fb6SSvatopluk Kraus uint32_t bgi_mask; 10589de2fb6SSvatopluk Kraus }; 10689de2fb6SSvatopluk Kraus 1074063f925SOleksandr Tymoshenko struct bcm_gpio_softc { 1084063f925SOleksandr Tymoshenko device_t sc_dev; 1097836352bSLuiz Otavio O Souza device_t sc_busdev; 1104063f925SOleksandr Tymoshenko struct mtx sc_mtx; 111c2136589SLuiz Otavio O Souza struct resource * sc_res[BCM_GPIO_IRQS + 1]; 1124063f925SOleksandr Tymoshenko bus_space_tag_t sc_bst; 1134063f925SOleksandr Tymoshenko bus_space_handle_t sc_bsh; 11412471cecSLuiz Otavio O Souza void * sc_intrhand[BCM_GPIO_IRQS]; 1154063f925SOleksandr Tymoshenko int sc_gpio_npins; 1164063f925SOleksandr Tymoshenko int sc_ro_npins; 1174063f925SOleksandr Tymoshenko int sc_ro_pins[BCM_GPIO_PINS]; 1184063f925SOleksandr Tymoshenko struct gpio_pin sc_gpio_pins[BCM_GPIO_PINS]; 11990576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl sc_sysctl[BCM_GPIO_PINS]; 12089de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc sc_isrcs[BCM_GPIO_PINS]; 1214063f925SOleksandr Tymoshenko }; 1224063f925SOleksandr Tymoshenko 1234063f925SOleksandr Tymoshenko enum bcm_gpio_pud { 1244063f925SOleksandr Tymoshenko BCM_GPIO_NONE, 1254063f925SOleksandr Tymoshenko BCM_GPIO_PULLDOWN, 1264063f925SOleksandr Tymoshenko BCM_GPIO_PULLUP, 1274063f925SOleksandr Tymoshenko }; 1284063f925SOleksandr Tymoshenko 12912471cecSLuiz Otavio O Souza #define BCM_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) 13012471cecSLuiz Otavio O Souza #define BCM_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) 13112471cecSLuiz Otavio O Souza #define BCM_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) 1324063f925SOleksandr Tymoshenko #define BCM_GPIO_WRITE(_sc, _off, _val) \ 13312471cecSLuiz Otavio O Souza bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val) 1344063f925SOleksandr Tymoshenko #define BCM_GPIO_READ(_sc, _off) \ 13512471cecSLuiz Otavio O Souza bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off) 13612471cecSLuiz Otavio O Souza #define BCM_GPIO_CLEAR_BITS(_sc, _off, _bits) \ 13712471cecSLuiz Otavio O Souza BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) & ~(_bits)) 13812471cecSLuiz Otavio O Souza #define BCM_GPIO_SET_BITS(_sc, _off, _bits) \ 13912471cecSLuiz Otavio O Souza BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) | _bits) 14012471cecSLuiz Otavio O Souza #define BCM_GPIO_BANK(a) (a / BCM_GPIO_PINS_PER_BANK) 14112471cecSLuiz Otavio O Souza #define BCM_GPIO_MASK(a) (1U << (a % BCM_GPIO_PINS_PER_BANK)) 14212471cecSLuiz Otavio O Souza 14312471cecSLuiz Otavio O Souza #define BCM_GPIO_GPFSEL(_bank) (0x00 + _bank * 4) /* Function Select */ 14412471cecSLuiz Otavio O Souza #define BCM_GPIO_GPSET(_bank) (0x1c + _bank * 4) /* Pin Out Set */ 14512471cecSLuiz Otavio O Souza #define BCM_GPIO_GPCLR(_bank) (0x28 + _bank * 4) /* Pin Out Clear */ 14612471cecSLuiz Otavio O Souza #define BCM_GPIO_GPLEV(_bank) (0x34 + _bank * 4) /* Pin Level */ 14712471cecSLuiz Otavio O Souza #define BCM_GPIO_GPEDS(_bank) (0x40 + _bank * 4) /* Event Status */ 14812471cecSLuiz Otavio O Souza #define BCM_GPIO_GPREN(_bank) (0x4c + _bank * 4) /* Rising Edge irq */ 14912471cecSLuiz Otavio O Souza #define BCM_GPIO_GPFEN(_bank) (0x58 + _bank * 4) /* Falling Edge irq */ 15012471cecSLuiz Otavio O Souza #define BCM_GPIO_GPHEN(_bank) (0x64 + _bank * 4) /* High Level irq */ 15112471cecSLuiz Otavio O Souza #define BCM_GPIO_GPLEN(_bank) (0x70 + _bank * 4) /* Low Level irq */ 15212471cecSLuiz Otavio O Souza #define BCM_GPIO_GPAREN(_bank) (0x7c + _bank * 4) /* Async Rising Edge */ 15312471cecSLuiz Otavio O Souza #define BCM_GPIO_GPAFEN(_bank) (0x88 + _bank * 4) /* Async Falling Egde */ 15412471cecSLuiz Otavio O Souza #define BCM_GPIO_GPPUD(_bank) (0x94) /* Pin Pull up/down */ 15512471cecSLuiz Otavio O Souza #define BCM_GPIO_GPPUDCLK(_bank) (0x98 + _bank * 4) /* Pin Pull up clock */ 15612471cecSLuiz Otavio O Souza 157aa2959baSOleksandr Tymoshenko static struct ofw_compat_data compat_data[] = { 158aa2959baSOleksandr Tymoshenko {"broadcom,bcm2835-gpio", 1}, 159aa2959baSOleksandr Tymoshenko {"brcm,bcm2835-gpio", 1}, 160aa2959baSOleksandr Tymoshenko {NULL, 0} 161aa2959baSOleksandr Tymoshenko }; 162aa2959baSOleksandr Tymoshenko 16312471cecSLuiz Otavio O Souza static struct bcm_gpio_softc *bcm_gpio_sc = NULL; 1644063f925SOleksandr Tymoshenko 16589de2fb6SSvatopluk Kraus static int bcm_gpio_intr_bank0(void *arg); 16689de2fb6SSvatopluk Kraus static int bcm_gpio_intr_bank1(void *arg); 16789de2fb6SSvatopluk Kraus static int bcm_gpio_pic_attach(struct bcm_gpio_softc *sc); 16889de2fb6SSvatopluk Kraus static int bcm_gpio_pic_detach(struct bcm_gpio_softc *sc); 16989de2fb6SSvatopluk Kraus 1704063f925SOleksandr Tymoshenko static int 1714063f925SOleksandr Tymoshenko bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin) 1724063f925SOleksandr Tymoshenko { 1734063f925SOleksandr Tymoshenko int i; 1744063f925SOleksandr Tymoshenko 1754063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_ro_npins; i++) 1764063f925SOleksandr Tymoshenko if (pin == sc->sc_ro_pins[i]) 1774063f925SOleksandr Tymoshenko return (1); 1784063f925SOleksandr Tymoshenko return (0); 1794063f925SOleksandr Tymoshenko } 1804063f925SOleksandr Tymoshenko 1814063f925SOleksandr Tymoshenko static uint32_t 1824063f925SOleksandr Tymoshenko bcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin) 1834063f925SOleksandr Tymoshenko { 18490576f54SOleksandr Tymoshenko uint32_t bank, func, offset; 1854063f925SOleksandr Tymoshenko 1864063f925SOleksandr Tymoshenko /* Five banks, 10 pins per bank, 3 bits per pin. */ 1874063f925SOleksandr Tymoshenko bank = pin / 10; 1884063f925SOleksandr Tymoshenko offset = (pin - bank * 10) * 3; 1894063f925SOleksandr Tymoshenko 1904063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 19190576f54SOleksandr Tymoshenko func = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7; 1924063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 1934063f925SOleksandr Tymoshenko 19490576f54SOleksandr Tymoshenko return (func); 19590576f54SOleksandr Tymoshenko } 19690576f54SOleksandr Tymoshenko 19790576f54SOleksandr Tymoshenko static void 19890576f54SOleksandr Tymoshenko bcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize) 19990576f54SOleksandr Tymoshenko { 20090576f54SOleksandr Tymoshenko 20190576f54SOleksandr Tymoshenko switch (nfunc) { 20291cc58afSOleksandr Tymoshenko case BCM2835_FSEL_GPIO_IN: 20390576f54SOleksandr Tymoshenko strncpy(buf, "input", bufsize); 2044063f925SOleksandr Tymoshenko break; 20591cc58afSOleksandr Tymoshenko case BCM2835_FSEL_GPIO_OUT: 20690576f54SOleksandr Tymoshenko strncpy(buf, "output", bufsize); 2074063f925SOleksandr Tymoshenko break; 20891cc58afSOleksandr Tymoshenko case BCM2835_FSEL_ALT0: 20990576f54SOleksandr Tymoshenko strncpy(buf, "alt0", bufsize); 2104063f925SOleksandr Tymoshenko break; 21191cc58afSOleksandr Tymoshenko case BCM2835_FSEL_ALT1: 21290576f54SOleksandr Tymoshenko strncpy(buf, "alt1", bufsize); 2134063f925SOleksandr Tymoshenko break; 21491cc58afSOleksandr Tymoshenko case BCM2835_FSEL_ALT2: 21590576f54SOleksandr Tymoshenko strncpy(buf, "alt2", bufsize); 2164063f925SOleksandr Tymoshenko break; 21791cc58afSOleksandr Tymoshenko case BCM2835_FSEL_ALT3: 21890576f54SOleksandr Tymoshenko strncpy(buf, "alt3", bufsize); 2194063f925SOleksandr Tymoshenko break; 22091cc58afSOleksandr Tymoshenko case BCM2835_FSEL_ALT4: 22190576f54SOleksandr Tymoshenko strncpy(buf, "alt4", bufsize); 2224063f925SOleksandr Tymoshenko break; 22391cc58afSOleksandr Tymoshenko case BCM2835_FSEL_ALT5: 22490576f54SOleksandr Tymoshenko strncpy(buf, "alt5", bufsize); 2254063f925SOleksandr Tymoshenko break; 22690576f54SOleksandr Tymoshenko default: 22790576f54SOleksandr Tymoshenko strncpy(buf, "invalid", bufsize); 2284063f925SOleksandr Tymoshenko } 22990576f54SOleksandr Tymoshenko } 2304063f925SOleksandr Tymoshenko 23190576f54SOleksandr Tymoshenko static int 23290576f54SOleksandr Tymoshenko bcm_gpio_str_func(char *func, uint32_t *nfunc) 23390576f54SOleksandr Tymoshenko { 23490576f54SOleksandr Tymoshenko 23590576f54SOleksandr Tymoshenko if (strcasecmp(func, "input") == 0) 23691cc58afSOleksandr Tymoshenko *nfunc = BCM2835_FSEL_GPIO_IN; 23790576f54SOleksandr Tymoshenko else if (strcasecmp(func, "output") == 0) 23891cc58afSOleksandr Tymoshenko *nfunc = BCM2835_FSEL_GPIO_OUT; 23990576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt0") == 0) 24091cc58afSOleksandr Tymoshenko *nfunc = BCM2835_FSEL_ALT0; 24190576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt1") == 0) 24291cc58afSOleksandr Tymoshenko *nfunc = BCM2835_FSEL_ALT1; 24390576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt2") == 0) 24491cc58afSOleksandr Tymoshenko *nfunc = BCM2835_FSEL_ALT2; 24590576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt3") == 0) 24691cc58afSOleksandr Tymoshenko *nfunc = BCM2835_FSEL_ALT3; 24790576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt4") == 0) 24891cc58afSOleksandr Tymoshenko *nfunc = BCM2835_FSEL_ALT4; 24990576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt5") == 0) 25091cc58afSOleksandr Tymoshenko *nfunc = BCM2835_FSEL_ALT5; 25190576f54SOleksandr Tymoshenko else 25290576f54SOleksandr Tymoshenko return (-1); 25390576f54SOleksandr Tymoshenko 25490576f54SOleksandr Tymoshenko return (0); 25590576f54SOleksandr Tymoshenko } 25690576f54SOleksandr Tymoshenko 25790576f54SOleksandr Tymoshenko static uint32_t 25890576f54SOleksandr Tymoshenko bcm_gpio_func_flag(uint32_t nfunc) 25990576f54SOleksandr Tymoshenko { 26090576f54SOleksandr Tymoshenko 26190576f54SOleksandr Tymoshenko switch (nfunc) { 26291cc58afSOleksandr Tymoshenko case BCM2835_FSEL_GPIO_IN: 2634063f925SOleksandr Tymoshenko return (GPIO_PIN_INPUT); 26491cc58afSOleksandr Tymoshenko case BCM2835_FSEL_GPIO_OUT: 2654063f925SOleksandr Tymoshenko return (GPIO_PIN_OUTPUT); 2664063f925SOleksandr Tymoshenko } 2674063f925SOleksandr Tymoshenko return (0); 2684063f925SOleksandr Tymoshenko } 2694063f925SOleksandr Tymoshenko 2704063f925SOleksandr Tymoshenko static void 2714063f925SOleksandr Tymoshenko bcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f) 2724063f925SOleksandr Tymoshenko { 2734063f925SOleksandr Tymoshenko uint32_t bank, data, offset; 2744063f925SOleksandr Tymoshenko 27590576f54SOleksandr Tymoshenko /* Must be called with lock held. */ 27690576f54SOleksandr Tymoshenko BCM_GPIO_LOCK_ASSERT(sc); 27790576f54SOleksandr Tymoshenko 2784063f925SOleksandr Tymoshenko /* Five banks, 10 pins per bank, 3 bits per pin. */ 2794063f925SOleksandr Tymoshenko bank = pin / 10; 2804063f925SOleksandr Tymoshenko offset = (pin - bank * 10) * 3; 2814063f925SOleksandr Tymoshenko 2824063f925SOleksandr Tymoshenko data = BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)); 2834063f925SOleksandr Tymoshenko data &= ~(7 << offset); 2844063f925SOleksandr Tymoshenko data |= (f << offset); 2854063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPFSEL(bank), data); 2864063f925SOleksandr Tymoshenko } 2874063f925SOleksandr Tymoshenko 2884063f925SOleksandr Tymoshenko static void 2894063f925SOleksandr Tymoshenko bcm_gpio_set_pud(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t state) 2904063f925SOleksandr Tymoshenko { 2915508fc88SLuiz Otavio O Souza uint32_t bank; 2924063f925SOleksandr Tymoshenko 29390576f54SOleksandr Tymoshenko /* Must be called with lock held. */ 29490576f54SOleksandr Tymoshenko BCM_GPIO_LOCK_ASSERT(sc); 29590576f54SOleksandr Tymoshenko 2965508fc88SLuiz Otavio O Souza bank = BCM_GPIO_BANK(pin); 2974063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state); 2985508fc88SLuiz Otavio O Souza BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), BCM_GPIO_MASK(pin)); 2994063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0); 3004063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0); 3014063f925SOleksandr Tymoshenko } 3024063f925SOleksandr Tymoshenko 30391cc58afSOleksandr Tymoshenko static void 30444d06d8dSLuiz Otavio O Souza bcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc) 30544d06d8dSLuiz Otavio O Souza { 30644d06d8dSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 30744d06d8dSLuiz Otavio O Souza int i; 30844d06d8dSLuiz Otavio O Souza 30944d06d8dSLuiz Otavio O Souza sc = device_get_softc(dev); 31044d06d8dSLuiz Otavio O Souza BCM_GPIO_LOCK(sc); 31144d06d8dSLuiz Otavio O Souza 31291cc58afSOleksandr Tymoshenko /* Set the pin function. */ 31344d06d8dSLuiz Otavio O Souza bcm_gpio_set_function(sc, pin, nfunc); 31444d06d8dSLuiz Otavio O Souza 31544d06d8dSLuiz Otavio O Souza /* Update the pin flags. */ 31644d06d8dSLuiz Otavio O Souza for (i = 0; i < sc->sc_gpio_npins; i++) { 31744d06d8dSLuiz Otavio O Souza if (sc->sc_gpio_pins[i].gp_pin == pin) 31844d06d8dSLuiz Otavio O Souza break; 31944d06d8dSLuiz Otavio O Souza } 32044d06d8dSLuiz Otavio O Souza if (i < sc->sc_gpio_npins) 32144d06d8dSLuiz Otavio O Souza sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc); 32244d06d8dSLuiz Otavio O Souza 32344d06d8dSLuiz Otavio O Souza BCM_GPIO_UNLOCK(sc); 32444d06d8dSLuiz Otavio O Souza } 32544d06d8dSLuiz Otavio O Souza 3264063f925SOleksandr Tymoshenko static void 3274063f925SOleksandr Tymoshenko bcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin, 3284063f925SOleksandr Tymoshenko unsigned int flags) 3294063f925SOleksandr Tymoshenko { 3304063f925SOleksandr Tymoshenko 33190576f54SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 33290576f54SOleksandr Tymoshenko 3334063f925SOleksandr Tymoshenko /* 3344063f925SOleksandr Tymoshenko * Manage input/output. 3354063f925SOleksandr Tymoshenko */ 3364063f925SOleksandr Tymoshenko if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { 3374063f925SOleksandr Tymoshenko pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); 3384063f925SOleksandr Tymoshenko if (flags & GPIO_PIN_OUTPUT) { 3394063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_OUTPUT; 3404063f925SOleksandr Tymoshenko bcm_gpio_set_function(sc, pin->gp_pin, 34191cc58afSOleksandr Tymoshenko BCM2835_FSEL_GPIO_OUT); 3424063f925SOleksandr Tymoshenko } else { 3434063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_INPUT; 3444063f925SOleksandr Tymoshenko bcm_gpio_set_function(sc, pin->gp_pin, 34591cc58afSOleksandr Tymoshenko BCM2835_FSEL_GPIO_IN); 3464063f925SOleksandr Tymoshenko } 3474063f925SOleksandr Tymoshenko } 3484063f925SOleksandr Tymoshenko 3494063f925SOleksandr Tymoshenko /* Manage Pull-up/pull-down. */ 3504063f925SOleksandr Tymoshenko pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN); 3514063f925SOleksandr Tymoshenko if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) { 3524063f925SOleksandr Tymoshenko if (flags & GPIO_PIN_PULLUP) { 3534063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_PULLUP; 3544063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLUP); 3554063f925SOleksandr Tymoshenko } else { 3564063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_PULLDOWN; 3574063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLDOWN); 3584063f925SOleksandr Tymoshenko } 3594063f925SOleksandr Tymoshenko } else 3604063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE); 36190576f54SOleksandr Tymoshenko 36290576f54SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 3634063f925SOleksandr Tymoshenko } 3644063f925SOleksandr Tymoshenko 3657836352bSLuiz Otavio O Souza static device_t 3667836352bSLuiz Otavio O Souza bcm_gpio_get_bus(device_t dev) 3677836352bSLuiz Otavio O Souza { 3687836352bSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 3697836352bSLuiz Otavio O Souza 3707836352bSLuiz Otavio O Souza sc = device_get_softc(dev); 3717836352bSLuiz Otavio O Souza 3727836352bSLuiz Otavio O Souza return (sc->sc_busdev); 3737836352bSLuiz Otavio O Souza } 3747836352bSLuiz Otavio O Souza 3754063f925SOleksandr Tymoshenko static int 3764063f925SOleksandr Tymoshenko bcm_gpio_pin_max(device_t dev, int *maxpin) 3774063f925SOleksandr Tymoshenko { 3784063f925SOleksandr Tymoshenko 3794063f925SOleksandr Tymoshenko *maxpin = BCM_GPIO_PINS - 1; 3804063f925SOleksandr Tymoshenko return (0); 3814063f925SOleksandr Tymoshenko } 3824063f925SOleksandr Tymoshenko 3834063f925SOleksandr Tymoshenko static int 3844063f925SOleksandr Tymoshenko bcm_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 3854063f925SOleksandr Tymoshenko { 3864063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 3874063f925SOleksandr Tymoshenko int i; 3884063f925SOleksandr Tymoshenko 3894063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 3904063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 3914063f925SOleksandr Tymoshenko break; 3924063f925SOleksandr Tymoshenko } 3934063f925SOleksandr Tymoshenko 3944063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 3954063f925SOleksandr Tymoshenko return (EINVAL); 3964063f925SOleksandr Tymoshenko 3974063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 3984063f925SOleksandr Tymoshenko *caps = sc->sc_gpio_pins[i].gp_caps; 3994063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4004063f925SOleksandr Tymoshenko 4014063f925SOleksandr Tymoshenko return (0); 4024063f925SOleksandr Tymoshenko } 4034063f925SOleksandr Tymoshenko 4044063f925SOleksandr Tymoshenko static int 4054063f925SOleksandr Tymoshenko bcm_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 4064063f925SOleksandr Tymoshenko { 4074063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4084063f925SOleksandr Tymoshenko int i; 4094063f925SOleksandr Tymoshenko 4104063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4114063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4124063f925SOleksandr Tymoshenko break; 4134063f925SOleksandr Tymoshenko } 4144063f925SOleksandr Tymoshenko 4154063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4164063f925SOleksandr Tymoshenko return (EINVAL); 4174063f925SOleksandr Tymoshenko 4184063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4194063f925SOleksandr Tymoshenko *flags = sc->sc_gpio_pins[i].gp_flags; 4204063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4214063f925SOleksandr Tymoshenko 4224063f925SOleksandr Tymoshenko return (0); 4234063f925SOleksandr Tymoshenko } 4244063f925SOleksandr Tymoshenko 4254063f925SOleksandr Tymoshenko static int 4264063f925SOleksandr Tymoshenko bcm_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 4274063f925SOleksandr Tymoshenko { 4284063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4294063f925SOleksandr Tymoshenko int i; 4304063f925SOleksandr Tymoshenko 4314063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4324063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4334063f925SOleksandr Tymoshenko break; 4344063f925SOleksandr Tymoshenko } 4354063f925SOleksandr Tymoshenko 4364063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4374063f925SOleksandr Tymoshenko return (EINVAL); 4384063f925SOleksandr Tymoshenko 4394063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4404063f925SOleksandr Tymoshenko memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); 4414063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4424063f925SOleksandr Tymoshenko 4434063f925SOleksandr Tymoshenko return (0); 4444063f925SOleksandr Tymoshenko } 4454063f925SOleksandr Tymoshenko 4464063f925SOleksandr Tymoshenko static int 4474063f925SOleksandr Tymoshenko bcm_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 4484063f925SOleksandr Tymoshenko { 4494063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4504063f925SOleksandr Tymoshenko int i; 4514063f925SOleksandr Tymoshenko 4524063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4534063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4544063f925SOleksandr Tymoshenko break; 4554063f925SOleksandr Tymoshenko } 4564063f925SOleksandr Tymoshenko 4574063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4584063f925SOleksandr Tymoshenko return (EINVAL); 4594063f925SOleksandr Tymoshenko 4604063f925SOleksandr Tymoshenko /* We never touch on read-only/reserved pins. */ 4614063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 4624063f925SOleksandr Tymoshenko return (EINVAL); 4634063f925SOleksandr Tymoshenko 4644063f925SOleksandr Tymoshenko bcm_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); 4654063f925SOleksandr Tymoshenko 4664063f925SOleksandr Tymoshenko return (0); 4674063f925SOleksandr Tymoshenko } 4684063f925SOleksandr Tymoshenko 4694063f925SOleksandr Tymoshenko static int 4704063f925SOleksandr Tymoshenko bcm_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) 4714063f925SOleksandr Tymoshenko { 4724063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4735508fc88SLuiz Otavio O Souza uint32_t bank, reg; 4744063f925SOleksandr Tymoshenko int i; 4754063f925SOleksandr Tymoshenko 4764063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4774063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4784063f925SOleksandr Tymoshenko break; 4794063f925SOleksandr Tymoshenko } 4804063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4814063f925SOleksandr Tymoshenko return (EINVAL); 4824063f925SOleksandr Tymoshenko /* We never write to read-only/reserved pins. */ 4834063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 4844063f925SOleksandr Tymoshenko return (EINVAL); 4854063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4865508fc88SLuiz Otavio O Souza bank = BCM_GPIO_BANK(pin); 4874063f925SOleksandr Tymoshenko if (value) 4885508fc88SLuiz Otavio O Souza reg = BCM_GPIO_GPSET(bank); 4894063f925SOleksandr Tymoshenko else 4905508fc88SLuiz Otavio O Souza reg = BCM_GPIO_GPCLR(bank); 4915508fc88SLuiz Otavio O Souza BCM_GPIO_WRITE(sc, reg, BCM_GPIO_MASK(pin)); 4924063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4934063f925SOleksandr Tymoshenko 4944063f925SOleksandr Tymoshenko return (0); 4954063f925SOleksandr Tymoshenko } 4964063f925SOleksandr Tymoshenko 4974063f925SOleksandr Tymoshenko static int 4984063f925SOleksandr Tymoshenko bcm_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) 4994063f925SOleksandr Tymoshenko { 5004063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 5015508fc88SLuiz Otavio O Souza uint32_t bank, reg_data; 5024063f925SOleksandr Tymoshenko int i; 5034063f925SOleksandr Tymoshenko 5044063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 5054063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 5064063f925SOleksandr Tymoshenko break; 5074063f925SOleksandr Tymoshenko } 5084063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 5094063f925SOleksandr Tymoshenko return (EINVAL); 5105508fc88SLuiz Otavio O Souza bank = BCM_GPIO_BANK(pin); 5114063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 5124063f925SOleksandr Tymoshenko reg_data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); 5134063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 5145508fc88SLuiz Otavio O Souza *val = (reg_data & BCM_GPIO_MASK(pin)) ? 1 : 0; 5154063f925SOleksandr Tymoshenko 5164063f925SOleksandr Tymoshenko return (0); 5174063f925SOleksandr Tymoshenko } 5184063f925SOleksandr Tymoshenko 5194063f925SOleksandr Tymoshenko static int 5204063f925SOleksandr Tymoshenko bcm_gpio_pin_toggle(device_t dev, uint32_t pin) 5214063f925SOleksandr Tymoshenko { 5224063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 5235508fc88SLuiz Otavio O Souza uint32_t bank, data, reg; 5244063f925SOleksandr Tymoshenko int i; 5254063f925SOleksandr Tymoshenko 5264063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 5274063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 5284063f925SOleksandr Tymoshenko break; 5294063f925SOleksandr Tymoshenko } 5304063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 5314063f925SOleksandr Tymoshenko return (EINVAL); 5324063f925SOleksandr Tymoshenko /* We never write to read-only/reserved pins. */ 5334063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 5344063f925SOleksandr Tymoshenko return (EINVAL); 5354063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 5365508fc88SLuiz Otavio O Souza bank = BCM_GPIO_BANK(pin); 5374063f925SOleksandr Tymoshenko data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); 5385508fc88SLuiz Otavio O Souza if (data & BCM_GPIO_MASK(pin)) 5395508fc88SLuiz Otavio O Souza reg = BCM_GPIO_GPCLR(bank); 5404063f925SOleksandr Tymoshenko else 5415508fc88SLuiz Otavio O Souza reg = BCM_GPIO_GPSET(bank); 5425508fc88SLuiz Otavio O Souza BCM_GPIO_WRITE(sc, reg, BCM_GPIO_MASK(pin)); 5434063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 5444063f925SOleksandr Tymoshenko 5454063f925SOleksandr Tymoshenko return (0); 5464063f925SOleksandr Tymoshenko } 5474063f925SOleksandr Tymoshenko 5484063f925SOleksandr Tymoshenko static int 54990576f54SOleksandr Tymoshenko bcm_gpio_func_proc(SYSCTL_HANDLER_ARGS) 55090576f54SOleksandr Tymoshenko { 55190576f54SOleksandr Tymoshenko char buf[16]; 55290576f54SOleksandr Tymoshenko struct bcm_gpio_softc *sc; 55390576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl *sc_sysctl; 55490576f54SOleksandr Tymoshenko uint32_t nfunc; 55544d06d8dSLuiz Otavio O Souza int error; 55690576f54SOleksandr Tymoshenko 55790576f54SOleksandr Tymoshenko sc_sysctl = arg1; 55890576f54SOleksandr Tymoshenko sc = sc_sysctl->sc; 55990576f54SOleksandr Tymoshenko 56090576f54SOleksandr Tymoshenko /* Get the current pin function. */ 56190576f54SOleksandr Tymoshenko nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin); 56290576f54SOleksandr Tymoshenko bcm_gpio_func_str(nfunc, buf, sizeof(buf)); 56390576f54SOleksandr Tymoshenko 56490576f54SOleksandr Tymoshenko error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 56590576f54SOleksandr Tymoshenko if (error != 0 || req->newptr == NULL) 56690576f54SOleksandr Tymoshenko return (error); 56799a20111SLuiz Otavio O Souza /* Ignore changes on read-only pins. */ 56899a20111SLuiz Otavio O Souza if (bcm_gpio_pin_is_ro(sc, sc_sysctl->pin)) 56999a20111SLuiz Otavio O Souza return (0); 57090576f54SOleksandr Tymoshenko /* Parse the user supplied string and check for a valid pin function. */ 57190576f54SOleksandr Tymoshenko if (bcm_gpio_str_func(buf, &nfunc) != 0) 57290576f54SOleksandr Tymoshenko return (EINVAL); 57390576f54SOleksandr Tymoshenko 57444d06d8dSLuiz Otavio O Souza /* Update the pin alternate function. */ 57544d06d8dSLuiz Otavio O Souza bcm_gpio_set_alternate(sc->sc_dev, sc_sysctl->pin, nfunc); 57690576f54SOleksandr Tymoshenko 57790576f54SOleksandr Tymoshenko return (0); 57890576f54SOleksandr Tymoshenko } 57990576f54SOleksandr Tymoshenko 58090576f54SOleksandr Tymoshenko static void 58190576f54SOleksandr Tymoshenko bcm_gpio_sysctl_init(struct bcm_gpio_softc *sc) 58290576f54SOleksandr Tymoshenko { 58390576f54SOleksandr Tymoshenko char pinbuf[3]; 58490576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl *sc_sysctl; 58590576f54SOleksandr Tymoshenko struct sysctl_ctx_list *ctx; 58690576f54SOleksandr Tymoshenko struct sysctl_oid *tree_node, *pin_node, *pinN_node; 58790576f54SOleksandr Tymoshenko struct sysctl_oid_list *tree, *pin_tree, *pinN_tree; 58890576f54SOleksandr Tymoshenko int i; 58990576f54SOleksandr Tymoshenko 59090576f54SOleksandr Tymoshenko /* 59190576f54SOleksandr Tymoshenko * Add per-pin sysctl tree/handlers. 59290576f54SOleksandr Tymoshenko */ 59390576f54SOleksandr Tymoshenko ctx = device_get_sysctl_ctx(sc->sc_dev); 59490576f54SOleksandr Tymoshenko tree_node = device_get_sysctl_tree(sc->sc_dev); 59590576f54SOleksandr Tymoshenko tree = SYSCTL_CHILDREN(tree_node); 59690576f54SOleksandr Tymoshenko pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin", 597*7029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "GPIO Pins"); 59890576f54SOleksandr Tymoshenko pin_tree = SYSCTL_CHILDREN(pin_node); 59990576f54SOleksandr Tymoshenko 60090576f54SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 60190576f54SOleksandr Tymoshenko 60290576f54SOleksandr Tymoshenko snprintf(pinbuf, sizeof(pinbuf), "%d", i); 60390576f54SOleksandr Tymoshenko pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf, 604*7029da5cSPawel Biernacki CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "GPIO Pin"); 60590576f54SOleksandr Tymoshenko pinN_tree = SYSCTL_CHILDREN(pinN_node); 60690576f54SOleksandr Tymoshenko 60790576f54SOleksandr Tymoshenko sc->sc_sysctl[i].sc = sc; 60890576f54SOleksandr Tymoshenko sc_sysctl = &sc->sc_sysctl[i]; 60990576f54SOleksandr Tymoshenko sc_sysctl->sc = sc; 61090576f54SOleksandr Tymoshenko sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin; 61190576f54SOleksandr Tymoshenko SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function", 612*7029da5cSPawel Biernacki CTLFLAG_RW | CTLTYPE_STRING | CTLFLAG_NEEDGIANT, sc_sysctl, 61390576f54SOleksandr Tymoshenko sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc, 61490576f54SOleksandr Tymoshenko "A", "Pin Function"); 61590576f54SOleksandr Tymoshenko } 61690576f54SOleksandr Tymoshenko } 61790576f54SOleksandr Tymoshenko 61890576f54SOleksandr Tymoshenko static int 61999a20111SLuiz Otavio O Souza bcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc, phandle_t node, 62099a20111SLuiz Otavio O Souza const char *propname, const char *label) 62199a20111SLuiz Otavio O Souza { 62299a20111SLuiz Otavio O Souza int i, need_comma, npins, range_start, range_stop; 62399a20111SLuiz Otavio O Souza pcell_t *pins; 62499a20111SLuiz Otavio O Souza 62599a20111SLuiz Otavio O Souza /* Get the property data. */ 626f7604b1bSOleksandr Tymoshenko npins = OF_getencprop_alloc_multi(node, propname, sizeof(*pins), 62799a20111SLuiz Otavio O Souza (void **)&pins); 62899a20111SLuiz Otavio O Souza if (npins < 0) 62999a20111SLuiz Otavio O Souza return (-1); 63099a20111SLuiz Otavio O Souza if (npins == 0) { 631bc90a48cSOleksandr Tymoshenko OF_prop_free(pins); 63299a20111SLuiz Otavio O Souza return (0); 63399a20111SLuiz Otavio O Souza } 63499a20111SLuiz Otavio O Souza for (i = 0; i < npins; i++) 63599a20111SLuiz Otavio O Souza sc->sc_ro_pins[i + sc->sc_ro_npins] = pins[i]; 63699a20111SLuiz Otavio O Souza sc->sc_ro_npins += npins; 63799a20111SLuiz Otavio O Souza need_comma = 0; 63899a20111SLuiz Otavio O Souza device_printf(sc->sc_dev, "%s pins: ", label); 63999a20111SLuiz Otavio O Souza range_start = range_stop = pins[0]; 64099a20111SLuiz Otavio O Souza for (i = 1; i < npins; i++) { 64199a20111SLuiz Otavio O Souza if (pins[i] != range_stop + 1) { 64299a20111SLuiz Otavio O Souza if (need_comma) 64399a20111SLuiz Otavio O Souza printf(","); 64499a20111SLuiz Otavio O Souza if (range_start != range_stop) 64599a20111SLuiz Otavio O Souza printf("%d-%d", range_start, range_stop); 64699a20111SLuiz Otavio O Souza else 64799a20111SLuiz Otavio O Souza printf("%d", range_start); 64899a20111SLuiz Otavio O Souza range_start = range_stop = pins[i]; 64999a20111SLuiz Otavio O Souza need_comma = 1; 65099a20111SLuiz Otavio O Souza } else 65199a20111SLuiz Otavio O Souza range_stop++; 65299a20111SLuiz Otavio O Souza } 65399a20111SLuiz Otavio O Souza if (need_comma) 65499a20111SLuiz Otavio O Souza printf(","); 65599a20111SLuiz Otavio O Souza if (range_start != range_stop) 65699a20111SLuiz Otavio O Souza printf("%d-%d.\n", range_start, range_stop); 65799a20111SLuiz Otavio O Souza else 65899a20111SLuiz Otavio O Souza printf("%d.\n", range_start); 659bc90a48cSOleksandr Tymoshenko OF_prop_free(pins); 66099a20111SLuiz Otavio O Souza 66199a20111SLuiz Otavio O Souza return (0); 66299a20111SLuiz Otavio O Souza } 66399a20111SLuiz Otavio O Souza 66499a20111SLuiz Otavio O Souza static int 6654063f925SOleksandr Tymoshenko bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc) 6664063f925SOleksandr Tymoshenko { 66799a20111SLuiz Otavio O Souza char *name; 6684063f925SOleksandr Tymoshenko phandle_t gpio, node, reserved; 66999a20111SLuiz Otavio O Souza ssize_t len; 6704063f925SOleksandr Tymoshenko 6710e3cfd98SOleksandr Tymoshenko /* Get read-only pins if they're provided */ 6724063f925SOleksandr Tymoshenko gpio = ofw_bus_get_node(sc->sc_dev); 67399a20111SLuiz Otavio O Souza if (bcm_gpio_get_ro_pins(sc, gpio, "broadcom,read-only", 67499a20111SLuiz Otavio O Souza "read-only") != 0) 675aa2959baSOleksandr Tymoshenko return (0); 67699a20111SLuiz Otavio O Souza /* Traverse the GPIO subnodes to find the reserved pins node. */ 6774063f925SOleksandr Tymoshenko reserved = 0; 67899a20111SLuiz Otavio O Souza node = OF_child(gpio); 6794063f925SOleksandr Tymoshenko while ((node != 0) && (reserved == 0)) { 680217d17bcSOleksandr Tymoshenko len = OF_getprop_alloc(node, "name", (void **)&name); 68199a20111SLuiz Otavio O Souza if (len == -1) 68299a20111SLuiz Otavio O Souza return (-1); 6834063f925SOleksandr Tymoshenko if (strcmp(name, "reserved") == 0) 6844063f925SOleksandr Tymoshenko reserved = node; 685bc90a48cSOleksandr Tymoshenko OF_prop_free(name); 6864063f925SOleksandr Tymoshenko node = OF_peer(node); 6874063f925SOleksandr Tymoshenko } 6884063f925SOleksandr Tymoshenko if (reserved == 0) 6894063f925SOleksandr Tymoshenko return (-1); 6904063f925SOleksandr Tymoshenko /* Get the reserved pins. */ 69199a20111SLuiz Otavio O Souza if (bcm_gpio_get_ro_pins(sc, reserved, "broadcom,pins", 69299a20111SLuiz Otavio O Souza "reserved") != 0) 6934063f925SOleksandr Tymoshenko return (-1); 6944063f925SOleksandr Tymoshenko 6954063f925SOleksandr Tymoshenko return (0); 6964063f925SOleksandr Tymoshenko } 6974063f925SOleksandr Tymoshenko 69812471cecSLuiz Otavio O Souza static int 6994063f925SOleksandr Tymoshenko bcm_gpio_probe(device_t dev) 7004063f925SOleksandr Tymoshenko { 701add35ed5SIan Lepore 702add35ed5SIan Lepore if (!ofw_bus_status_okay(dev)) 703add35ed5SIan Lepore return (ENXIO); 704add35ed5SIan Lepore 705aa2959baSOleksandr Tymoshenko if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 7064063f925SOleksandr Tymoshenko return (ENXIO); 7074063f925SOleksandr Tymoshenko 7084063f925SOleksandr Tymoshenko device_set_desc(dev, "BCM2708/2835 GPIO controller"); 7094063f925SOleksandr Tymoshenko return (BUS_PROBE_DEFAULT); 7104063f925SOleksandr Tymoshenko } 7114063f925SOleksandr Tymoshenko 71289de2fb6SSvatopluk Kraus static int 71389de2fb6SSvatopluk Kraus bcm_gpio_intr_attach(device_t dev) 71489de2fb6SSvatopluk Kraus { 71589de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc; 71689de2fb6SSvatopluk Kraus 71789de2fb6SSvatopluk Kraus /* 71889de2fb6SSvatopluk Kraus * Only first two interrupt lines are used. Third line is 71989de2fb6SSvatopluk Kraus * mirrored second line and forth line is common for all banks. 72089de2fb6SSvatopluk Kraus */ 72189de2fb6SSvatopluk Kraus sc = device_get_softc(dev); 72289de2fb6SSvatopluk Kraus if (sc->sc_res[1] == NULL || sc->sc_res[2] == NULL) 72389de2fb6SSvatopluk Kraus return (-1); 72489de2fb6SSvatopluk Kraus 72589de2fb6SSvatopluk Kraus if (bcm_gpio_pic_attach(sc) != 0) { 72689de2fb6SSvatopluk Kraus device_printf(dev, "unable to attach PIC\n"); 72789de2fb6SSvatopluk Kraus return (-1); 72889de2fb6SSvatopluk Kraus } 72989de2fb6SSvatopluk Kraus if (bus_setup_intr(dev, sc->sc_res[1], INTR_TYPE_MISC | INTR_MPSAFE, 73089de2fb6SSvatopluk Kraus bcm_gpio_intr_bank0, NULL, sc, &sc->sc_intrhand[0]) != 0) 73189de2fb6SSvatopluk Kraus return (-1); 73289de2fb6SSvatopluk Kraus if (bus_setup_intr(dev, sc->sc_res[2], INTR_TYPE_MISC | INTR_MPSAFE, 73389de2fb6SSvatopluk Kraus bcm_gpio_intr_bank1, NULL, sc, &sc->sc_intrhand[1]) != 0) 73489de2fb6SSvatopluk Kraus return (-1); 73589de2fb6SSvatopluk Kraus 73689de2fb6SSvatopluk Kraus return (0); 73789de2fb6SSvatopluk Kraus } 73889de2fb6SSvatopluk Kraus 73989de2fb6SSvatopluk Kraus static void 74089de2fb6SSvatopluk Kraus bcm_gpio_intr_detach(device_t dev) 74189de2fb6SSvatopluk Kraus { 74289de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc; 74389de2fb6SSvatopluk Kraus 74489de2fb6SSvatopluk Kraus sc = device_get_softc(dev); 74589de2fb6SSvatopluk Kraus if (sc->sc_intrhand[0] != NULL) 74689de2fb6SSvatopluk Kraus bus_teardown_intr(dev, sc->sc_res[1], sc->sc_intrhand[0]); 74789de2fb6SSvatopluk Kraus if (sc->sc_intrhand[1] != NULL) 74889de2fb6SSvatopluk Kraus bus_teardown_intr(dev, sc->sc_res[2], sc->sc_intrhand[1]); 74989de2fb6SSvatopluk Kraus 75089de2fb6SSvatopluk Kraus bcm_gpio_pic_detach(sc); 75189de2fb6SSvatopluk Kraus } 75289de2fb6SSvatopluk Kraus 75312471cecSLuiz Otavio O Souza static int 7544063f925SOleksandr Tymoshenko bcm_gpio_attach(device_t dev) 7554063f925SOleksandr Tymoshenko { 756c2136589SLuiz Otavio O Souza int i, j; 7574063f925SOleksandr Tymoshenko phandle_t gpio; 758c2136589SLuiz Otavio O Souza struct bcm_gpio_softc *sc; 759c2136589SLuiz Otavio O Souza uint32_t func; 7604063f925SOleksandr Tymoshenko 76112471cecSLuiz Otavio O Souza if (bcm_gpio_sc != NULL) 76212471cecSLuiz Otavio O Souza return (ENXIO); 76312471cecSLuiz Otavio O Souza 76412471cecSLuiz Otavio O Souza bcm_gpio_sc = sc = device_get_softc(dev); 7654063f925SOleksandr Tymoshenko sc->sc_dev = dev; 76612471cecSLuiz Otavio O Souza mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_SPIN); 767c2136589SLuiz Otavio O Souza if (bus_alloc_resources(dev, bcm_gpio_res_spec, sc->sc_res) != 0) { 768c2136589SLuiz Otavio O Souza device_printf(dev, "cannot allocate resources\n"); 769c2136589SLuiz Otavio O Souza goto fail; 7704063f925SOleksandr Tymoshenko } 771c2136589SLuiz Otavio O Souza sc->sc_bst = rman_get_bustag(sc->sc_res[0]); 772c2136589SLuiz Otavio O Souza sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); 77312471cecSLuiz Otavio O Souza /* Setup the GPIO interrupt handler. */ 77412471cecSLuiz Otavio O Souza if (bcm_gpio_intr_attach(dev)) { 77512471cecSLuiz Otavio O Souza device_printf(dev, "unable to setup the gpio irq handler\n"); 77612471cecSLuiz Otavio O Souza goto fail; 77712471cecSLuiz Otavio O Souza } 7784063f925SOleksandr Tymoshenko /* Find our node. */ 7794063f925SOleksandr Tymoshenko gpio = ofw_bus_get_node(sc->sc_dev); 7804063f925SOleksandr Tymoshenko if (!OF_hasprop(gpio, "gpio-controller")) 7814063f925SOleksandr Tymoshenko /* Node is not a GPIO controller. */ 7824063f925SOleksandr Tymoshenko goto fail; 7834063f925SOleksandr Tymoshenko /* 7844063f925SOleksandr Tymoshenko * Find the read-only pins. These are pins we never touch or bad 7854063f925SOleksandr Tymoshenko * things could happen. 7864063f925SOleksandr Tymoshenko */ 7874063f925SOleksandr Tymoshenko if (bcm_gpio_get_reserved_pins(sc) == -1) 7884063f925SOleksandr Tymoshenko goto fail; 7894063f925SOleksandr Tymoshenko /* Initialize the software controlled pins. */ 7908d900240SLuiz Otavio O Souza for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) { 7914063f925SOleksandr Tymoshenko snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, 7924063f925SOleksandr Tymoshenko "pin %d", j); 79390576f54SOleksandr Tymoshenko func = bcm_gpio_get_function(sc, j); 7944063f925SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_pin = j; 7954063f925SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS; 79690576f54SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func); 7974063f925SOleksandr Tymoshenko i++; 7984063f925SOleksandr Tymoshenko } 7994063f925SOleksandr Tymoshenko sc->sc_gpio_npins = i; 80090576f54SOleksandr Tymoshenko bcm_gpio_sysctl_init(sc); 8017836352bSLuiz Otavio O Souza sc->sc_busdev = gpiobus_attach_bus(dev); 8027836352bSLuiz Otavio O Souza if (sc->sc_busdev == NULL) 8037836352bSLuiz Otavio O Souza goto fail; 80490576f54SOleksandr Tymoshenko 80591cc58afSOleksandr Tymoshenko fdt_pinctrl_register(dev, "brcm,pins"); 80691cc58afSOleksandr Tymoshenko fdt_pinctrl_configure_tree(dev); 80791cc58afSOleksandr Tymoshenko 8087836352bSLuiz Otavio O Souza return (0); 8094063f925SOleksandr Tymoshenko 8104063f925SOleksandr Tymoshenko fail: 81112471cecSLuiz Otavio O Souza bcm_gpio_intr_detach(dev); 812c2136589SLuiz Otavio O Souza bus_release_resources(dev, bcm_gpio_res_spec, sc->sc_res); 813c2136589SLuiz Otavio O Souza mtx_destroy(&sc->sc_mtx); 814c2136589SLuiz Otavio O Souza 8154063f925SOleksandr Tymoshenko return (ENXIO); 8164063f925SOleksandr Tymoshenko } 8174063f925SOleksandr Tymoshenko 8184063f925SOleksandr Tymoshenko static int 8194063f925SOleksandr Tymoshenko bcm_gpio_detach(device_t dev) 8204063f925SOleksandr Tymoshenko { 8214063f925SOleksandr Tymoshenko 8224063f925SOleksandr Tymoshenko return (EBUSY); 8234063f925SOleksandr Tymoshenko } 8244063f925SOleksandr Tymoshenko 82589de2fb6SSvatopluk Kraus static inline void 826c28b681cSSvatopluk Kraus bcm_gpio_modify(struct bcm_gpio_softc *sc, uint32_t reg, uint32_t mask, 827c28b681cSSvatopluk Kraus bool set_bits) 828c28b681cSSvatopluk Kraus { 829c28b681cSSvatopluk Kraus 830c28b681cSSvatopluk Kraus if (set_bits) 831c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, reg, mask); 832c28b681cSSvatopluk Kraus else 833c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, reg, mask); 834c28b681cSSvatopluk Kraus } 835c28b681cSSvatopluk Kraus 836c28b681cSSvatopluk Kraus static inline void 83789de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) 83889de2fb6SSvatopluk Kraus { 83989de2fb6SSvatopluk Kraus uint32_t bank; 84089de2fb6SSvatopluk Kraus 84189de2fb6SSvatopluk Kraus /* Write 1 to clear. */ 84289de2fb6SSvatopluk Kraus bank = BCM_GPIO_BANK(bgi->bgi_irq); 84389de2fb6SSvatopluk Kraus BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), bgi->bgi_mask); 84489de2fb6SSvatopluk Kraus } 84589de2fb6SSvatopluk Kraus 84689de2fb6SSvatopluk Kraus static inline bool 84789de2fb6SSvatopluk Kraus bcm_gpio_isrc_is_level(struct bcm_gpio_irqsrc *bgi) 84889de2fb6SSvatopluk Kraus { 84989de2fb6SSvatopluk Kraus 850c28b681cSSvatopluk Kraus return (bgi->bgi_mode == GPIO_INTR_LEVEL_LOW || 851c28b681cSSvatopluk Kraus bgi->bgi_mode == GPIO_INTR_LEVEL_HIGH); 85289de2fb6SSvatopluk Kraus } 85389de2fb6SSvatopluk Kraus 85489de2fb6SSvatopluk Kraus static inline void 85589de2fb6SSvatopluk Kraus bcm_gpio_isrc_mask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) 85689de2fb6SSvatopluk Kraus { 857c28b681cSSvatopluk Kraus uint32_t bank; 85889de2fb6SSvatopluk Kraus 859c28b681cSSvatopluk Kraus bank = BCM_GPIO_BANK(bgi->bgi_irq); 86089de2fb6SSvatopluk Kraus BCM_GPIO_LOCK(sc); 861c28b681cSSvatopluk Kraus switch (bgi->bgi_mode) { 862c28b681cSSvatopluk Kraus case GPIO_INTR_LEVEL_LOW: 863c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask); 864c28b681cSSvatopluk Kraus break; 865c28b681cSSvatopluk Kraus case GPIO_INTR_LEVEL_HIGH: 866c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask); 867c28b681cSSvatopluk Kraus break; 868c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_RISING: 869c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); 870c28b681cSSvatopluk Kraus break; 871c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_FALLING: 872c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); 873c28b681cSSvatopluk Kraus break; 874c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_BOTH: 875c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); 876c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); 877c28b681cSSvatopluk Kraus break; 878c28b681cSSvatopluk Kraus } 879c28b681cSSvatopluk Kraus BCM_GPIO_UNLOCK(sc); 88089de2fb6SSvatopluk Kraus } 88189de2fb6SSvatopluk Kraus 88289de2fb6SSvatopluk Kraus static inline void 88389de2fb6SSvatopluk Kraus bcm_gpio_isrc_unmask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) 88489de2fb6SSvatopluk Kraus { 885c28b681cSSvatopluk Kraus uint32_t bank; 88689de2fb6SSvatopluk Kraus 887c28b681cSSvatopluk Kraus bank = BCM_GPIO_BANK(bgi->bgi_irq); 88889de2fb6SSvatopluk Kraus BCM_GPIO_LOCK(sc); 889c28b681cSSvatopluk Kraus switch (bgi->bgi_mode) { 890c28b681cSSvatopluk Kraus case GPIO_INTR_LEVEL_LOW: 891c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask); 892c28b681cSSvatopluk Kraus break; 893c28b681cSSvatopluk Kraus case GPIO_INTR_LEVEL_HIGH: 894c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask); 895c28b681cSSvatopluk Kraus break; 896c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_RISING: 897c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); 898c28b681cSSvatopluk Kraus break; 899c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_FALLING: 900c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); 901c28b681cSSvatopluk Kraus break; 902c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_BOTH: 903c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); 904c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); 905c28b681cSSvatopluk Kraus break; 906c28b681cSSvatopluk Kraus } 90789de2fb6SSvatopluk Kraus BCM_GPIO_UNLOCK(sc); 90889de2fb6SSvatopluk Kraus } 90989de2fb6SSvatopluk Kraus 91089de2fb6SSvatopluk Kraus static int 91189de2fb6SSvatopluk Kraus bcm_gpio_intr_internal(struct bcm_gpio_softc *sc, uint32_t bank) 91289de2fb6SSvatopluk Kraus { 91389de2fb6SSvatopluk Kraus u_int irq; 91489de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi; 91589de2fb6SSvatopluk Kraus uint32_t reg; 91689de2fb6SSvatopluk Kraus 91789de2fb6SSvatopluk Kraus /* Do not care of spurious interrupt on GPIO. */ 91889de2fb6SSvatopluk Kraus reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank)); 91989de2fb6SSvatopluk Kraus while (reg != 0) { 92089de2fb6SSvatopluk Kraus irq = BCM_GPIO_PINS_PER_BANK * bank + ffs(reg) - 1; 92189de2fb6SSvatopluk Kraus bgi = sc->sc_isrcs + irq; 92289de2fb6SSvatopluk Kraus if (!bcm_gpio_isrc_is_level(bgi)) 92389de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(sc, bgi); 92489de2fb6SSvatopluk Kraus if (intr_isrc_dispatch(&bgi->bgi_isrc, 92589de2fb6SSvatopluk Kraus curthread->td_intr_frame) != 0) { 92689de2fb6SSvatopluk Kraus bcm_gpio_isrc_mask(sc, bgi); 92789de2fb6SSvatopluk Kraus if (bcm_gpio_isrc_is_level(bgi)) 92889de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(sc, bgi); 92989de2fb6SSvatopluk Kraus device_printf(sc->sc_dev, "Stray irq %u disabled\n", 93089de2fb6SSvatopluk Kraus irq); 93189de2fb6SSvatopluk Kraus } 93289de2fb6SSvatopluk Kraus reg &= ~bgi->bgi_mask; 93389de2fb6SSvatopluk Kraus } 93489de2fb6SSvatopluk Kraus return (FILTER_HANDLED); 93589de2fb6SSvatopluk Kraus } 93689de2fb6SSvatopluk Kraus 93789de2fb6SSvatopluk Kraus static int 93889de2fb6SSvatopluk Kraus bcm_gpio_intr_bank0(void *arg) 93989de2fb6SSvatopluk Kraus { 94089de2fb6SSvatopluk Kraus 94189de2fb6SSvatopluk Kraus return (bcm_gpio_intr_internal(arg, 0)); 94289de2fb6SSvatopluk Kraus } 94389de2fb6SSvatopluk Kraus 94489de2fb6SSvatopluk Kraus static int 94589de2fb6SSvatopluk Kraus bcm_gpio_intr_bank1(void *arg) 94689de2fb6SSvatopluk Kraus { 94789de2fb6SSvatopluk Kraus 94889de2fb6SSvatopluk Kraus return (bcm_gpio_intr_internal(arg, 1)); 94989de2fb6SSvatopluk Kraus } 95089de2fb6SSvatopluk Kraus 95189de2fb6SSvatopluk Kraus static int 95289de2fb6SSvatopluk Kraus bcm_gpio_pic_attach(struct bcm_gpio_softc *sc) 95389de2fb6SSvatopluk Kraus { 95489de2fb6SSvatopluk Kraus int error; 95589de2fb6SSvatopluk Kraus uint32_t irq; 95689de2fb6SSvatopluk Kraus const char *name; 95789de2fb6SSvatopluk Kraus 95889de2fb6SSvatopluk Kraus name = device_get_nameunit(sc->sc_dev); 95989de2fb6SSvatopluk Kraus for (irq = 0; irq < BCM_GPIO_PINS; irq++) { 96089de2fb6SSvatopluk Kraus sc->sc_isrcs[irq].bgi_irq = irq; 96189de2fb6SSvatopluk Kraus sc->sc_isrcs[irq].bgi_mask = BCM_GPIO_MASK(irq); 962c28b681cSSvatopluk Kraus sc->sc_isrcs[irq].bgi_mode = GPIO_INTR_CONFORM; 96389de2fb6SSvatopluk Kraus 96489de2fb6SSvatopluk Kraus error = intr_isrc_register(&sc->sc_isrcs[irq].bgi_isrc, 96589de2fb6SSvatopluk Kraus sc->sc_dev, 0, "%s,%u", name, irq); 96689de2fb6SSvatopluk Kraus if (error != 0) 96789de2fb6SSvatopluk Kraus return (error); /* XXX deregister ISRCs */ 96889de2fb6SSvatopluk Kraus } 9699346e913SAndrew Turner if (intr_pic_register(sc->sc_dev, 9709346e913SAndrew Turner OF_xref_from_node(ofw_bus_get_node(sc->sc_dev))) == NULL) 9719346e913SAndrew Turner return (ENXIO); 9729346e913SAndrew Turner 9739346e913SAndrew Turner return (0); 97489de2fb6SSvatopluk Kraus } 97589de2fb6SSvatopluk Kraus 97689de2fb6SSvatopluk Kraus static int 97789de2fb6SSvatopluk Kraus bcm_gpio_pic_detach(struct bcm_gpio_softc *sc) 97889de2fb6SSvatopluk Kraus { 97989de2fb6SSvatopluk Kraus 98089de2fb6SSvatopluk Kraus /* 98189de2fb6SSvatopluk Kraus * There has not been established any procedure yet 98289de2fb6SSvatopluk Kraus * how to detach PIC from living system correctly. 98389de2fb6SSvatopluk Kraus */ 98489de2fb6SSvatopluk Kraus device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__); 98589de2fb6SSvatopluk Kraus return (EBUSY); 98689de2fb6SSvatopluk Kraus } 98789de2fb6SSvatopluk Kraus 98889de2fb6SSvatopluk Kraus static void 989c28b681cSSvatopluk Kraus bcm_gpio_pic_config_intr(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi, 990c28b681cSSvatopluk Kraus uint32_t mode) 991c28b681cSSvatopluk Kraus { 992c28b681cSSvatopluk Kraus uint32_t bank; 993c28b681cSSvatopluk Kraus 994c28b681cSSvatopluk Kraus bank = BCM_GPIO_BANK(bgi->bgi_irq); 995c28b681cSSvatopluk Kraus BCM_GPIO_LOCK(sc); 996c28b681cSSvatopluk Kraus bcm_gpio_modify(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask, 997c28b681cSSvatopluk Kraus mode == GPIO_INTR_EDGE_RISING || mode == GPIO_INTR_EDGE_BOTH); 998c28b681cSSvatopluk Kraus bcm_gpio_modify(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask, 999c28b681cSSvatopluk Kraus mode == GPIO_INTR_EDGE_FALLING || mode == GPIO_INTR_EDGE_BOTH); 1000c28b681cSSvatopluk Kraus bcm_gpio_modify(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask, 1001c28b681cSSvatopluk Kraus mode == GPIO_INTR_LEVEL_HIGH); 1002c28b681cSSvatopluk Kraus bcm_gpio_modify(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask, 1003c28b681cSSvatopluk Kraus mode == GPIO_INTR_LEVEL_LOW); 1004c28b681cSSvatopluk Kraus bgi->bgi_mode = mode; 1005c28b681cSSvatopluk Kraus BCM_GPIO_UNLOCK(sc); 1006c28b681cSSvatopluk Kraus } 1007c28b681cSSvatopluk Kraus 1008c28b681cSSvatopluk Kraus static void 100989de2fb6SSvatopluk Kraus bcm_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) 101089de2fb6SSvatopluk Kraus { 101189de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 101289de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 101389de2fb6SSvatopluk Kraus 101489de2fb6SSvatopluk Kraus bcm_gpio_isrc_mask(sc, bgi); 101589de2fb6SSvatopluk Kraus } 101689de2fb6SSvatopluk Kraus 101789de2fb6SSvatopluk Kraus static void 101889de2fb6SSvatopluk Kraus bcm_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) 101989de2fb6SSvatopluk Kraus { 102089de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 102189de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 102289de2fb6SSvatopluk Kraus 102389de2fb6SSvatopluk Kraus arm_irq_memory_barrier(bgi->bgi_irq); 102489de2fb6SSvatopluk Kraus bcm_gpio_isrc_unmask(sc, bgi); 102589de2fb6SSvatopluk Kraus } 102689de2fb6SSvatopluk Kraus 102789de2fb6SSvatopluk Kraus static int 10286247277aSSvatopluk Kraus bcm_gpio_pic_map_fdt(struct bcm_gpio_softc *sc, struct intr_map_data_fdt *daf, 1029c28b681cSSvatopluk Kraus u_int *irqp, uint32_t *modep) 103089de2fb6SSvatopluk Kraus { 103189de2fb6SSvatopluk Kraus u_int irq; 1032151ba793SAlexander Kabaev uint32_t mode; 103389de2fb6SSvatopluk Kraus 103489de2fb6SSvatopluk Kraus /* 103589de2fb6SSvatopluk Kraus * The first cell is the interrupt number. 103689de2fb6SSvatopluk Kraus * The second cell is used to specify flags: 103789de2fb6SSvatopluk Kraus * bits[3:0] trigger type and level flags: 103889de2fb6SSvatopluk Kraus * 1 = low-to-high edge triggered. 103989de2fb6SSvatopluk Kraus * 2 = high-to-low edge triggered. 104089de2fb6SSvatopluk Kraus * 4 = active high level-sensitive. 104189de2fb6SSvatopluk Kraus * 8 = active low level-sensitive. 104289de2fb6SSvatopluk Kraus */ 10436247277aSSvatopluk Kraus if (daf->ncells != 2) 104489de2fb6SSvatopluk Kraus return (EINVAL); 104589de2fb6SSvatopluk Kraus 10466247277aSSvatopluk Kraus irq = daf->cells[0]; 104789de2fb6SSvatopluk Kraus if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq)) 104889de2fb6SSvatopluk Kraus return (EINVAL); 104989de2fb6SSvatopluk Kraus 1050c28b681cSSvatopluk Kraus /* Only reasonable modes are supported. */ 10516247277aSSvatopluk Kraus if (daf->cells[1] == 1) 1052c28b681cSSvatopluk Kraus mode = GPIO_INTR_EDGE_RISING; 10536247277aSSvatopluk Kraus else if (daf->cells[1] == 2) 1054c28b681cSSvatopluk Kraus mode = GPIO_INTR_EDGE_FALLING; 10556247277aSSvatopluk Kraus else if (daf->cells[1] == 3) 1056c28b681cSSvatopluk Kraus mode = GPIO_INTR_EDGE_BOTH; 10576247277aSSvatopluk Kraus else if (daf->cells[1] == 4) 1058c28b681cSSvatopluk Kraus mode = GPIO_INTR_LEVEL_HIGH; 10596247277aSSvatopluk Kraus else if (daf->cells[1] == 8) 1060c28b681cSSvatopluk Kraus mode = GPIO_INTR_LEVEL_LOW; 106189de2fb6SSvatopluk Kraus else 106289de2fb6SSvatopluk Kraus return (EINVAL); 106389de2fb6SSvatopluk Kraus 106489de2fb6SSvatopluk Kraus *irqp = irq; 1065c28b681cSSvatopluk Kraus if (modep != NULL) 1066c28b681cSSvatopluk Kraus *modep = mode; 106789de2fb6SSvatopluk Kraus return (0); 106889de2fb6SSvatopluk Kraus } 106989de2fb6SSvatopluk Kraus 107089de2fb6SSvatopluk Kraus static int 10716247277aSSvatopluk Kraus bcm_gpio_pic_map_gpio(struct bcm_gpio_softc *sc, struct intr_map_data_gpio *dag, 10726247277aSSvatopluk Kraus u_int *irqp, uint32_t *modep) 10736247277aSSvatopluk Kraus { 10746247277aSSvatopluk Kraus u_int irq; 10756247277aSSvatopluk Kraus uint32_t mode; 10766247277aSSvatopluk Kraus 10776247277aSSvatopluk Kraus irq = dag->gpio_pin_num; 10786247277aSSvatopluk Kraus if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq)) 10796247277aSSvatopluk Kraus return (EINVAL); 10806247277aSSvatopluk Kraus 10816247277aSSvatopluk Kraus mode = dag->gpio_intr_mode; 10826247277aSSvatopluk Kraus if (mode != GPIO_INTR_LEVEL_LOW && mode != GPIO_INTR_LEVEL_HIGH && 10836247277aSSvatopluk Kraus mode != GPIO_INTR_EDGE_RISING && mode != GPIO_INTR_EDGE_FALLING && 10846247277aSSvatopluk Kraus mode != GPIO_INTR_EDGE_BOTH) 10856247277aSSvatopluk Kraus return (EINVAL); 10866247277aSSvatopluk Kraus 10876247277aSSvatopluk Kraus *irqp = irq; 10886247277aSSvatopluk Kraus if (modep != NULL) 10896247277aSSvatopluk Kraus *modep = mode; 10906247277aSSvatopluk Kraus return (0); 10916247277aSSvatopluk Kraus } 10926247277aSSvatopluk Kraus 10936247277aSSvatopluk Kraus static int 10946247277aSSvatopluk Kraus bcm_gpio_pic_map(struct bcm_gpio_softc *sc, struct intr_map_data *data, 10956247277aSSvatopluk Kraus u_int *irqp, uint32_t *modep) 10966247277aSSvatopluk Kraus { 10976247277aSSvatopluk Kraus 10986247277aSSvatopluk Kraus switch (data->type) { 10996247277aSSvatopluk Kraus case INTR_MAP_DATA_FDT: 11006247277aSSvatopluk Kraus return (bcm_gpio_pic_map_fdt(sc, 11016247277aSSvatopluk Kraus (struct intr_map_data_fdt *)data, irqp, modep)); 11026247277aSSvatopluk Kraus case INTR_MAP_DATA_GPIO: 11036247277aSSvatopluk Kraus return (bcm_gpio_pic_map_gpio(sc, 11046247277aSSvatopluk Kraus (struct intr_map_data_gpio *)data, irqp, modep)); 11056247277aSSvatopluk Kraus default: 11066247277aSSvatopluk Kraus return (ENOTSUP); 11076247277aSSvatopluk Kraus } 11086247277aSSvatopluk Kraus } 11096247277aSSvatopluk Kraus 11106247277aSSvatopluk Kraus static int 111189de2fb6SSvatopluk Kraus bcm_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, 111289de2fb6SSvatopluk Kraus struct intr_irqsrc **isrcp) 111389de2fb6SSvatopluk Kraus { 111489de2fb6SSvatopluk Kraus int error; 111589de2fb6SSvatopluk Kraus u_int irq; 11166247277aSSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 111789de2fb6SSvatopluk Kraus 11186247277aSSvatopluk Kraus error = bcm_gpio_pic_map(sc, data, &irq, NULL); 111989de2fb6SSvatopluk Kraus if (error == 0) 112089de2fb6SSvatopluk Kraus *isrcp = &sc->sc_isrcs[irq].bgi_isrc; 112189de2fb6SSvatopluk Kraus return (error); 112289de2fb6SSvatopluk Kraus } 112389de2fb6SSvatopluk Kraus 112489de2fb6SSvatopluk Kraus static void 112589de2fb6SSvatopluk Kraus bcm_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) 112689de2fb6SSvatopluk Kraus { 112789de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 112889de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 112989de2fb6SSvatopluk Kraus 113089de2fb6SSvatopluk Kraus if (bcm_gpio_isrc_is_level(bgi)) 113189de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(sc, bgi); 113289de2fb6SSvatopluk Kraus } 113389de2fb6SSvatopluk Kraus 113489de2fb6SSvatopluk Kraus static void 113589de2fb6SSvatopluk Kraus bcm_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) 113689de2fb6SSvatopluk Kraus { 113789de2fb6SSvatopluk Kraus 113889de2fb6SSvatopluk Kraus bcm_gpio_pic_enable_intr(dev, isrc); 113989de2fb6SSvatopluk Kraus } 114089de2fb6SSvatopluk Kraus 114189de2fb6SSvatopluk Kraus static void 114289de2fb6SSvatopluk Kraus bcm_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) 114389de2fb6SSvatopluk Kraus { 114489de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 114589de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 114689de2fb6SSvatopluk Kraus 114789de2fb6SSvatopluk Kraus bcm_gpio_isrc_mask(sc, bgi); 114889de2fb6SSvatopluk Kraus if (bcm_gpio_isrc_is_level(bgi)) 114989de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(sc, bgi); 115089de2fb6SSvatopluk Kraus } 115189de2fb6SSvatopluk Kraus 115289de2fb6SSvatopluk Kraus static int 115389de2fb6SSvatopluk Kraus bcm_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, 115489de2fb6SSvatopluk Kraus struct resource *res, struct intr_map_data *data) 115589de2fb6SSvatopluk Kraus { 115689de2fb6SSvatopluk Kraus u_int irq; 1157c28b681cSSvatopluk Kraus uint32_t mode; 115889de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc; 115989de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi; 116089de2fb6SSvatopluk Kraus 11616247277aSSvatopluk Kraus if (data == NULL) 116289de2fb6SSvatopluk Kraus return (ENOTSUP); 116389de2fb6SSvatopluk Kraus 116489de2fb6SSvatopluk Kraus sc = device_get_softc(dev); 116589de2fb6SSvatopluk Kraus bgi = (struct bcm_gpio_irqsrc *)isrc; 116689de2fb6SSvatopluk Kraus 116789de2fb6SSvatopluk Kraus /* Get and check config for an interrupt. */ 11686247277aSSvatopluk Kraus if (bcm_gpio_pic_map(sc, data, &irq, &mode) != 0 || bgi->bgi_irq != irq) 116989de2fb6SSvatopluk Kraus return (EINVAL); 117089de2fb6SSvatopluk Kraus 117189de2fb6SSvatopluk Kraus /* 117289de2fb6SSvatopluk Kraus * If this is a setup for another handler, 117389de2fb6SSvatopluk Kraus * only check that its configuration match. 117489de2fb6SSvatopluk Kraus */ 117589de2fb6SSvatopluk Kraus if (isrc->isrc_handlers != 0) 1176c28b681cSSvatopluk Kraus return (bgi->bgi_mode == mode ? 0 : EINVAL); 117789de2fb6SSvatopluk Kraus 1178c28b681cSSvatopluk Kraus bcm_gpio_pic_config_intr(sc, bgi, mode); 117989de2fb6SSvatopluk Kraus return (0); 118089de2fb6SSvatopluk Kraus } 118189de2fb6SSvatopluk Kraus 118289de2fb6SSvatopluk Kraus static int 118389de2fb6SSvatopluk Kraus bcm_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, 118489de2fb6SSvatopluk Kraus struct resource *res, struct intr_map_data *data) 118589de2fb6SSvatopluk Kraus { 118689de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 118789de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 118889de2fb6SSvatopluk Kraus 1189c28b681cSSvatopluk Kraus if (isrc->isrc_handlers == 0) 1190c28b681cSSvatopluk Kraus bcm_gpio_pic_config_intr(sc, bgi, GPIO_INTR_CONFORM); 119189de2fb6SSvatopluk Kraus return (0); 119289de2fb6SSvatopluk Kraus } 119389de2fb6SSvatopluk Kraus 11948c705c2cSLuiz Otavio O Souza static phandle_t 11958c705c2cSLuiz Otavio O Souza bcm_gpio_get_node(device_t bus, device_t dev) 11968c705c2cSLuiz Otavio O Souza { 11978c705c2cSLuiz Otavio O Souza 11988c705c2cSLuiz Otavio O Souza /* We only have one child, the GPIO bus, which needs our own node. */ 11998c705c2cSLuiz Otavio O Souza return (ofw_bus_get_node(bus)); 12008c705c2cSLuiz Otavio O Souza } 12018c705c2cSLuiz Otavio O Souza 120291cc58afSOleksandr Tymoshenko static int 120391cc58afSOleksandr Tymoshenko bcm_gpio_configure_pins(device_t dev, phandle_t cfgxref) 120491cc58afSOleksandr Tymoshenko { 120591cc58afSOleksandr Tymoshenko phandle_t cfgnode; 120691cc58afSOleksandr Tymoshenko int i, pintuples, pulltuples; 120791cc58afSOleksandr Tymoshenko uint32_t pin; 120891cc58afSOleksandr Tymoshenko uint32_t *pins; 120991cc58afSOleksandr Tymoshenko uint32_t *pulls; 121091cc58afSOleksandr Tymoshenko uint32_t function; 121191cc58afSOleksandr Tymoshenko static struct bcm_gpio_softc *sc; 121291cc58afSOleksandr Tymoshenko 121391cc58afSOleksandr Tymoshenko sc = device_get_softc(dev); 121491cc58afSOleksandr Tymoshenko cfgnode = OF_node_from_xref(cfgxref); 121591cc58afSOleksandr Tymoshenko 121691cc58afSOleksandr Tymoshenko pins = NULL; 1217f7604b1bSOleksandr Tymoshenko pintuples = OF_getencprop_alloc_multi(cfgnode, "brcm,pins", 1218f7604b1bSOleksandr Tymoshenko sizeof(*pins), (void **)&pins); 121991cc58afSOleksandr Tymoshenko 122091cc58afSOleksandr Tymoshenko char name[32]; 122191cc58afSOleksandr Tymoshenko OF_getprop(cfgnode, "name", &name, sizeof(name)); 122291cc58afSOleksandr Tymoshenko 122391cc58afSOleksandr Tymoshenko if (pintuples < 0) 122491cc58afSOleksandr Tymoshenko return (ENOENT); 122591cc58afSOleksandr Tymoshenko 122691cc58afSOleksandr Tymoshenko if (pintuples == 0) 122791cc58afSOleksandr Tymoshenko return (0); /* Empty property is not an error. */ 122891cc58afSOleksandr Tymoshenko 122991cc58afSOleksandr Tymoshenko if (OF_getencprop(cfgnode, "brcm,function", &function, 123091cc58afSOleksandr Tymoshenko sizeof(function)) <= 0) { 123191cc58afSOleksandr Tymoshenko OF_prop_free(pins); 123291cc58afSOleksandr Tymoshenko return (EINVAL); 123391cc58afSOleksandr Tymoshenko } 123491cc58afSOleksandr Tymoshenko 123591cc58afSOleksandr Tymoshenko pulls = NULL; 1236f7604b1bSOleksandr Tymoshenko pulltuples = OF_getencprop_alloc_multi(cfgnode, "brcm,pull", 1237f7604b1bSOleksandr Tymoshenko sizeof(*pulls), (void **)&pulls); 123891cc58afSOleksandr Tymoshenko 123991cc58afSOleksandr Tymoshenko if ((pulls != NULL) && (pulltuples != pintuples)) { 124091cc58afSOleksandr Tymoshenko OF_prop_free(pins); 124191cc58afSOleksandr Tymoshenko OF_prop_free(pulls); 124291cc58afSOleksandr Tymoshenko return (EINVAL); 124391cc58afSOleksandr Tymoshenko } 124491cc58afSOleksandr Tymoshenko 124591cc58afSOleksandr Tymoshenko for (i = 0; i < pintuples; i++) { 124691cc58afSOleksandr Tymoshenko pin = pins[i]; 124791cc58afSOleksandr Tymoshenko bcm_gpio_set_alternate(dev, pin, function); 124891cc58afSOleksandr Tymoshenko if (bootverbose) 124991cc58afSOleksandr Tymoshenko device_printf(dev, "set pin %d to func %d", pin, function); 125091cc58afSOleksandr Tymoshenko if (pulls) { 125191cc58afSOleksandr Tymoshenko if (bootverbose) 125291cc58afSOleksandr Tymoshenko printf(", pull %d", pulls[i]); 125391cc58afSOleksandr Tymoshenko switch (pulls[i]) { 125491cc58afSOleksandr Tymoshenko /* Convert to gpio(4) flags */ 125591cc58afSOleksandr Tymoshenko case BCM2835_PUD_OFF: 125691cc58afSOleksandr Tymoshenko bcm_gpio_pin_setflags(dev, pin, 0); 125791cc58afSOleksandr Tymoshenko break; 125891cc58afSOleksandr Tymoshenko case BCM2835_PUD_UP: 125991cc58afSOleksandr Tymoshenko bcm_gpio_pin_setflags(dev, pin, GPIO_PIN_PULLUP); 126091cc58afSOleksandr Tymoshenko break; 126191cc58afSOleksandr Tymoshenko case BCM2835_PUD_DOWN: 126291cc58afSOleksandr Tymoshenko bcm_gpio_pin_setflags(dev, pin, GPIO_PIN_PULLDOWN); 126391cc58afSOleksandr Tymoshenko break; 126491cc58afSOleksandr Tymoshenko default: 126591cc58afSOleksandr Tymoshenko printf("%s: invalid pull value for pin %d: %d\n", 126691cc58afSOleksandr Tymoshenko name, pin, pulls[i]); 126791cc58afSOleksandr Tymoshenko } 126891cc58afSOleksandr Tymoshenko } 126991cc58afSOleksandr Tymoshenko if (bootverbose) 127091cc58afSOleksandr Tymoshenko printf("\n"); 127191cc58afSOleksandr Tymoshenko } 127291cc58afSOleksandr Tymoshenko 127391cc58afSOleksandr Tymoshenko OF_prop_free(pins); 127491cc58afSOleksandr Tymoshenko if (pulls) 127591cc58afSOleksandr Tymoshenko OF_prop_free(pulls); 127691cc58afSOleksandr Tymoshenko 127791cc58afSOleksandr Tymoshenko return (0); 127891cc58afSOleksandr Tymoshenko } 127991cc58afSOleksandr Tymoshenko 12804063f925SOleksandr Tymoshenko static device_method_t bcm_gpio_methods[] = { 12814063f925SOleksandr Tymoshenko /* Device interface */ 12824063f925SOleksandr Tymoshenko DEVMETHOD(device_probe, bcm_gpio_probe), 12834063f925SOleksandr Tymoshenko DEVMETHOD(device_attach, bcm_gpio_attach), 12844063f925SOleksandr Tymoshenko DEVMETHOD(device_detach, bcm_gpio_detach), 12854063f925SOleksandr Tymoshenko 12864063f925SOleksandr Tymoshenko /* GPIO protocol */ 12877836352bSLuiz Otavio O Souza DEVMETHOD(gpio_get_bus, bcm_gpio_get_bus), 12884063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_max, bcm_gpio_pin_max), 12894063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getname, bcm_gpio_pin_getname), 12904063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getflags, bcm_gpio_pin_getflags), 12914063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getcaps, bcm_gpio_pin_getcaps), 12924063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_setflags, bcm_gpio_pin_setflags), 12934063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_get, bcm_gpio_pin_get), 12944063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set), 12954063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle), 12964063f925SOleksandr Tymoshenko 129789de2fb6SSvatopluk Kraus /* Interrupt controller interface */ 129889de2fb6SSvatopluk Kraus DEVMETHOD(pic_disable_intr, bcm_gpio_pic_disable_intr), 129989de2fb6SSvatopluk Kraus DEVMETHOD(pic_enable_intr, bcm_gpio_pic_enable_intr), 130089de2fb6SSvatopluk Kraus DEVMETHOD(pic_map_intr, bcm_gpio_pic_map_intr), 130189de2fb6SSvatopluk Kraus DEVMETHOD(pic_post_filter, bcm_gpio_pic_post_filter), 130289de2fb6SSvatopluk Kraus DEVMETHOD(pic_post_ithread, bcm_gpio_pic_post_ithread), 130389de2fb6SSvatopluk Kraus DEVMETHOD(pic_pre_ithread, bcm_gpio_pic_pre_ithread), 130489de2fb6SSvatopluk Kraus DEVMETHOD(pic_setup_intr, bcm_gpio_pic_setup_intr), 130589de2fb6SSvatopluk Kraus DEVMETHOD(pic_teardown_intr, bcm_gpio_pic_teardown_intr), 1306feabce61SAndrew Turner 13078c705c2cSLuiz Otavio O Souza /* ofw_bus interface */ 13088c705c2cSLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node), 13098c705c2cSLuiz Otavio O Souza 131091cc58afSOleksandr Tymoshenko /* fdt_pinctrl interface */ 131191cc58afSOleksandr Tymoshenko DEVMETHOD(fdt_pinctrl_configure, bcm_gpio_configure_pins), 131291cc58afSOleksandr Tymoshenko 13134063f925SOleksandr Tymoshenko DEVMETHOD_END 13144063f925SOleksandr Tymoshenko }; 13154063f925SOleksandr Tymoshenko 13164063f925SOleksandr Tymoshenko static devclass_t bcm_gpio_devclass; 13174063f925SOleksandr Tymoshenko 13184063f925SOleksandr Tymoshenko static driver_t bcm_gpio_driver = { 13194063f925SOleksandr Tymoshenko "gpio", 13204063f925SOleksandr Tymoshenko bcm_gpio_methods, 13214063f925SOleksandr Tymoshenko sizeof(struct bcm_gpio_softc), 13224063f925SOleksandr Tymoshenko }; 13234063f925SOleksandr Tymoshenko 132491cc58afSOleksandr Tymoshenko EARLY_DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE); 1325