14063f925SOleksandr Tymoshenko /*- 2*af3dc4a7SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3*af3dc4a7SPedro 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 517836352bSLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h> 524063f925SOleksandr Tymoshenko #include <dev/ofw/ofw_bus.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 5889de2fb6SSvatopluk Kraus #include "pic_if.h" 5989de2fb6SSvatopluk Kraus 604063f925SOleksandr Tymoshenko #ifdef DEBUG 614063f925SOleksandr Tymoshenko #define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ 624063f925SOleksandr Tymoshenko printf(fmt,##args); } while (0) 634063f925SOleksandr Tymoshenko #else 644063f925SOleksandr Tymoshenko #define dprintf(fmt, args...) 654063f925SOleksandr Tymoshenko #endif 664063f925SOleksandr Tymoshenko 67c2136589SLuiz Otavio O Souza #define BCM_GPIO_IRQS 4 684063f925SOleksandr Tymoshenko #define BCM_GPIO_PINS 54 6912471cecSLuiz Otavio O Souza #define BCM_GPIO_PINS_PER_BANK 32 70f7f2b2fbSSvatopluk Kraus 71f7f2b2fbSSvatopluk Kraus #define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ 72f7f2b2fbSSvatopluk Kraus GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_INTR_LEVEL_LOW | \ 73f7f2b2fbSSvatopluk Kraus GPIO_INTR_LEVEL_HIGH | GPIO_INTR_EDGE_RISING | \ 74f7f2b2fbSSvatopluk Kraus GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH) 754063f925SOleksandr Tymoshenko 76c2136589SLuiz Otavio O Souza static struct resource_spec bcm_gpio_res_spec[] = { 77c2136589SLuiz Otavio O Souza { SYS_RES_MEMORY, 0, RF_ACTIVE }, 7889de2fb6SSvatopluk Kraus { SYS_RES_IRQ, 0, RF_ACTIVE }, /* bank 0 interrupt */ 7989de2fb6SSvatopluk Kraus { SYS_RES_IRQ, 1, RF_ACTIVE }, /* bank 1 interrupt */ 80c2136589SLuiz Otavio O Souza { -1, 0, 0 } 81c2136589SLuiz Otavio O Souza }; 82c2136589SLuiz Otavio O Souza 8390576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl { 8490576f54SOleksandr Tymoshenko struct bcm_gpio_softc *sc; 8590576f54SOleksandr Tymoshenko uint32_t pin; 8690576f54SOleksandr Tymoshenko }; 8790576f54SOleksandr Tymoshenko 8889de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc { 8989de2fb6SSvatopluk Kraus struct intr_irqsrc bgi_isrc; 9089de2fb6SSvatopluk Kraus uint32_t bgi_irq; 91c28b681cSSvatopluk Kraus uint32_t bgi_mode; 9289de2fb6SSvatopluk Kraus uint32_t bgi_mask; 9389de2fb6SSvatopluk Kraus }; 9489de2fb6SSvatopluk Kraus 954063f925SOleksandr Tymoshenko struct bcm_gpio_softc { 964063f925SOleksandr Tymoshenko device_t sc_dev; 977836352bSLuiz Otavio O Souza device_t sc_busdev; 984063f925SOleksandr Tymoshenko struct mtx sc_mtx; 99c2136589SLuiz Otavio O Souza struct resource * sc_res[BCM_GPIO_IRQS + 1]; 1004063f925SOleksandr Tymoshenko bus_space_tag_t sc_bst; 1014063f925SOleksandr Tymoshenko bus_space_handle_t sc_bsh; 10212471cecSLuiz Otavio O Souza void * sc_intrhand[BCM_GPIO_IRQS]; 1034063f925SOleksandr Tymoshenko int sc_gpio_npins; 1044063f925SOleksandr Tymoshenko int sc_ro_npins; 1054063f925SOleksandr Tymoshenko int sc_ro_pins[BCM_GPIO_PINS]; 1064063f925SOleksandr Tymoshenko struct gpio_pin sc_gpio_pins[BCM_GPIO_PINS]; 10790576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl sc_sysctl[BCM_GPIO_PINS]; 10889de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc sc_isrcs[BCM_GPIO_PINS]; 1094063f925SOleksandr Tymoshenko }; 1104063f925SOleksandr Tymoshenko 1114063f925SOleksandr Tymoshenko enum bcm_gpio_pud { 1124063f925SOleksandr Tymoshenko BCM_GPIO_NONE, 1134063f925SOleksandr Tymoshenko BCM_GPIO_PULLDOWN, 1144063f925SOleksandr Tymoshenko BCM_GPIO_PULLUP, 1154063f925SOleksandr Tymoshenko }; 1164063f925SOleksandr Tymoshenko 11712471cecSLuiz Otavio O Souza #define BCM_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) 11812471cecSLuiz Otavio O Souza #define BCM_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) 11912471cecSLuiz Otavio O Souza #define BCM_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) 1204063f925SOleksandr Tymoshenko #define BCM_GPIO_WRITE(_sc, _off, _val) \ 12112471cecSLuiz Otavio O Souza bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val) 1224063f925SOleksandr Tymoshenko #define BCM_GPIO_READ(_sc, _off) \ 12312471cecSLuiz Otavio O Souza bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off) 12412471cecSLuiz Otavio O Souza #define BCM_GPIO_CLEAR_BITS(_sc, _off, _bits) \ 12512471cecSLuiz Otavio O Souza BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) & ~(_bits)) 12612471cecSLuiz Otavio O Souza #define BCM_GPIO_SET_BITS(_sc, _off, _bits) \ 12712471cecSLuiz Otavio O Souza BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) | _bits) 12812471cecSLuiz Otavio O Souza #define BCM_GPIO_BANK(a) (a / BCM_GPIO_PINS_PER_BANK) 12912471cecSLuiz Otavio O Souza #define BCM_GPIO_MASK(a) (1U << (a % BCM_GPIO_PINS_PER_BANK)) 13012471cecSLuiz Otavio O Souza 13112471cecSLuiz Otavio O Souza #define BCM_GPIO_GPFSEL(_bank) (0x00 + _bank * 4) /* Function Select */ 13212471cecSLuiz Otavio O Souza #define BCM_GPIO_GPSET(_bank) (0x1c + _bank * 4) /* Pin Out Set */ 13312471cecSLuiz Otavio O Souza #define BCM_GPIO_GPCLR(_bank) (0x28 + _bank * 4) /* Pin Out Clear */ 13412471cecSLuiz Otavio O Souza #define BCM_GPIO_GPLEV(_bank) (0x34 + _bank * 4) /* Pin Level */ 13512471cecSLuiz Otavio O Souza #define BCM_GPIO_GPEDS(_bank) (0x40 + _bank * 4) /* Event Status */ 13612471cecSLuiz Otavio O Souza #define BCM_GPIO_GPREN(_bank) (0x4c + _bank * 4) /* Rising Edge irq */ 13712471cecSLuiz Otavio O Souza #define BCM_GPIO_GPFEN(_bank) (0x58 + _bank * 4) /* Falling Edge irq */ 13812471cecSLuiz Otavio O Souza #define BCM_GPIO_GPHEN(_bank) (0x64 + _bank * 4) /* High Level irq */ 13912471cecSLuiz Otavio O Souza #define BCM_GPIO_GPLEN(_bank) (0x70 + _bank * 4) /* Low Level irq */ 14012471cecSLuiz Otavio O Souza #define BCM_GPIO_GPAREN(_bank) (0x7c + _bank * 4) /* Async Rising Edge */ 14112471cecSLuiz Otavio O Souza #define BCM_GPIO_GPAFEN(_bank) (0x88 + _bank * 4) /* Async Falling Egde */ 14212471cecSLuiz Otavio O Souza #define BCM_GPIO_GPPUD(_bank) (0x94) /* Pin Pull up/down */ 14312471cecSLuiz Otavio O Souza #define BCM_GPIO_GPPUDCLK(_bank) (0x98 + _bank * 4) /* Pin Pull up clock */ 14412471cecSLuiz Otavio O Souza 145aa2959baSOleksandr Tymoshenko static struct ofw_compat_data compat_data[] = { 146aa2959baSOleksandr Tymoshenko {"broadcom,bcm2835-gpio", 1}, 147aa2959baSOleksandr Tymoshenko {"brcm,bcm2835-gpio", 1}, 148aa2959baSOleksandr Tymoshenko {NULL, 0} 149aa2959baSOleksandr Tymoshenko }; 150aa2959baSOleksandr Tymoshenko 15112471cecSLuiz Otavio O Souza static struct bcm_gpio_softc *bcm_gpio_sc = NULL; 1524063f925SOleksandr Tymoshenko 15389de2fb6SSvatopluk Kraus static int bcm_gpio_intr_bank0(void *arg); 15489de2fb6SSvatopluk Kraus static int bcm_gpio_intr_bank1(void *arg); 15589de2fb6SSvatopluk Kraus static int bcm_gpio_pic_attach(struct bcm_gpio_softc *sc); 15689de2fb6SSvatopluk Kraus static int bcm_gpio_pic_detach(struct bcm_gpio_softc *sc); 15789de2fb6SSvatopluk Kraus 1584063f925SOleksandr Tymoshenko static int 1594063f925SOleksandr Tymoshenko bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin) 1604063f925SOleksandr Tymoshenko { 1614063f925SOleksandr Tymoshenko int i; 1624063f925SOleksandr Tymoshenko 1634063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_ro_npins; i++) 1644063f925SOleksandr Tymoshenko if (pin == sc->sc_ro_pins[i]) 1654063f925SOleksandr Tymoshenko return (1); 1664063f925SOleksandr Tymoshenko return (0); 1674063f925SOleksandr Tymoshenko } 1684063f925SOleksandr Tymoshenko 1694063f925SOleksandr Tymoshenko static uint32_t 1704063f925SOleksandr Tymoshenko bcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin) 1714063f925SOleksandr Tymoshenko { 17290576f54SOleksandr Tymoshenko uint32_t bank, func, offset; 1734063f925SOleksandr Tymoshenko 1744063f925SOleksandr Tymoshenko /* Five banks, 10 pins per bank, 3 bits per pin. */ 1754063f925SOleksandr Tymoshenko bank = pin / 10; 1764063f925SOleksandr Tymoshenko offset = (pin - bank * 10) * 3; 1774063f925SOleksandr Tymoshenko 1784063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 17990576f54SOleksandr Tymoshenko func = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7; 1804063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 1814063f925SOleksandr Tymoshenko 18290576f54SOleksandr Tymoshenko return (func); 18390576f54SOleksandr Tymoshenko } 18490576f54SOleksandr Tymoshenko 18590576f54SOleksandr Tymoshenko static void 18690576f54SOleksandr Tymoshenko bcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize) 18790576f54SOleksandr Tymoshenko { 18890576f54SOleksandr Tymoshenko 18990576f54SOleksandr Tymoshenko switch (nfunc) { 1904063f925SOleksandr Tymoshenko case BCM_GPIO_INPUT: 19190576f54SOleksandr Tymoshenko strncpy(buf, "input", bufsize); 1924063f925SOleksandr Tymoshenko break; 1934063f925SOleksandr Tymoshenko case BCM_GPIO_OUTPUT: 19490576f54SOleksandr Tymoshenko strncpy(buf, "output", bufsize); 1954063f925SOleksandr Tymoshenko break; 1964063f925SOleksandr Tymoshenko case BCM_GPIO_ALT0: 19790576f54SOleksandr Tymoshenko strncpy(buf, "alt0", bufsize); 1984063f925SOleksandr Tymoshenko break; 1994063f925SOleksandr Tymoshenko case BCM_GPIO_ALT1: 20090576f54SOleksandr Tymoshenko strncpy(buf, "alt1", bufsize); 2014063f925SOleksandr Tymoshenko break; 2024063f925SOleksandr Tymoshenko case BCM_GPIO_ALT2: 20390576f54SOleksandr Tymoshenko strncpy(buf, "alt2", bufsize); 2044063f925SOleksandr Tymoshenko break; 2054063f925SOleksandr Tymoshenko case BCM_GPIO_ALT3: 20690576f54SOleksandr Tymoshenko strncpy(buf, "alt3", bufsize); 2074063f925SOleksandr Tymoshenko break; 2084063f925SOleksandr Tymoshenko case BCM_GPIO_ALT4: 20990576f54SOleksandr Tymoshenko strncpy(buf, "alt4", bufsize); 2104063f925SOleksandr Tymoshenko break; 2114063f925SOleksandr Tymoshenko case BCM_GPIO_ALT5: 21290576f54SOleksandr Tymoshenko strncpy(buf, "alt5", bufsize); 2134063f925SOleksandr Tymoshenko break; 21490576f54SOleksandr Tymoshenko default: 21590576f54SOleksandr Tymoshenko strncpy(buf, "invalid", bufsize); 2164063f925SOleksandr Tymoshenko } 21790576f54SOleksandr Tymoshenko } 2184063f925SOleksandr Tymoshenko 21990576f54SOleksandr Tymoshenko static int 22090576f54SOleksandr Tymoshenko bcm_gpio_str_func(char *func, uint32_t *nfunc) 22190576f54SOleksandr Tymoshenko { 22290576f54SOleksandr Tymoshenko 22390576f54SOleksandr Tymoshenko if (strcasecmp(func, "input") == 0) 22490576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_INPUT; 22590576f54SOleksandr Tymoshenko else if (strcasecmp(func, "output") == 0) 22690576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_OUTPUT; 22790576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt0") == 0) 22890576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT0; 22990576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt1") == 0) 23090576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT1; 23190576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt2") == 0) 23290576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT2; 23390576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt3") == 0) 23490576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT3; 23590576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt4") == 0) 23690576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT4; 23790576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt5") == 0) 23890576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT5; 23990576f54SOleksandr Tymoshenko else 24090576f54SOleksandr Tymoshenko return (-1); 24190576f54SOleksandr Tymoshenko 24290576f54SOleksandr Tymoshenko return (0); 24390576f54SOleksandr Tymoshenko } 24490576f54SOleksandr Tymoshenko 24590576f54SOleksandr Tymoshenko static uint32_t 24690576f54SOleksandr Tymoshenko bcm_gpio_func_flag(uint32_t nfunc) 24790576f54SOleksandr Tymoshenko { 24890576f54SOleksandr Tymoshenko 24990576f54SOleksandr Tymoshenko switch (nfunc) { 2504063f925SOleksandr Tymoshenko case BCM_GPIO_INPUT: 2514063f925SOleksandr Tymoshenko return (GPIO_PIN_INPUT); 2524063f925SOleksandr Tymoshenko case BCM_GPIO_OUTPUT: 2534063f925SOleksandr Tymoshenko return (GPIO_PIN_OUTPUT); 2544063f925SOleksandr Tymoshenko } 2554063f925SOleksandr Tymoshenko return (0); 2564063f925SOleksandr Tymoshenko } 2574063f925SOleksandr Tymoshenko 2584063f925SOleksandr Tymoshenko static void 2594063f925SOleksandr Tymoshenko bcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f) 2604063f925SOleksandr Tymoshenko { 2614063f925SOleksandr Tymoshenko uint32_t bank, data, offset; 2624063f925SOleksandr Tymoshenko 26390576f54SOleksandr Tymoshenko /* Must be called with lock held. */ 26490576f54SOleksandr Tymoshenko BCM_GPIO_LOCK_ASSERT(sc); 26590576f54SOleksandr Tymoshenko 2664063f925SOleksandr Tymoshenko /* Five banks, 10 pins per bank, 3 bits per pin. */ 2674063f925SOleksandr Tymoshenko bank = pin / 10; 2684063f925SOleksandr Tymoshenko offset = (pin - bank * 10) * 3; 2694063f925SOleksandr Tymoshenko 2704063f925SOleksandr Tymoshenko data = BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)); 2714063f925SOleksandr Tymoshenko data &= ~(7 << offset); 2724063f925SOleksandr Tymoshenko data |= (f << offset); 2734063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPFSEL(bank), data); 2744063f925SOleksandr Tymoshenko } 2754063f925SOleksandr Tymoshenko 2764063f925SOleksandr Tymoshenko static void 2774063f925SOleksandr Tymoshenko bcm_gpio_set_pud(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t state) 2784063f925SOleksandr Tymoshenko { 2795508fc88SLuiz Otavio O Souza uint32_t bank; 2804063f925SOleksandr Tymoshenko 28190576f54SOleksandr Tymoshenko /* Must be called with lock held. */ 28290576f54SOleksandr Tymoshenko BCM_GPIO_LOCK_ASSERT(sc); 28390576f54SOleksandr Tymoshenko 2845508fc88SLuiz Otavio O Souza bank = BCM_GPIO_BANK(pin); 2854063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state); 2865508fc88SLuiz Otavio O Souza BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), BCM_GPIO_MASK(pin)); 2874063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0); 2884063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0); 2894063f925SOleksandr Tymoshenko } 2904063f925SOleksandr Tymoshenko 29144d06d8dSLuiz Otavio O Souza void 29244d06d8dSLuiz Otavio O Souza bcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc) 29344d06d8dSLuiz Otavio O Souza { 29444d06d8dSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 29544d06d8dSLuiz Otavio O Souza int i; 29644d06d8dSLuiz Otavio O Souza 29744d06d8dSLuiz Otavio O Souza sc = device_get_softc(dev); 29844d06d8dSLuiz Otavio O Souza BCM_GPIO_LOCK(sc); 29944d06d8dSLuiz Otavio O Souza 30044d06d8dSLuiz Otavio O Souza /* Disable pull-up or pull-down on pin. */ 30144d06d8dSLuiz Otavio O Souza bcm_gpio_set_pud(sc, pin, BCM_GPIO_NONE); 30244d06d8dSLuiz Otavio O Souza 30344d06d8dSLuiz Otavio O Souza /* And now set the pin function. */ 30444d06d8dSLuiz Otavio O Souza bcm_gpio_set_function(sc, pin, nfunc); 30544d06d8dSLuiz Otavio O Souza 30644d06d8dSLuiz Otavio O Souza /* Update the pin flags. */ 30744d06d8dSLuiz Otavio O Souza for (i = 0; i < sc->sc_gpio_npins; i++) { 30844d06d8dSLuiz Otavio O Souza if (sc->sc_gpio_pins[i].gp_pin == pin) 30944d06d8dSLuiz Otavio O Souza break; 31044d06d8dSLuiz Otavio O Souza } 31144d06d8dSLuiz Otavio O Souza if (i < sc->sc_gpio_npins) 31244d06d8dSLuiz Otavio O Souza sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc); 31344d06d8dSLuiz Otavio O Souza 31444d06d8dSLuiz Otavio O Souza BCM_GPIO_UNLOCK(sc); 31544d06d8dSLuiz Otavio O Souza } 31644d06d8dSLuiz Otavio O Souza 3174063f925SOleksandr Tymoshenko static void 3184063f925SOleksandr Tymoshenko bcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin, 3194063f925SOleksandr Tymoshenko unsigned int flags) 3204063f925SOleksandr Tymoshenko { 3214063f925SOleksandr Tymoshenko 32290576f54SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 32390576f54SOleksandr Tymoshenko 3244063f925SOleksandr Tymoshenko /* 3254063f925SOleksandr Tymoshenko * Manage input/output. 3264063f925SOleksandr Tymoshenko */ 3274063f925SOleksandr Tymoshenko if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { 3284063f925SOleksandr Tymoshenko pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); 3294063f925SOleksandr Tymoshenko if (flags & GPIO_PIN_OUTPUT) { 3304063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_OUTPUT; 3314063f925SOleksandr Tymoshenko bcm_gpio_set_function(sc, pin->gp_pin, 3324063f925SOleksandr Tymoshenko BCM_GPIO_OUTPUT); 3334063f925SOleksandr Tymoshenko } else { 3344063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_INPUT; 3354063f925SOleksandr Tymoshenko bcm_gpio_set_function(sc, pin->gp_pin, 3364063f925SOleksandr Tymoshenko BCM_GPIO_INPUT); 3374063f925SOleksandr Tymoshenko } 3384063f925SOleksandr Tymoshenko } 3394063f925SOleksandr Tymoshenko 3404063f925SOleksandr Tymoshenko /* Manage Pull-up/pull-down. */ 3414063f925SOleksandr Tymoshenko pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN); 3424063f925SOleksandr Tymoshenko if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) { 3434063f925SOleksandr Tymoshenko if (flags & GPIO_PIN_PULLUP) { 3444063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_PULLUP; 3454063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLUP); 3464063f925SOleksandr Tymoshenko } else { 3474063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_PULLDOWN; 3484063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLDOWN); 3494063f925SOleksandr Tymoshenko } 3504063f925SOleksandr Tymoshenko } else 3514063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE); 35290576f54SOleksandr Tymoshenko 35390576f54SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 3544063f925SOleksandr Tymoshenko } 3554063f925SOleksandr Tymoshenko 3567836352bSLuiz Otavio O Souza static device_t 3577836352bSLuiz Otavio O Souza bcm_gpio_get_bus(device_t dev) 3587836352bSLuiz Otavio O Souza { 3597836352bSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 3607836352bSLuiz Otavio O Souza 3617836352bSLuiz Otavio O Souza sc = device_get_softc(dev); 3627836352bSLuiz Otavio O Souza 3637836352bSLuiz Otavio O Souza return (sc->sc_busdev); 3647836352bSLuiz Otavio O Souza } 3657836352bSLuiz Otavio O Souza 3664063f925SOleksandr Tymoshenko static int 3674063f925SOleksandr Tymoshenko bcm_gpio_pin_max(device_t dev, int *maxpin) 3684063f925SOleksandr Tymoshenko { 3694063f925SOleksandr Tymoshenko 3704063f925SOleksandr Tymoshenko *maxpin = BCM_GPIO_PINS - 1; 3714063f925SOleksandr Tymoshenko return (0); 3724063f925SOleksandr Tymoshenko } 3734063f925SOleksandr Tymoshenko 3744063f925SOleksandr Tymoshenko static int 3754063f925SOleksandr Tymoshenko bcm_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 3764063f925SOleksandr Tymoshenko { 3774063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 3784063f925SOleksandr Tymoshenko int i; 3794063f925SOleksandr Tymoshenko 3804063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 3814063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 3824063f925SOleksandr Tymoshenko break; 3834063f925SOleksandr Tymoshenko } 3844063f925SOleksandr Tymoshenko 3854063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 3864063f925SOleksandr Tymoshenko return (EINVAL); 3874063f925SOleksandr Tymoshenko 3884063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 3894063f925SOleksandr Tymoshenko *caps = sc->sc_gpio_pins[i].gp_caps; 3904063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 3914063f925SOleksandr Tymoshenko 3924063f925SOleksandr Tymoshenko return (0); 3934063f925SOleksandr Tymoshenko } 3944063f925SOleksandr Tymoshenko 3954063f925SOleksandr Tymoshenko static int 3964063f925SOleksandr Tymoshenko bcm_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 3974063f925SOleksandr Tymoshenko { 3984063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 3994063f925SOleksandr Tymoshenko int i; 4004063f925SOleksandr Tymoshenko 4014063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4024063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4034063f925SOleksandr Tymoshenko break; 4044063f925SOleksandr Tymoshenko } 4054063f925SOleksandr Tymoshenko 4064063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4074063f925SOleksandr Tymoshenko return (EINVAL); 4084063f925SOleksandr Tymoshenko 4094063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4104063f925SOleksandr Tymoshenko *flags = sc->sc_gpio_pins[i].gp_flags; 4114063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4124063f925SOleksandr Tymoshenko 4134063f925SOleksandr Tymoshenko return (0); 4144063f925SOleksandr Tymoshenko } 4154063f925SOleksandr Tymoshenko 4164063f925SOleksandr Tymoshenko static int 4174063f925SOleksandr Tymoshenko bcm_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 4184063f925SOleksandr Tymoshenko { 4194063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4204063f925SOleksandr Tymoshenko int i; 4214063f925SOleksandr Tymoshenko 4224063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4234063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4244063f925SOleksandr Tymoshenko break; 4254063f925SOleksandr Tymoshenko } 4264063f925SOleksandr Tymoshenko 4274063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4284063f925SOleksandr Tymoshenko return (EINVAL); 4294063f925SOleksandr Tymoshenko 4304063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4314063f925SOleksandr Tymoshenko memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); 4324063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4334063f925SOleksandr Tymoshenko 4344063f925SOleksandr Tymoshenko return (0); 4354063f925SOleksandr Tymoshenko } 4364063f925SOleksandr Tymoshenko 4374063f925SOleksandr Tymoshenko static int 4384063f925SOleksandr Tymoshenko bcm_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 4394063f925SOleksandr Tymoshenko { 4404063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4414063f925SOleksandr Tymoshenko int i; 4424063f925SOleksandr Tymoshenko 4434063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4444063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4454063f925SOleksandr Tymoshenko break; 4464063f925SOleksandr Tymoshenko } 4474063f925SOleksandr Tymoshenko 4484063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4494063f925SOleksandr Tymoshenko return (EINVAL); 4504063f925SOleksandr Tymoshenko 4514063f925SOleksandr Tymoshenko /* We never touch on read-only/reserved pins. */ 4524063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 4534063f925SOleksandr Tymoshenko return (EINVAL); 4544063f925SOleksandr Tymoshenko 4554063f925SOleksandr Tymoshenko bcm_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); 4564063f925SOleksandr Tymoshenko 4574063f925SOleksandr Tymoshenko return (0); 4584063f925SOleksandr Tymoshenko } 4594063f925SOleksandr Tymoshenko 4604063f925SOleksandr Tymoshenko static int 4614063f925SOleksandr Tymoshenko bcm_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) 4624063f925SOleksandr Tymoshenko { 4634063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4645508fc88SLuiz Otavio O Souza uint32_t bank, reg; 4654063f925SOleksandr Tymoshenko int i; 4664063f925SOleksandr Tymoshenko 4674063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4684063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4694063f925SOleksandr Tymoshenko break; 4704063f925SOleksandr Tymoshenko } 4714063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4724063f925SOleksandr Tymoshenko return (EINVAL); 4734063f925SOleksandr Tymoshenko /* We never write to read-only/reserved pins. */ 4744063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 4754063f925SOleksandr Tymoshenko return (EINVAL); 4764063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4775508fc88SLuiz Otavio O Souza bank = BCM_GPIO_BANK(pin); 4784063f925SOleksandr Tymoshenko if (value) 4795508fc88SLuiz Otavio O Souza reg = BCM_GPIO_GPSET(bank); 4804063f925SOleksandr Tymoshenko else 4815508fc88SLuiz Otavio O Souza reg = BCM_GPIO_GPCLR(bank); 4825508fc88SLuiz Otavio O Souza BCM_GPIO_WRITE(sc, reg, BCM_GPIO_MASK(pin)); 4834063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4844063f925SOleksandr Tymoshenko 4854063f925SOleksandr Tymoshenko return (0); 4864063f925SOleksandr Tymoshenko } 4874063f925SOleksandr Tymoshenko 4884063f925SOleksandr Tymoshenko static int 4894063f925SOleksandr Tymoshenko bcm_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) 4904063f925SOleksandr Tymoshenko { 4914063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4925508fc88SLuiz Otavio O Souza uint32_t bank, reg_data; 4934063f925SOleksandr Tymoshenko int i; 4944063f925SOleksandr Tymoshenko 4954063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4964063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4974063f925SOleksandr Tymoshenko break; 4984063f925SOleksandr Tymoshenko } 4994063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 5004063f925SOleksandr Tymoshenko return (EINVAL); 5015508fc88SLuiz Otavio O Souza bank = BCM_GPIO_BANK(pin); 5024063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 5034063f925SOleksandr Tymoshenko reg_data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); 5044063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 5055508fc88SLuiz Otavio O Souza *val = (reg_data & BCM_GPIO_MASK(pin)) ? 1 : 0; 5064063f925SOleksandr Tymoshenko 5074063f925SOleksandr Tymoshenko return (0); 5084063f925SOleksandr Tymoshenko } 5094063f925SOleksandr Tymoshenko 5104063f925SOleksandr Tymoshenko static int 5114063f925SOleksandr Tymoshenko bcm_gpio_pin_toggle(device_t dev, uint32_t pin) 5124063f925SOleksandr Tymoshenko { 5134063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 5145508fc88SLuiz Otavio O Souza uint32_t bank, data, reg; 5154063f925SOleksandr Tymoshenko int i; 5164063f925SOleksandr Tymoshenko 5174063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 5184063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 5194063f925SOleksandr Tymoshenko break; 5204063f925SOleksandr Tymoshenko } 5214063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 5224063f925SOleksandr Tymoshenko return (EINVAL); 5234063f925SOleksandr Tymoshenko /* We never write to read-only/reserved pins. */ 5244063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 5254063f925SOleksandr Tymoshenko return (EINVAL); 5264063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 5275508fc88SLuiz Otavio O Souza bank = BCM_GPIO_BANK(pin); 5284063f925SOleksandr Tymoshenko data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); 5295508fc88SLuiz Otavio O Souza if (data & BCM_GPIO_MASK(pin)) 5305508fc88SLuiz Otavio O Souza reg = BCM_GPIO_GPCLR(bank); 5314063f925SOleksandr Tymoshenko else 5325508fc88SLuiz Otavio O Souza reg = BCM_GPIO_GPSET(bank); 5335508fc88SLuiz Otavio O Souza BCM_GPIO_WRITE(sc, reg, BCM_GPIO_MASK(pin)); 5344063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 5354063f925SOleksandr Tymoshenko 5364063f925SOleksandr Tymoshenko return (0); 5374063f925SOleksandr Tymoshenko } 5384063f925SOleksandr Tymoshenko 5394063f925SOleksandr Tymoshenko static int 54090576f54SOleksandr Tymoshenko bcm_gpio_func_proc(SYSCTL_HANDLER_ARGS) 54190576f54SOleksandr Tymoshenko { 54290576f54SOleksandr Tymoshenko char buf[16]; 54390576f54SOleksandr Tymoshenko struct bcm_gpio_softc *sc; 54490576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl *sc_sysctl; 54590576f54SOleksandr Tymoshenko uint32_t nfunc; 54644d06d8dSLuiz Otavio O Souza int error; 54790576f54SOleksandr Tymoshenko 54890576f54SOleksandr Tymoshenko sc_sysctl = arg1; 54990576f54SOleksandr Tymoshenko sc = sc_sysctl->sc; 55090576f54SOleksandr Tymoshenko 55190576f54SOleksandr Tymoshenko /* Get the current pin function. */ 55290576f54SOleksandr Tymoshenko nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin); 55390576f54SOleksandr Tymoshenko bcm_gpio_func_str(nfunc, buf, sizeof(buf)); 55490576f54SOleksandr Tymoshenko 55590576f54SOleksandr Tymoshenko error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 55690576f54SOleksandr Tymoshenko if (error != 0 || req->newptr == NULL) 55790576f54SOleksandr Tymoshenko return (error); 55899a20111SLuiz Otavio O Souza /* Ignore changes on read-only pins. */ 55999a20111SLuiz Otavio O Souza if (bcm_gpio_pin_is_ro(sc, sc_sysctl->pin)) 56099a20111SLuiz Otavio O Souza return (0); 56190576f54SOleksandr Tymoshenko /* Parse the user supplied string and check for a valid pin function. */ 56290576f54SOleksandr Tymoshenko if (bcm_gpio_str_func(buf, &nfunc) != 0) 56390576f54SOleksandr Tymoshenko return (EINVAL); 56490576f54SOleksandr Tymoshenko 56544d06d8dSLuiz Otavio O Souza /* Update the pin alternate function. */ 56644d06d8dSLuiz Otavio O Souza bcm_gpio_set_alternate(sc->sc_dev, sc_sysctl->pin, nfunc); 56790576f54SOleksandr Tymoshenko 56890576f54SOleksandr Tymoshenko return (0); 56990576f54SOleksandr Tymoshenko } 57090576f54SOleksandr Tymoshenko 57190576f54SOleksandr Tymoshenko static void 57290576f54SOleksandr Tymoshenko bcm_gpio_sysctl_init(struct bcm_gpio_softc *sc) 57390576f54SOleksandr Tymoshenko { 57490576f54SOleksandr Tymoshenko char pinbuf[3]; 57590576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl *sc_sysctl; 57690576f54SOleksandr Tymoshenko struct sysctl_ctx_list *ctx; 57790576f54SOleksandr Tymoshenko struct sysctl_oid *tree_node, *pin_node, *pinN_node; 57890576f54SOleksandr Tymoshenko struct sysctl_oid_list *tree, *pin_tree, *pinN_tree; 57990576f54SOleksandr Tymoshenko int i; 58090576f54SOleksandr Tymoshenko 58190576f54SOleksandr Tymoshenko /* 58290576f54SOleksandr Tymoshenko * Add per-pin sysctl tree/handlers. 58390576f54SOleksandr Tymoshenko */ 58490576f54SOleksandr Tymoshenko ctx = device_get_sysctl_ctx(sc->sc_dev); 58590576f54SOleksandr Tymoshenko tree_node = device_get_sysctl_tree(sc->sc_dev); 58690576f54SOleksandr Tymoshenko tree = SYSCTL_CHILDREN(tree_node); 58790576f54SOleksandr Tymoshenko pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin", 5881fbabb48SLuiz Otavio O Souza CTLFLAG_RD, NULL, "GPIO Pins"); 58990576f54SOleksandr Tymoshenko pin_tree = SYSCTL_CHILDREN(pin_node); 59090576f54SOleksandr Tymoshenko 59190576f54SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 59290576f54SOleksandr Tymoshenko 59390576f54SOleksandr Tymoshenko snprintf(pinbuf, sizeof(pinbuf), "%d", i); 59490576f54SOleksandr Tymoshenko pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf, 59590576f54SOleksandr Tymoshenko CTLFLAG_RD, NULL, "GPIO Pin"); 59690576f54SOleksandr Tymoshenko pinN_tree = SYSCTL_CHILDREN(pinN_node); 59790576f54SOleksandr Tymoshenko 59890576f54SOleksandr Tymoshenko sc->sc_sysctl[i].sc = sc; 59990576f54SOleksandr Tymoshenko sc_sysctl = &sc->sc_sysctl[i]; 60090576f54SOleksandr Tymoshenko sc_sysctl->sc = sc; 60190576f54SOleksandr Tymoshenko sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin; 60290576f54SOleksandr Tymoshenko SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function", 60390576f54SOleksandr Tymoshenko CTLFLAG_RW | CTLTYPE_STRING, sc_sysctl, 60490576f54SOleksandr Tymoshenko sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc, 60590576f54SOleksandr Tymoshenko "A", "Pin Function"); 60690576f54SOleksandr Tymoshenko } 60790576f54SOleksandr Tymoshenko } 60890576f54SOleksandr Tymoshenko 60990576f54SOleksandr Tymoshenko static int 61099a20111SLuiz Otavio O Souza bcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc, phandle_t node, 61199a20111SLuiz Otavio O Souza const char *propname, const char *label) 61299a20111SLuiz Otavio O Souza { 61399a20111SLuiz Otavio O Souza int i, need_comma, npins, range_start, range_stop; 61499a20111SLuiz Otavio O Souza pcell_t *pins; 61599a20111SLuiz Otavio O Souza 61699a20111SLuiz Otavio O Souza /* Get the property data. */ 61799a20111SLuiz Otavio O Souza npins = OF_getencprop_alloc(node, propname, sizeof(*pins), 61899a20111SLuiz Otavio O Souza (void **)&pins); 61999a20111SLuiz Otavio O Souza if (npins < 0) 62099a20111SLuiz Otavio O Souza return (-1); 62199a20111SLuiz Otavio O Souza if (npins == 0) { 622bc90a48cSOleksandr Tymoshenko OF_prop_free(pins); 62399a20111SLuiz Otavio O Souza return (0); 62499a20111SLuiz Otavio O Souza } 62599a20111SLuiz Otavio O Souza for (i = 0; i < npins; i++) 62699a20111SLuiz Otavio O Souza sc->sc_ro_pins[i + sc->sc_ro_npins] = pins[i]; 62799a20111SLuiz Otavio O Souza sc->sc_ro_npins += npins; 62899a20111SLuiz Otavio O Souza need_comma = 0; 62999a20111SLuiz Otavio O Souza device_printf(sc->sc_dev, "%s pins: ", label); 63099a20111SLuiz Otavio O Souza range_start = range_stop = pins[0]; 63199a20111SLuiz Otavio O Souza for (i = 1; i < npins; i++) { 63299a20111SLuiz Otavio O Souza if (pins[i] != range_stop + 1) { 63399a20111SLuiz Otavio O Souza if (need_comma) 63499a20111SLuiz Otavio O Souza printf(","); 63599a20111SLuiz Otavio O Souza if (range_start != range_stop) 63699a20111SLuiz Otavio O Souza printf("%d-%d", range_start, range_stop); 63799a20111SLuiz Otavio O Souza else 63899a20111SLuiz Otavio O Souza printf("%d", range_start); 63999a20111SLuiz Otavio O Souza range_start = range_stop = pins[i]; 64099a20111SLuiz Otavio O Souza need_comma = 1; 64199a20111SLuiz Otavio O Souza } else 64299a20111SLuiz Otavio O Souza range_stop++; 64399a20111SLuiz Otavio O Souza } 64499a20111SLuiz Otavio O Souza if (need_comma) 64599a20111SLuiz Otavio O Souza printf(","); 64699a20111SLuiz Otavio O Souza if (range_start != range_stop) 64799a20111SLuiz Otavio O Souza printf("%d-%d.\n", range_start, range_stop); 64899a20111SLuiz Otavio O Souza else 64999a20111SLuiz Otavio O Souza printf("%d.\n", range_start); 650bc90a48cSOleksandr Tymoshenko OF_prop_free(pins); 65199a20111SLuiz Otavio O Souza 65299a20111SLuiz Otavio O Souza return (0); 65399a20111SLuiz Otavio O Souza } 65499a20111SLuiz Otavio O Souza 65599a20111SLuiz Otavio O Souza static int 6564063f925SOleksandr Tymoshenko bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc) 6574063f925SOleksandr Tymoshenko { 65899a20111SLuiz Otavio O Souza char *name; 6594063f925SOleksandr Tymoshenko phandle_t gpio, node, reserved; 66099a20111SLuiz Otavio O Souza ssize_t len; 6614063f925SOleksandr Tymoshenko 6620e3cfd98SOleksandr Tymoshenko /* Get read-only pins if they're provided */ 6634063f925SOleksandr Tymoshenko gpio = ofw_bus_get_node(sc->sc_dev); 66499a20111SLuiz Otavio O Souza if (bcm_gpio_get_ro_pins(sc, gpio, "broadcom,read-only", 66599a20111SLuiz Otavio O Souza "read-only") != 0) 666aa2959baSOleksandr Tymoshenko return (0); 66799a20111SLuiz Otavio O Souza /* Traverse the GPIO subnodes to find the reserved pins node. */ 6684063f925SOleksandr Tymoshenko reserved = 0; 66999a20111SLuiz Otavio O Souza node = OF_child(gpio); 6704063f925SOleksandr Tymoshenko while ((node != 0) && (reserved == 0)) { 67199a20111SLuiz Otavio O Souza len = OF_getprop_alloc(node, "name", 1, (void **)&name); 67299a20111SLuiz Otavio O Souza if (len == -1) 67399a20111SLuiz Otavio O Souza return (-1); 6744063f925SOleksandr Tymoshenko if (strcmp(name, "reserved") == 0) 6754063f925SOleksandr Tymoshenko reserved = node; 676bc90a48cSOleksandr Tymoshenko OF_prop_free(name); 6774063f925SOleksandr Tymoshenko node = OF_peer(node); 6784063f925SOleksandr Tymoshenko } 6794063f925SOleksandr Tymoshenko if (reserved == 0) 6804063f925SOleksandr Tymoshenko return (-1); 6814063f925SOleksandr Tymoshenko /* Get the reserved pins. */ 68299a20111SLuiz Otavio O Souza if (bcm_gpio_get_ro_pins(sc, reserved, "broadcom,pins", 68399a20111SLuiz Otavio O Souza "reserved") != 0) 6844063f925SOleksandr Tymoshenko return (-1); 6854063f925SOleksandr Tymoshenko 6864063f925SOleksandr Tymoshenko return (0); 6874063f925SOleksandr Tymoshenko } 6884063f925SOleksandr Tymoshenko 68912471cecSLuiz Otavio O Souza static int 6904063f925SOleksandr Tymoshenko bcm_gpio_probe(device_t dev) 6914063f925SOleksandr Tymoshenko { 692add35ed5SIan Lepore 693add35ed5SIan Lepore if (!ofw_bus_status_okay(dev)) 694add35ed5SIan Lepore return (ENXIO); 695add35ed5SIan Lepore 696aa2959baSOleksandr Tymoshenko if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 6974063f925SOleksandr Tymoshenko return (ENXIO); 6984063f925SOleksandr Tymoshenko 6994063f925SOleksandr Tymoshenko device_set_desc(dev, "BCM2708/2835 GPIO controller"); 7004063f925SOleksandr Tymoshenko return (BUS_PROBE_DEFAULT); 7014063f925SOleksandr Tymoshenko } 7024063f925SOleksandr Tymoshenko 70389de2fb6SSvatopluk Kraus static int 70489de2fb6SSvatopluk Kraus bcm_gpio_intr_attach(device_t dev) 70589de2fb6SSvatopluk Kraus { 70689de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc; 70789de2fb6SSvatopluk Kraus 70889de2fb6SSvatopluk Kraus /* 70989de2fb6SSvatopluk Kraus * Only first two interrupt lines are used. Third line is 71089de2fb6SSvatopluk Kraus * mirrored second line and forth line is common for all banks. 71189de2fb6SSvatopluk Kraus */ 71289de2fb6SSvatopluk Kraus sc = device_get_softc(dev); 71389de2fb6SSvatopluk Kraus if (sc->sc_res[1] == NULL || sc->sc_res[2] == NULL) 71489de2fb6SSvatopluk Kraus return (-1); 71589de2fb6SSvatopluk Kraus 71689de2fb6SSvatopluk Kraus if (bcm_gpio_pic_attach(sc) != 0) { 71789de2fb6SSvatopluk Kraus device_printf(dev, "unable to attach PIC\n"); 71889de2fb6SSvatopluk Kraus return (-1); 71989de2fb6SSvatopluk Kraus } 72089de2fb6SSvatopluk Kraus if (bus_setup_intr(dev, sc->sc_res[1], INTR_TYPE_MISC | INTR_MPSAFE, 72189de2fb6SSvatopluk Kraus bcm_gpio_intr_bank0, NULL, sc, &sc->sc_intrhand[0]) != 0) 72289de2fb6SSvatopluk Kraus return (-1); 72389de2fb6SSvatopluk Kraus if (bus_setup_intr(dev, sc->sc_res[2], INTR_TYPE_MISC | INTR_MPSAFE, 72489de2fb6SSvatopluk Kraus bcm_gpio_intr_bank1, NULL, sc, &sc->sc_intrhand[1]) != 0) 72589de2fb6SSvatopluk Kraus return (-1); 72689de2fb6SSvatopluk Kraus 72789de2fb6SSvatopluk Kraus return (0); 72889de2fb6SSvatopluk Kraus } 72989de2fb6SSvatopluk Kraus 73089de2fb6SSvatopluk Kraus static void 73189de2fb6SSvatopluk Kraus bcm_gpio_intr_detach(device_t dev) 73289de2fb6SSvatopluk Kraus { 73389de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc; 73489de2fb6SSvatopluk Kraus 73589de2fb6SSvatopluk Kraus sc = device_get_softc(dev); 73689de2fb6SSvatopluk Kraus if (sc->sc_intrhand[0] != NULL) 73789de2fb6SSvatopluk Kraus bus_teardown_intr(dev, sc->sc_res[1], sc->sc_intrhand[0]); 73889de2fb6SSvatopluk Kraus if (sc->sc_intrhand[1] != NULL) 73989de2fb6SSvatopluk Kraus bus_teardown_intr(dev, sc->sc_res[2], sc->sc_intrhand[1]); 74089de2fb6SSvatopluk Kraus 74189de2fb6SSvatopluk Kraus bcm_gpio_pic_detach(sc); 74289de2fb6SSvatopluk Kraus } 74389de2fb6SSvatopluk Kraus 74412471cecSLuiz Otavio O Souza static int 7454063f925SOleksandr Tymoshenko bcm_gpio_attach(device_t dev) 7464063f925SOleksandr Tymoshenko { 747c2136589SLuiz Otavio O Souza int i, j; 7484063f925SOleksandr Tymoshenko phandle_t gpio; 749c2136589SLuiz Otavio O Souza struct bcm_gpio_softc *sc; 750c2136589SLuiz Otavio O Souza uint32_t func; 7514063f925SOleksandr Tymoshenko 75212471cecSLuiz Otavio O Souza if (bcm_gpio_sc != NULL) 75312471cecSLuiz Otavio O Souza return (ENXIO); 75412471cecSLuiz Otavio O Souza 75512471cecSLuiz Otavio O Souza bcm_gpio_sc = sc = device_get_softc(dev); 7564063f925SOleksandr Tymoshenko sc->sc_dev = dev; 75712471cecSLuiz Otavio O Souza mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_SPIN); 758c2136589SLuiz Otavio O Souza if (bus_alloc_resources(dev, bcm_gpio_res_spec, sc->sc_res) != 0) { 759c2136589SLuiz Otavio O Souza device_printf(dev, "cannot allocate resources\n"); 760c2136589SLuiz Otavio O Souza goto fail; 7614063f925SOleksandr Tymoshenko } 762c2136589SLuiz Otavio O Souza sc->sc_bst = rman_get_bustag(sc->sc_res[0]); 763c2136589SLuiz Otavio O Souza sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); 76412471cecSLuiz Otavio O Souza /* Setup the GPIO interrupt handler. */ 76512471cecSLuiz Otavio O Souza if (bcm_gpio_intr_attach(dev)) { 76612471cecSLuiz Otavio O Souza device_printf(dev, "unable to setup the gpio irq handler\n"); 76712471cecSLuiz Otavio O Souza goto fail; 76812471cecSLuiz Otavio O Souza } 7694063f925SOleksandr Tymoshenko /* Find our node. */ 7704063f925SOleksandr Tymoshenko gpio = ofw_bus_get_node(sc->sc_dev); 7714063f925SOleksandr Tymoshenko if (!OF_hasprop(gpio, "gpio-controller")) 7724063f925SOleksandr Tymoshenko /* Node is not a GPIO controller. */ 7734063f925SOleksandr Tymoshenko goto fail; 7744063f925SOleksandr Tymoshenko /* 7754063f925SOleksandr Tymoshenko * Find the read-only pins. These are pins we never touch or bad 7764063f925SOleksandr Tymoshenko * things could happen. 7774063f925SOleksandr Tymoshenko */ 7784063f925SOleksandr Tymoshenko if (bcm_gpio_get_reserved_pins(sc) == -1) 7794063f925SOleksandr Tymoshenko goto fail; 7804063f925SOleksandr Tymoshenko /* Initialize the software controlled pins. */ 7818d900240SLuiz Otavio O Souza for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) { 7824063f925SOleksandr Tymoshenko snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, 7834063f925SOleksandr Tymoshenko "pin %d", j); 78490576f54SOleksandr Tymoshenko func = bcm_gpio_get_function(sc, j); 7854063f925SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_pin = j; 7864063f925SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS; 78790576f54SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func); 7884063f925SOleksandr Tymoshenko i++; 7894063f925SOleksandr Tymoshenko } 7904063f925SOleksandr Tymoshenko sc->sc_gpio_npins = i; 79190576f54SOleksandr Tymoshenko bcm_gpio_sysctl_init(sc); 7927836352bSLuiz Otavio O Souza sc->sc_busdev = gpiobus_attach_bus(dev); 7937836352bSLuiz Otavio O Souza if (sc->sc_busdev == NULL) 7947836352bSLuiz Otavio O Souza goto fail; 79590576f54SOleksandr Tymoshenko 7967836352bSLuiz Otavio O Souza return (0); 7974063f925SOleksandr Tymoshenko 7984063f925SOleksandr Tymoshenko fail: 79912471cecSLuiz Otavio O Souza bcm_gpio_intr_detach(dev); 800c2136589SLuiz Otavio O Souza bus_release_resources(dev, bcm_gpio_res_spec, sc->sc_res); 801c2136589SLuiz Otavio O Souza mtx_destroy(&sc->sc_mtx); 802c2136589SLuiz Otavio O Souza 8034063f925SOleksandr Tymoshenko return (ENXIO); 8044063f925SOleksandr Tymoshenko } 8054063f925SOleksandr Tymoshenko 8064063f925SOleksandr Tymoshenko static int 8074063f925SOleksandr Tymoshenko bcm_gpio_detach(device_t dev) 8084063f925SOleksandr Tymoshenko { 8094063f925SOleksandr Tymoshenko 8104063f925SOleksandr Tymoshenko return (EBUSY); 8114063f925SOleksandr Tymoshenko } 8124063f925SOleksandr Tymoshenko 81389de2fb6SSvatopluk Kraus static inline void 814c28b681cSSvatopluk Kraus bcm_gpio_modify(struct bcm_gpio_softc *sc, uint32_t reg, uint32_t mask, 815c28b681cSSvatopluk Kraus bool set_bits) 816c28b681cSSvatopluk Kraus { 817c28b681cSSvatopluk Kraus 818c28b681cSSvatopluk Kraus if (set_bits) 819c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, reg, mask); 820c28b681cSSvatopluk Kraus else 821c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, reg, mask); 822c28b681cSSvatopluk Kraus } 823c28b681cSSvatopluk Kraus 824c28b681cSSvatopluk Kraus static inline void 82589de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) 82689de2fb6SSvatopluk Kraus { 82789de2fb6SSvatopluk Kraus uint32_t bank; 82889de2fb6SSvatopluk Kraus 82989de2fb6SSvatopluk Kraus /* Write 1 to clear. */ 83089de2fb6SSvatopluk Kraus bank = BCM_GPIO_BANK(bgi->bgi_irq); 83189de2fb6SSvatopluk Kraus BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), bgi->bgi_mask); 83289de2fb6SSvatopluk Kraus } 83389de2fb6SSvatopluk Kraus 83489de2fb6SSvatopluk Kraus static inline bool 83589de2fb6SSvatopluk Kraus bcm_gpio_isrc_is_level(struct bcm_gpio_irqsrc *bgi) 83689de2fb6SSvatopluk Kraus { 83789de2fb6SSvatopluk Kraus 838c28b681cSSvatopluk Kraus return (bgi->bgi_mode == GPIO_INTR_LEVEL_LOW || 839c28b681cSSvatopluk Kraus bgi->bgi_mode == GPIO_INTR_LEVEL_HIGH); 84089de2fb6SSvatopluk Kraus } 84189de2fb6SSvatopluk Kraus 84289de2fb6SSvatopluk Kraus static inline void 84389de2fb6SSvatopluk Kraus bcm_gpio_isrc_mask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) 84489de2fb6SSvatopluk Kraus { 845c28b681cSSvatopluk Kraus uint32_t bank; 84689de2fb6SSvatopluk Kraus 847c28b681cSSvatopluk Kraus bank = BCM_GPIO_BANK(bgi->bgi_irq); 84889de2fb6SSvatopluk Kraus BCM_GPIO_LOCK(sc); 849c28b681cSSvatopluk Kraus switch (bgi->bgi_mode) { 850c28b681cSSvatopluk Kraus case GPIO_INTR_LEVEL_LOW: 851c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask); 852c28b681cSSvatopluk Kraus break; 853c28b681cSSvatopluk Kraus case GPIO_INTR_LEVEL_HIGH: 854c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask); 855c28b681cSSvatopluk Kraus break; 856c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_RISING: 857c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); 858c28b681cSSvatopluk Kraus break; 859c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_FALLING: 860c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); 861c28b681cSSvatopluk Kraus break; 862c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_BOTH: 863c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); 864c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); 865c28b681cSSvatopluk Kraus break; 866c28b681cSSvatopluk Kraus } 867c28b681cSSvatopluk Kraus BCM_GPIO_UNLOCK(sc); 86889de2fb6SSvatopluk Kraus } 86989de2fb6SSvatopluk Kraus 87089de2fb6SSvatopluk Kraus static inline void 87189de2fb6SSvatopluk Kraus bcm_gpio_isrc_unmask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) 87289de2fb6SSvatopluk Kraus { 873c28b681cSSvatopluk Kraus uint32_t bank; 87489de2fb6SSvatopluk Kraus 875c28b681cSSvatopluk Kraus bank = BCM_GPIO_BANK(bgi->bgi_irq); 87689de2fb6SSvatopluk Kraus BCM_GPIO_LOCK(sc); 877c28b681cSSvatopluk Kraus switch (bgi->bgi_mode) { 878c28b681cSSvatopluk Kraus case GPIO_INTR_LEVEL_LOW: 879c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask); 880c28b681cSSvatopluk Kraus break; 881c28b681cSSvatopluk Kraus case GPIO_INTR_LEVEL_HIGH: 882c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask); 883c28b681cSSvatopluk Kraus break; 884c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_RISING: 885c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); 886c28b681cSSvatopluk Kraus break; 887c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_FALLING: 888c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); 889c28b681cSSvatopluk Kraus break; 890c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_BOTH: 891c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); 892c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); 893c28b681cSSvatopluk Kraus break; 894c28b681cSSvatopluk Kraus } 89589de2fb6SSvatopluk Kraus BCM_GPIO_UNLOCK(sc); 89689de2fb6SSvatopluk Kraus } 89789de2fb6SSvatopluk Kraus 89889de2fb6SSvatopluk Kraus static int 89989de2fb6SSvatopluk Kraus bcm_gpio_intr_internal(struct bcm_gpio_softc *sc, uint32_t bank) 90089de2fb6SSvatopluk Kraus { 90189de2fb6SSvatopluk Kraus u_int irq; 90289de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi; 90389de2fb6SSvatopluk Kraus uint32_t reg; 90489de2fb6SSvatopluk Kraus 90589de2fb6SSvatopluk Kraus /* Do not care of spurious interrupt on GPIO. */ 90689de2fb6SSvatopluk Kraus reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank)); 90789de2fb6SSvatopluk Kraus while (reg != 0) { 90889de2fb6SSvatopluk Kraus irq = BCM_GPIO_PINS_PER_BANK * bank + ffs(reg) - 1; 90989de2fb6SSvatopluk Kraus bgi = sc->sc_isrcs + irq; 91089de2fb6SSvatopluk Kraus if (!bcm_gpio_isrc_is_level(bgi)) 91189de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(sc, bgi); 91289de2fb6SSvatopluk Kraus if (intr_isrc_dispatch(&bgi->bgi_isrc, 91389de2fb6SSvatopluk Kraus curthread->td_intr_frame) != 0) { 91489de2fb6SSvatopluk Kraus bcm_gpio_isrc_mask(sc, bgi); 91589de2fb6SSvatopluk Kraus if (bcm_gpio_isrc_is_level(bgi)) 91689de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(sc, bgi); 91789de2fb6SSvatopluk Kraus device_printf(sc->sc_dev, "Stray irq %u disabled\n", 91889de2fb6SSvatopluk Kraus irq); 91989de2fb6SSvatopluk Kraus } 92089de2fb6SSvatopluk Kraus reg &= ~bgi->bgi_mask; 92189de2fb6SSvatopluk Kraus } 92289de2fb6SSvatopluk Kraus return (FILTER_HANDLED); 92389de2fb6SSvatopluk Kraus } 92489de2fb6SSvatopluk Kraus 92589de2fb6SSvatopluk Kraus static int 92689de2fb6SSvatopluk Kraus bcm_gpio_intr_bank0(void *arg) 92789de2fb6SSvatopluk Kraus { 92889de2fb6SSvatopluk Kraus 92989de2fb6SSvatopluk Kraus return (bcm_gpio_intr_internal(arg, 0)); 93089de2fb6SSvatopluk Kraus } 93189de2fb6SSvatopluk Kraus 93289de2fb6SSvatopluk Kraus static int 93389de2fb6SSvatopluk Kraus bcm_gpio_intr_bank1(void *arg) 93489de2fb6SSvatopluk Kraus { 93589de2fb6SSvatopluk Kraus 93689de2fb6SSvatopluk Kraus return (bcm_gpio_intr_internal(arg, 1)); 93789de2fb6SSvatopluk Kraus } 93889de2fb6SSvatopluk Kraus 93989de2fb6SSvatopluk Kraus static int 94089de2fb6SSvatopluk Kraus bcm_gpio_pic_attach(struct bcm_gpio_softc *sc) 94189de2fb6SSvatopluk Kraus { 94289de2fb6SSvatopluk Kraus int error; 94389de2fb6SSvatopluk Kraus uint32_t irq; 94489de2fb6SSvatopluk Kraus const char *name; 94589de2fb6SSvatopluk Kraus 94689de2fb6SSvatopluk Kraus name = device_get_nameunit(sc->sc_dev); 94789de2fb6SSvatopluk Kraus for (irq = 0; irq < BCM_GPIO_PINS; irq++) { 94889de2fb6SSvatopluk Kraus sc->sc_isrcs[irq].bgi_irq = irq; 94989de2fb6SSvatopluk Kraus sc->sc_isrcs[irq].bgi_mask = BCM_GPIO_MASK(irq); 950c28b681cSSvatopluk Kraus sc->sc_isrcs[irq].bgi_mode = GPIO_INTR_CONFORM; 95189de2fb6SSvatopluk Kraus 95289de2fb6SSvatopluk Kraus error = intr_isrc_register(&sc->sc_isrcs[irq].bgi_isrc, 95389de2fb6SSvatopluk Kraus sc->sc_dev, 0, "%s,%u", name, irq); 95489de2fb6SSvatopluk Kraus if (error != 0) 95589de2fb6SSvatopluk Kraus return (error); /* XXX deregister ISRCs */ 95689de2fb6SSvatopluk Kraus } 9579346e913SAndrew Turner if (intr_pic_register(sc->sc_dev, 9589346e913SAndrew Turner OF_xref_from_node(ofw_bus_get_node(sc->sc_dev))) == NULL) 9599346e913SAndrew Turner return (ENXIO); 9609346e913SAndrew Turner 9619346e913SAndrew Turner return (0); 96289de2fb6SSvatopluk Kraus } 96389de2fb6SSvatopluk Kraus 96489de2fb6SSvatopluk Kraus static int 96589de2fb6SSvatopluk Kraus bcm_gpio_pic_detach(struct bcm_gpio_softc *sc) 96689de2fb6SSvatopluk Kraus { 96789de2fb6SSvatopluk Kraus 96889de2fb6SSvatopluk Kraus /* 96989de2fb6SSvatopluk Kraus * There has not been established any procedure yet 97089de2fb6SSvatopluk Kraus * how to detach PIC from living system correctly. 97189de2fb6SSvatopluk Kraus */ 97289de2fb6SSvatopluk Kraus device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__); 97389de2fb6SSvatopluk Kraus return (EBUSY); 97489de2fb6SSvatopluk Kraus } 97589de2fb6SSvatopluk Kraus 97689de2fb6SSvatopluk Kraus static void 977c28b681cSSvatopluk Kraus bcm_gpio_pic_config_intr(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi, 978c28b681cSSvatopluk Kraus uint32_t mode) 979c28b681cSSvatopluk Kraus { 980c28b681cSSvatopluk Kraus uint32_t bank; 981c28b681cSSvatopluk Kraus 982c28b681cSSvatopluk Kraus bank = BCM_GPIO_BANK(bgi->bgi_irq); 983c28b681cSSvatopluk Kraus BCM_GPIO_LOCK(sc); 984c28b681cSSvatopluk Kraus bcm_gpio_modify(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask, 985c28b681cSSvatopluk Kraus mode == GPIO_INTR_EDGE_RISING || mode == GPIO_INTR_EDGE_BOTH); 986c28b681cSSvatopluk Kraus bcm_gpio_modify(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask, 987c28b681cSSvatopluk Kraus mode == GPIO_INTR_EDGE_FALLING || mode == GPIO_INTR_EDGE_BOTH); 988c28b681cSSvatopluk Kraus bcm_gpio_modify(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask, 989c28b681cSSvatopluk Kraus mode == GPIO_INTR_LEVEL_HIGH); 990c28b681cSSvatopluk Kraus bcm_gpio_modify(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask, 991c28b681cSSvatopluk Kraus mode == GPIO_INTR_LEVEL_LOW); 992c28b681cSSvatopluk Kraus bgi->bgi_mode = mode; 993c28b681cSSvatopluk Kraus BCM_GPIO_UNLOCK(sc); 994c28b681cSSvatopluk Kraus } 995c28b681cSSvatopluk Kraus 996c28b681cSSvatopluk Kraus static void 99789de2fb6SSvatopluk Kraus bcm_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) 99889de2fb6SSvatopluk Kraus { 99989de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 100089de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 100189de2fb6SSvatopluk Kraus 100289de2fb6SSvatopluk Kraus bcm_gpio_isrc_mask(sc, bgi); 100389de2fb6SSvatopluk Kraus } 100489de2fb6SSvatopluk Kraus 100589de2fb6SSvatopluk Kraus static void 100689de2fb6SSvatopluk Kraus bcm_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) 100789de2fb6SSvatopluk Kraus { 100889de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 100989de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 101089de2fb6SSvatopluk Kraus 101189de2fb6SSvatopluk Kraus arm_irq_memory_barrier(bgi->bgi_irq); 101289de2fb6SSvatopluk Kraus bcm_gpio_isrc_unmask(sc, bgi); 101389de2fb6SSvatopluk Kraus } 101489de2fb6SSvatopluk Kraus 101589de2fb6SSvatopluk Kraus static int 10166247277aSSvatopluk Kraus bcm_gpio_pic_map_fdt(struct bcm_gpio_softc *sc, struct intr_map_data_fdt *daf, 1017c28b681cSSvatopluk Kraus u_int *irqp, uint32_t *modep) 101889de2fb6SSvatopluk Kraus { 101989de2fb6SSvatopluk Kraus u_int irq; 1020c28b681cSSvatopluk Kraus uint32_t mode, bank; 102189de2fb6SSvatopluk Kraus 102289de2fb6SSvatopluk Kraus /* 102389de2fb6SSvatopluk Kraus * The first cell is the interrupt number. 102489de2fb6SSvatopluk Kraus * The second cell is used to specify flags: 102589de2fb6SSvatopluk Kraus * bits[3:0] trigger type and level flags: 102689de2fb6SSvatopluk Kraus * 1 = low-to-high edge triggered. 102789de2fb6SSvatopluk Kraus * 2 = high-to-low edge triggered. 102889de2fb6SSvatopluk Kraus * 4 = active high level-sensitive. 102989de2fb6SSvatopluk Kraus * 8 = active low level-sensitive. 103089de2fb6SSvatopluk Kraus */ 10316247277aSSvatopluk Kraus if (daf->ncells != 2) 103289de2fb6SSvatopluk Kraus return (EINVAL); 103389de2fb6SSvatopluk Kraus 10346247277aSSvatopluk Kraus irq = daf->cells[0]; 103589de2fb6SSvatopluk Kraus if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq)) 103689de2fb6SSvatopluk Kraus return (EINVAL); 103789de2fb6SSvatopluk Kraus 1038c28b681cSSvatopluk Kraus /* Only reasonable modes are supported. */ 103989de2fb6SSvatopluk Kraus bank = BCM_GPIO_BANK(irq); 10406247277aSSvatopluk Kraus if (daf->cells[1] == 1) 1041c28b681cSSvatopluk Kraus mode = GPIO_INTR_EDGE_RISING; 10426247277aSSvatopluk Kraus else if (daf->cells[1] == 2) 1043c28b681cSSvatopluk Kraus mode = GPIO_INTR_EDGE_FALLING; 10446247277aSSvatopluk Kraus else if (daf->cells[1] == 3) 1045c28b681cSSvatopluk Kraus mode = GPIO_INTR_EDGE_BOTH; 10466247277aSSvatopluk Kraus else if (daf->cells[1] == 4) 1047c28b681cSSvatopluk Kraus mode = GPIO_INTR_LEVEL_HIGH; 10486247277aSSvatopluk Kraus else if (daf->cells[1] == 8) 1049c28b681cSSvatopluk Kraus mode = GPIO_INTR_LEVEL_LOW; 105089de2fb6SSvatopluk Kraus else 105189de2fb6SSvatopluk Kraus return (EINVAL); 105289de2fb6SSvatopluk Kraus 105389de2fb6SSvatopluk Kraus *irqp = irq; 1054c28b681cSSvatopluk Kraus if (modep != NULL) 1055c28b681cSSvatopluk Kraus *modep = mode; 105689de2fb6SSvatopluk Kraus return (0); 105789de2fb6SSvatopluk Kraus } 105889de2fb6SSvatopluk Kraus 105989de2fb6SSvatopluk Kraus static int 10606247277aSSvatopluk Kraus bcm_gpio_pic_map_gpio(struct bcm_gpio_softc *sc, struct intr_map_data_gpio *dag, 10616247277aSSvatopluk Kraus u_int *irqp, uint32_t *modep) 10626247277aSSvatopluk Kraus { 10636247277aSSvatopluk Kraus u_int irq; 10646247277aSSvatopluk Kraus uint32_t mode; 10656247277aSSvatopluk Kraus 10666247277aSSvatopluk Kraus irq = dag->gpio_pin_num; 10676247277aSSvatopluk Kraus if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq)) 10686247277aSSvatopluk Kraus return (EINVAL); 10696247277aSSvatopluk Kraus 10706247277aSSvatopluk Kraus mode = dag->gpio_intr_mode; 10716247277aSSvatopluk Kraus if (mode != GPIO_INTR_LEVEL_LOW && mode != GPIO_INTR_LEVEL_HIGH && 10726247277aSSvatopluk Kraus mode != GPIO_INTR_EDGE_RISING && mode != GPIO_INTR_EDGE_FALLING && 10736247277aSSvatopluk Kraus mode != GPIO_INTR_EDGE_BOTH) 10746247277aSSvatopluk Kraus return (EINVAL); 10756247277aSSvatopluk Kraus 10766247277aSSvatopluk Kraus *irqp = irq; 10776247277aSSvatopluk Kraus if (modep != NULL) 10786247277aSSvatopluk Kraus *modep = mode; 10796247277aSSvatopluk Kraus return (0); 10806247277aSSvatopluk Kraus } 10816247277aSSvatopluk Kraus 10826247277aSSvatopluk Kraus static int 10836247277aSSvatopluk Kraus bcm_gpio_pic_map(struct bcm_gpio_softc *sc, struct intr_map_data *data, 10846247277aSSvatopluk Kraus u_int *irqp, uint32_t *modep) 10856247277aSSvatopluk Kraus { 10866247277aSSvatopluk Kraus 10876247277aSSvatopluk Kraus switch (data->type) { 10886247277aSSvatopluk Kraus case INTR_MAP_DATA_FDT: 10896247277aSSvatopluk Kraus return (bcm_gpio_pic_map_fdt(sc, 10906247277aSSvatopluk Kraus (struct intr_map_data_fdt *)data, irqp, modep)); 10916247277aSSvatopluk Kraus case INTR_MAP_DATA_GPIO: 10926247277aSSvatopluk Kraus return (bcm_gpio_pic_map_gpio(sc, 10936247277aSSvatopluk Kraus (struct intr_map_data_gpio *)data, irqp, modep)); 10946247277aSSvatopluk Kraus default: 10956247277aSSvatopluk Kraus return (ENOTSUP); 10966247277aSSvatopluk Kraus } 10976247277aSSvatopluk Kraus } 10986247277aSSvatopluk Kraus 10996247277aSSvatopluk Kraus static int 110089de2fb6SSvatopluk Kraus bcm_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, 110189de2fb6SSvatopluk Kraus struct intr_irqsrc **isrcp) 110289de2fb6SSvatopluk Kraus { 110389de2fb6SSvatopluk Kraus int error; 110489de2fb6SSvatopluk Kraus u_int irq; 11056247277aSSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 110689de2fb6SSvatopluk Kraus 11076247277aSSvatopluk Kraus error = bcm_gpio_pic_map(sc, data, &irq, NULL); 110889de2fb6SSvatopluk Kraus if (error == 0) 110989de2fb6SSvatopluk Kraus *isrcp = &sc->sc_isrcs[irq].bgi_isrc; 111089de2fb6SSvatopluk Kraus return (error); 111189de2fb6SSvatopluk Kraus } 111289de2fb6SSvatopluk Kraus 111389de2fb6SSvatopluk Kraus static void 111489de2fb6SSvatopluk Kraus bcm_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) 111589de2fb6SSvatopluk Kraus { 111689de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 111789de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 111889de2fb6SSvatopluk Kraus 111989de2fb6SSvatopluk Kraus if (bcm_gpio_isrc_is_level(bgi)) 112089de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(sc, bgi); 112189de2fb6SSvatopluk Kraus } 112289de2fb6SSvatopluk Kraus 112389de2fb6SSvatopluk Kraus static void 112489de2fb6SSvatopluk Kraus bcm_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) 112589de2fb6SSvatopluk Kraus { 112689de2fb6SSvatopluk Kraus 112789de2fb6SSvatopluk Kraus bcm_gpio_pic_enable_intr(dev, isrc); 112889de2fb6SSvatopluk Kraus } 112989de2fb6SSvatopluk Kraus 113089de2fb6SSvatopluk Kraus static void 113189de2fb6SSvatopluk Kraus bcm_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) 113289de2fb6SSvatopluk Kraus { 113389de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 113489de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 113589de2fb6SSvatopluk Kraus 113689de2fb6SSvatopluk Kraus bcm_gpio_isrc_mask(sc, bgi); 113789de2fb6SSvatopluk Kraus if (bcm_gpio_isrc_is_level(bgi)) 113889de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(sc, bgi); 113989de2fb6SSvatopluk Kraus } 114089de2fb6SSvatopluk Kraus 114189de2fb6SSvatopluk Kraus static int 114289de2fb6SSvatopluk Kraus bcm_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, 114389de2fb6SSvatopluk Kraus struct resource *res, struct intr_map_data *data) 114489de2fb6SSvatopluk Kraus { 114589de2fb6SSvatopluk Kraus u_int irq; 1146c28b681cSSvatopluk Kraus uint32_t mode; 114789de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc; 114889de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi; 114989de2fb6SSvatopluk Kraus 11506247277aSSvatopluk Kraus if (data == NULL) 115189de2fb6SSvatopluk Kraus return (ENOTSUP); 115289de2fb6SSvatopluk Kraus 115389de2fb6SSvatopluk Kraus sc = device_get_softc(dev); 115489de2fb6SSvatopluk Kraus bgi = (struct bcm_gpio_irqsrc *)isrc; 115589de2fb6SSvatopluk Kraus 115689de2fb6SSvatopluk Kraus /* Get and check config for an interrupt. */ 11576247277aSSvatopluk Kraus if (bcm_gpio_pic_map(sc, data, &irq, &mode) != 0 || bgi->bgi_irq != irq) 115889de2fb6SSvatopluk Kraus return (EINVAL); 115989de2fb6SSvatopluk Kraus 116089de2fb6SSvatopluk Kraus /* 116189de2fb6SSvatopluk Kraus * If this is a setup for another handler, 116289de2fb6SSvatopluk Kraus * only check that its configuration match. 116389de2fb6SSvatopluk Kraus */ 116489de2fb6SSvatopluk Kraus if (isrc->isrc_handlers != 0) 1165c28b681cSSvatopluk Kraus return (bgi->bgi_mode == mode ? 0 : EINVAL); 116689de2fb6SSvatopluk Kraus 1167c28b681cSSvatopluk Kraus bcm_gpio_pic_config_intr(sc, bgi, mode); 116889de2fb6SSvatopluk Kraus return (0); 116989de2fb6SSvatopluk Kraus } 117089de2fb6SSvatopluk Kraus 117189de2fb6SSvatopluk Kraus static int 117289de2fb6SSvatopluk Kraus bcm_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, 117389de2fb6SSvatopluk Kraus struct resource *res, struct intr_map_data *data) 117489de2fb6SSvatopluk Kraus { 117589de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 117689de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 117789de2fb6SSvatopluk Kraus 1178c28b681cSSvatopluk Kraus if (isrc->isrc_handlers == 0) 1179c28b681cSSvatopluk Kraus bcm_gpio_pic_config_intr(sc, bgi, GPIO_INTR_CONFORM); 118089de2fb6SSvatopluk Kraus return (0); 118189de2fb6SSvatopluk Kraus } 118289de2fb6SSvatopluk Kraus 11838c705c2cSLuiz Otavio O Souza static phandle_t 11848c705c2cSLuiz Otavio O Souza bcm_gpio_get_node(device_t bus, device_t dev) 11858c705c2cSLuiz Otavio O Souza { 11868c705c2cSLuiz Otavio O Souza 11878c705c2cSLuiz Otavio O Souza /* We only have one child, the GPIO bus, which needs our own node. */ 11888c705c2cSLuiz Otavio O Souza return (ofw_bus_get_node(bus)); 11898c705c2cSLuiz Otavio O Souza } 11908c705c2cSLuiz Otavio O Souza 11914063f925SOleksandr Tymoshenko static device_method_t bcm_gpio_methods[] = { 11924063f925SOleksandr Tymoshenko /* Device interface */ 11934063f925SOleksandr Tymoshenko DEVMETHOD(device_probe, bcm_gpio_probe), 11944063f925SOleksandr Tymoshenko DEVMETHOD(device_attach, bcm_gpio_attach), 11954063f925SOleksandr Tymoshenko DEVMETHOD(device_detach, bcm_gpio_detach), 11964063f925SOleksandr Tymoshenko 11974063f925SOleksandr Tymoshenko /* GPIO protocol */ 11987836352bSLuiz Otavio O Souza DEVMETHOD(gpio_get_bus, bcm_gpio_get_bus), 11994063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_max, bcm_gpio_pin_max), 12004063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getname, bcm_gpio_pin_getname), 12014063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getflags, bcm_gpio_pin_getflags), 12024063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getcaps, bcm_gpio_pin_getcaps), 12034063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_setflags, bcm_gpio_pin_setflags), 12044063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_get, bcm_gpio_pin_get), 12054063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set), 12064063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle), 12074063f925SOleksandr Tymoshenko 120889de2fb6SSvatopluk Kraus /* Interrupt controller interface */ 120989de2fb6SSvatopluk Kraus DEVMETHOD(pic_disable_intr, bcm_gpio_pic_disable_intr), 121089de2fb6SSvatopluk Kraus DEVMETHOD(pic_enable_intr, bcm_gpio_pic_enable_intr), 121189de2fb6SSvatopluk Kraus DEVMETHOD(pic_map_intr, bcm_gpio_pic_map_intr), 121289de2fb6SSvatopluk Kraus DEVMETHOD(pic_post_filter, bcm_gpio_pic_post_filter), 121389de2fb6SSvatopluk Kraus DEVMETHOD(pic_post_ithread, bcm_gpio_pic_post_ithread), 121489de2fb6SSvatopluk Kraus DEVMETHOD(pic_pre_ithread, bcm_gpio_pic_pre_ithread), 121589de2fb6SSvatopluk Kraus DEVMETHOD(pic_setup_intr, bcm_gpio_pic_setup_intr), 121689de2fb6SSvatopluk Kraus DEVMETHOD(pic_teardown_intr, bcm_gpio_pic_teardown_intr), 1217feabce61SAndrew Turner 12188c705c2cSLuiz Otavio O Souza /* ofw_bus interface */ 12198c705c2cSLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node), 12208c705c2cSLuiz Otavio O Souza 12214063f925SOleksandr Tymoshenko DEVMETHOD_END 12224063f925SOleksandr Tymoshenko }; 12234063f925SOleksandr Tymoshenko 12244063f925SOleksandr Tymoshenko static devclass_t bcm_gpio_devclass; 12254063f925SOleksandr Tymoshenko 12264063f925SOleksandr Tymoshenko static driver_t bcm_gpio_driver = { 12274063f925SOleksandr Tymoshenko "gpio", 12284063f925SOleksandr Tymoshenko bcm_gpio_methods, 12294063f925SOleksandr Tymoshenko sizeof(struct bcm_gpio_softc), 12304063f925SOleksandr Tymoshenko }; 12314063f925SOleksandr Tymoshenko 12324063f925SOleksandr Tymoshenko DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0); 1233