14063f925SOleksandr Tymoshenko /*- 25508fc88SLuiz Otavio O Souza * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@FreeBSD.org> 35508fc88SLuiz Otavio O Souza * Copyright (c) 2012-2015 Luiz Otavio O Souza <loos@FreeBSD.org> 44063f925SOleksandr Tymoshenko * All rights reserved. 54063f925SOleksandr Tymoshenko * 64063f925SOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without 74063f925SOleksandr Tymoshenko * modification, are permitted provided that the following conditions 84063f925SOleksandr Tymoshenko * are met: 94063f925SOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright 104063f925SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer. 114063f925SOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright 124063f925SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the 134063f925SOleksandr Tymoshenko * documentation and/or other materials provided with the distribution. 144063f925SOleksandr Tymoshenko * 154063f925SOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 164063f925SOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 174063f925SOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 184063f925SOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 194063f925SOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 204063f925SOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 214063f925SOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 224063f925SOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 234063f925SOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 244063f925SOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 254063f925SOleksandr Tymoshenko * SUCH DAMAGE. 264063f925SOleksandr Tymoshenko * 274063f925SOleksandr Tymoshenko */ 284063f925SOleksandr Tymoshenko #include <sys/cdefs.h> 294063f925SOleksandr Tymoshenko __FBSDID("$FreeBSD$"); 304063f925SOleksandr Tymoshenko 3189de2fb6SSvatopluk Kraus #include "opt_platform.h" 3289de2fb6SSvatopluk Kraus 334063f925SOleksandr Tymoshenko #include <sys/param.h> 344063f925SOleksandr Tymoshenko #include <sys/systm.h> 354063f925SOleksandr Tymoshenko #include <sys/bus.h> 364063f925SOleksandr Tymoshenko #include <sys/gpio.h> 3712471cecSLuiz Otavio O Souza #include <sys/interrupt.h> 38e74d6e2aSLuiz Otavio O Souza #include <sys/kernel.h> 39e74d6e2aSLuiz Otavio O Souza #include <sys/lock.h> 40e74d6e2aSLuiz Otavio O Souza #include <sys/module.h> 41e74d6e2aSLuiz Otavio O Souza #include <sys/mutex.h> 4289de2fb6SSvatopluk Kraus #include <sys/proc.h> 43e74d6e2aSLuiz Otavio O Souza #include <sys/rman.h> 4490576f54SOleksandr Tymoshenko #include <sys/sysctl.h> 454063f925SOleksandr Tymoshenko 464063f925SOleksandr Tymoshenko #include <machine/bus.h> 4789de2fb6SSvatopluk Kraus #include <machine/intr.h> 484063f925SOleksandr Tymoshenko 497836352bSLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h> 504063f925SOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h> 514063f925SOleksandr Tymoshenko 5244d06d8dSLuiz Otavio O Souza #include <arm/broadcom/bcm2835/bcm2835_gpio.h> 5344d06d8dSLuiz Otavio O Souza 544063f925SOleksandr Tymoshenko #include "gpio_if.h" 554063f925SOleksandr Tymoshenko 5659c3cb81SAndrew Turner #ifdef INTRNG 5789de2fb6SSvatopluk Kraus #include "pic_if.h" 5889de2fb6SSvatopluk Kraus #endif 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 #ifdef INTRNG 72f7f2b2fbSSvatopluk Kraus #define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ 73f7f2b2fbSSvatopluk Kraus GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_INTR_LEVEL_LOW | \ 74f7f2b2fbSSvatopluk Kraus GPIO_INTR_LEVEL_HIGH | GPIO_INTR_EDGE_RISING | \ 75f7f2b2fbSSvatopluk Kraus GPIO_INTR_EDGE_FALLING | GPIO_INTR_EDGE_BOTH) 76f7f2b2fbSSvatopluk Kraus #else 774063f925SOleksandr Tymoshenko #define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ 784063f925SOleksandr Tymoshenko GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) 79f7f2b2fbSSvatopluk Kraus #endif 804063f925SOleksandr Tymoshenko 81c2136589SLuiz Otavio O Souza static struct resource_spec bcm_gpio_res_spec[] = { 82c2136589SLuiz Otavio O Souza { SYS_RES_MEMORY, 0, RF_ACTIVE }, 8389de2fb6SSvatopluk Kraus { SYS_RES_IRQ, 0, RF_ACTIVE }, /* bank 0 interrupt */ 8489de2fb6SSvatopluk Kraus { SYS_RES_IRQ, 1, RF_ACTIVE }, /* bank 1 interrupt */ 85c2136589SLuiz Otavio O Souza { -1, 0, 0 } 86c2136589SLuiz Otavio O Souza }; 87c2136589SLuiz Otavio O Souza 8890576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl { 8990576f54SOleksandr Tymoshenko struct bcm_gpio_softc *sc; 9090576f54SOleksandr Tymoshenko uint32_t pin; 9190576f54SOleksandr Tymoshenko }; 9290576f54SOleksandr Tymoshenko 9359c3cb81SAndrew Turner #ifdef INTRNG 9489de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc { 9589de2fb6SSvatopluk Kraus struct intr_irqsrc bgi_isrc; 9689de2fb6SSvatopluk Kraus uint32_t bgi_irq; 97c28b681cSSvatopluk Kraus uint32_t bgi_mode; 9889de2fb6SSvatopluk Kraus uint32_t bgi_mask; 9989de2fb6SSvatopluk Kraus }; 10089de2fb6SSvatopluk Kraus #endif 10189de2fb6SSvatopluk Kraus 1024063f925SOleksandr Tymoshenko struct bcm_gpio_softc { 1034063f925SOleksandr Tymoshenko device_t sc_dev; 1047836352bSLuiz Otavio O Souza device_t sc_busdev; 1054063f925SOleksandr Tymoshenko struct mtx sc_mtx; 106c2136589SLuiz Otavio O Souza struct resource * sc_res[BCM_GPIO_IRQS + 1]; 1074063f925SOleksandr Tymoshenko bus_space_tag_t sc_bst; 1084063f925SOleksandr Tymoshenko bus_space_handle_t sc_bsh; 10912471cecSLuiz Otavio O Souza void * sc_intrhand[BCM_GPIO_IRQS]; 1104063f925SOleksandr Tymoshenko int sc_gpio_npins; 1114063f925SOleksandr Tymoshenko int sc_ro_npins; 1124063f925SOleksandr Tymoshenko int sc_ro_pins[BCM_GPIO_PINS]; 1134063f925SOleksandr Tymoshenko struct gpio_pin sc_gpio_pins[BCM_GPIO_PINS]; 11459c3cb81SAndrew Turner #ifndef INTRNG 11512471cecSLuiz Otavio O Souza struct intr_event * sc_events[BCM_GPIO_PINS]; 11689de2fb6SSvatopluk Kraus #endif 11790576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl sc_sysctl[BCM_GPIO_PINS]; 11859c3cb81SAndrew Turner #ifdef INTRNG 11989de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc sc_isrcs[BCM_GPIO_PINS]; 12089de2fb6SSvatopluk Kraus #else 12112471cecSLuiz Otavio O Souza enum intr_trigger sc_irq_trigger[BCM_GPIO_PINS]; 12212471cecSLuiz Otavio O Souza enum intr_polarity sc_irq_polarity[BCM_GPIO_PINS]; 12389de2fb6SSvatopluk Kraus #endif 1244063f925SOleksandr Tymoshenko }; 1254063f925SOleksandr Tymoshenko 1264063f925SOleksandr Tymoshenko enum bcm_gpio_pud { 1274063f925SOleksandr Tymoshenko BCM_GPIO_NONE, 1284063f925SOleksandr Tymoshenko BCM_GPIO_PULLDOWN, 1294063f925SOleksandr Tymoshenko BCM_GPIO_PULLUP, 1304063f925SOleksandr Tymoshenko }; 1314063f925SOleksandr Tymoshenko 13212471cecSLuiz Otavio O Souza #define BCM_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) 13312471cecSLuiz Otavio O Souza #define BCM_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) 13412471cecSLuiz Otavio O Souza #define BCM_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) 1354063f925SOleksandr Tymoshenko #define BCM_GPIO_WRITE(_sc, _off, _val) \ 13612471cecSLuiz Otavio O Souza bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val) 1374063f925SOleksandr Tymoshenko #define BCM_GPIO_READ(_sc, _off) \ 13812471cecSLuiz Otavio O Souza bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off) 13912471cecSLuiz Otavio O Souza #define BCM_GPIO_CLEAR_BITS(_sc, _off, _bits) \ 14012471cecSLuiz Otavio O Souza BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) & ~(_bits)) 14112471cecSLuiz Otavio O Souza #define BCM_GPIO_SET_BITS(_sc, _off, _bits) \ 14212471cecSLuiz Otavio O Souza BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) | _bits) 14312471cecSLuiz Otavio O Souza #define BCM_GPIO_BANK(a) (a / BCM_GPIO_PINS_PER_BANK) 14412471cecSLuiz Otavio O Souza #define BCM_GPIO_MASK(a) (1U << (a % BCM_GPIO_PINS_PER_BANK)) 14512471cecSLuiz Otavio O Souza 14612471cecSLuiz Otavio O Souza #define BCM_GPIO_GPFSEL(_bank) (0x00 + _bank * 4) /* Function Select */ 14712471cecSLuiz Otavio O Souza #define BCM_GPIO_GPSET(_bank) (0x1c + _bank * 4) /* Pin Out Set */ 14812471cecSLuiz Otavio O Souza #define BCM_GPIO_GPCLR(_bank) (0x28 + _bank * 4) /* Pin Out Clear */ 14912471cecSLuiz Otavio O Souza #define BCM_GPIO_GPLEV(_bank) (0x34 + _bank * 4) /* Pin Level */ 15012471cecSLuiz Otavio O Souza #define BCM_GPIO_GPEDS(_bank) (0x40 + _bank * 4) /* Event Status */ 15112471cecSLuiz Otavio O Souza #define BCM_GPIO_GPREN(_bank) (0x4c + _bank * 4) /* Rising Edge irq */ 15212471cecSLuiz Otavio O Souza #define BCM_GPIO_GPFEN(_bank) (0x58 + _bank * 4) /* Falling Edge irq */ 15312471cecSLuiz Otavio O Souza #define BCM_GPIO_GPHEN(_bank) (0x64 + _bank * 4) /* High Level irq */ 15412471cecSLuiz Otavio O Souza #define BCM_GPIO_GPLEN(_bank) (0x70 + _bank * 4) /* Low Level irq */ 15512471cecSLuiz Otavio O Souza #define BCM_GPIO_GPAREN(_bank) (0x7c + _bank * 4) /* Async Rising Edge */ 15612471cecSLuiz Otavio O Souza #define BCM_GPIO_GPAFEN(_bank) (0x88 + _bank * 4) /* Async Falling Egde */ 15712471cecSLuiz Otavio O Souza #define BCM_GPIO_GPPUD(_bank) (0x94) /* Pin Pull up/down */ 15812471cecSLuiz Otavio O Souza #define BCM_GPIO_GPPUDCLK(_bank) (0x98 + _bank * 4) /* Pin Pull up clock */ 15912471cecSLuiz Otavio O Souza 160aa2959baSOleksandr Tymoshenko static struct ofw_compat_data compat_data[] = { 161aa2959baSOleksandr Tymoshenko {"broadcom,bcm2835-gpio", 1}, 162aa2959baSOleksandr Tymoshenko {"brcm,bcm2835-gpio", 1}, 163aa2959baSOleksandr Tymoshenko {NULL, 0} 164aa2959baSOleksandr Tymoshenko }; 165aa2959baSOleksandr Tymoshenko 16612471cecSLuiz Otavio O Souza static struct bcm_gpio_softc *bcm_gpio_sc = NULL; 1674063f925SOleksandr Tymoshenko 16859c3cb81SAndrew Turner #ifdef INTRNG 16989de2fb6SSvatopluk Kraus static int bcm_gpio_intr_bank0(void *arg); 17089de2fb6SSvatopluk Kraus static int bcm_gpio_intr_bank1(void *arg); 17189de2fb6SSvatopluk Kraus static int bcm_gpio_pic_attach(struct bcm_gpio_softc *sc); 17289de2fb6SSvatopluk Kraus static int bcm_gpio_pic_detach(struct bcm_gpio_softc *sc); 17389de2fb6SSvatopluk Kraus #endif 17489de2fb6SSvatopluk Kraus 1754063f925SOleksandr Tymoshenko static int 1764063f925SOleksandr Tymoshenko bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin) 1774063f925SOleksandr Tymoshenko { 1784063f925SOleksandr Tymoshenko int i; 1794063f925SOleksandr Tymoshenko 1804063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_ro_npins; i++) 1814063f925SOleksandr Tymoshenko if (pin == sc->sc_ro_pins[i]) 1824063f925SOleksandr Tymoshenko return (1); 1834063f925SOleksandr Tymoshenko return (0); 1844063f925SOleksandr Tymoshenko } 1854063f925SOleksandr Tymoshenko 1864063f925SOleksandr Tymoshenko static uint32_t 1874063f925SOleksandr Tymoshenko bcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin) 1884063f925SOleksandr Tymoshenko { 18990576f54SOleksandr Tymoshenko uint32_t bank, func, offset; 1904063f925SOleksandr Tymoshenko 1914063f925SOleksandr Tymoshenko /* Five banks, 10 pins per bank, 3 bits per pin. */ 1924063f925SOleksandr Tymoshenko bank = pin / 10; 1934063f925SOleksandr Tymoshenko offset = (pin - bank * 10) * 3; 1944063f925SOleksandr Tymoshenko 1954063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 19690576f54SOleksandr Tymoshenko func = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7; 1974063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 1984063f925SOleksandr Tymoshenko 19990576f54SOleksandr Tymoshenko return (func); 20090576f54SOleksandr Tymoshenko } 20190576f54SOleksandr Tymoshenko 20290576f54SOleksandr Tymoshenko static void 20390576f54SOleksandr Tymoshenko bcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize) 20490576f54SOleksandr Tymoshenko { 20590576f54SOleksandr Tymoshenko 20690576f54SOleksandr Tymoshenko switch (nfunc) { 2074063f925SOleksandr Tymoshenko case BCM_GPIO_INPUT: 20890576f54SOleksandr Tymoshenko strncpy(buf, "input", bufsize); 2094063f925SOleksandr Tymoshenko break; 2104063f925SOleksandr Tymoshenko case BCM_GPIO_OUTPUT: 21190576f54SOleksandr Tymoshenko strncpy(buf, "output", bufsize); 2124063f925SOleksandr Tymoshenko break; 2134063f925SOleksandr Tymoshenko case BCM_GPIO_ALT0: 21490576f54SOleksandr Tymoshenko strncpy(buf, "alt0", bufsize); 2154063f925SOleksandr Tymoshenko break; 2164063f925SOleksandr Tymoshenko case BCM_GPIO_ALT1: 21790576f54SOleksandr Tymoshenko strncpy(buf, "alt1", bufsize); 2184063f925SOleksandr Tymoshenko break; 2194063f925SOleksandr Tymoshenko case BCM_GPIO_ALT2: 22090576f54SOleksandr Tymoshenko strncpy(buf, "alt2", bufsize); 2214063f925SOleksandr Tymoshenko break; 2224063f925SOleksandr Tymoshenko case BCM_GPIO_ALT3: 22390576f54SOleksandr Tymoshenko strncpy(buf, "alt3", bufsize); 2244063f925SOleksandr Tymoshenko break; 2254063f925SOleksandr Tymoshenko case BCM_GPIO_ALT4: 22690576f54SOleksandr Tymoshenko strncpy(buf, "alt4", bufsize); 2274063f925SOleksandr Tymoshenko break; 2284063f925SOleksandr Tymoshenko case BCM_GPIO_ALT5: 22990576f54SOleksandr Tymoshenko strncpy(buf, "alt5", bufsize); 2304063f925SOleksandr Tymoshenko break; 23190576f54SOleksandr Tymoshenko default: 23290576f54SOleksandr Tymoshenko strncpy(buf, "invalid", bufsize); 2334063f925SOleksandr Tymoshenko } 23490576f54SOleksandr Tymoshenko } 2354063f925SOleksandr Tymoshenko 23690576f54SOleksandr Tymoshenko static int 23790576f54SOleksandr Tymoshenko bcm_gpio_str_func(char *func, uint32_t *nfunc) 23890576f54SOleksandr Tymoshenko { 23990576f54SOleksandr Tymoshenko 24090576f54SOleksandr Tymoshenko if (strcasecmp(func, "input") == 0) 24190576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_INPUT; 24290576f54SOleksandr Tymoshenko else if (strcasecmp(func, "output") == 0) 24390576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_OUTPUT; 24490576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt0") == 0) 24590576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT0; 24690576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt1") == 0) 24790576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT1; 24890576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt2") == 0) 24990576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT2; 25090576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt3") == 0) 25190576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT3; 25290576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt4") == 0) 25390576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT4; 25490576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt5") == 0) 25590576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT5; 25690576f54SOleksandr Tymoshenko else 25790576f54SOleksandr Tymoshenko return (-1); 25890576f54SOleksandr Tymoshenko 25990576f54SOleksandr Tymoshenko return (0); 26090576f54SOleksandr Tymoshenko } 26190576f54SOleksandr Tymoshenko 26290576f54SOleksandr Tymoshenko static uint32_t 26390576f54SOleksandr Tymoshenko bcm_gpio_func_flag(uint32_t nfunc) 26490576f54SOleksandr Tymoshenko { 26590576f54SOleksandr Tymoshenko 26690576f54SOleksandr Tymoshenko switch (nfunc) { 2674063f925SOleksandr Tymoshenko case BCM_GPIO_INPUT: 2684063f925SOleksandr Tymoshenko return (GPIO_PIN_INPUT); 2694063f925SOleksandr Tymoshenko case BCM_GPIO_OUTPUT: 2704063f925SOleksandr Tymoshenko return (GPIO_PIN_OUTPUT); 2714063f925SOleksandr Tymoshenko } 2724063f925SOleksandr Tymoshenko return (0); 2734063f925SOleksandr Tymoshenko } 2744063f925SOleksandr Tymoshenko 2754063f925SOleksandr Tymoshenko static void 2764063f925SOleksandr Tymoshenko bcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f) 2774063f925SOleksandr Tymoshenko { 2784063f925SOleksandr Tymoshenko uint32_t bank, data, offset; 2794063f925SOleksandr Tymoshenko 28090576f54SOleksandr Tymoshenko /* Must be called with lock held. */ 28190576f54SOleksandr Tymoshenko BCM_GPIO_LOCK_ASSERT(sc); 28290576f54SOleksandr Tymoshenko 2834063f925SOleksandr Tymoshenko /* Five banks, 10 pins per bank, 3 bits per pin. */ 2844063f925SOleksandr Tymoshenko bank = pin / 10; 2854063f925SOleksandr Tymoshenko offset = (pin - bank * 10) * 3; 2864063f925SOleksandr Tymoshenko 2874063f925SOleksandr Tymoshenko data = BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)); 2884063f925SOleksandr Tymoshenko data &= ~(7 << offset); 2894063f925SOleksandr Tymoshenko data |= (f << offset); 2904063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPFSEL(bank), data); 2914063f925SOleksandr Tymoshenko } 2924063f925SOleksandr Tymoshenko 2934063f925SOleksandr Tymoshenko static void 2944063f925SOleksandr Tymoshenko bcm_gpio_set_pud(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t state) 2954063f925SOleksandr Tymoshenko { 2965508fc88SLuiz Otavio O Souza uint32_t bank; 2974063f925SOleksandr Tymoshenko 29890576f54SOleksandr Tymoshenko /* Must be called with lock held. */ 29990576f54SOleksandr Tymoshenko BCM_GPIO_LOCK_ASSERT(sc); 30090576f54SOleksandr Tymoshenko 3015508fc88SLuiz Otavio O Souza bank = BCM_GPIO_BANK(pin); 3024063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state); 3035508fc88SLuiz Otavio O Souza BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), BCM_GPIO_MASK(pin)); 3044063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0); 3054063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0); 3064063f925SOleksandr Tymoshenko } 3074063f925SOleksandr Tymoshenko 30844d06d8dSLuiz Otavio O Souza void 30944d06d8dSLuiz Otavio O Souza bcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc) 31044d06d8dSLuiz Otavio O Souza { 31144d06d8dSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 31244d06d8dSLuiz Otavio O Souza int i; 31344d06d8dSLuiz Otavio O Souza 31444d06d8dSLuiz Otavio O Souza sc = device_get_softc(dev); 31544d06d8dSLuiz Otavio O Souza BCM_GPIO_LOCK(sc); 31644d06d8dSLuiz Otavio O Souza 31744d06d8dSLuiz Otavio O Souza /* Disable pull-up or pull-down on pin. */ 31844d06d8dSLuiz Otavio O Souza bcm_gpio_set_pud(sc, pin, BCM_GPIO_NONE); 31944d06d8dSLuiz Otavio O Souza 32044d06d8dSLuiz Otavio O Souza /* And now set the pin function. */ 32144d06d8dSLuiz Otavio O Souza bcm_gpio_set_function(sc, pin, nfunc); 32244d06d8dSLuiz Otavio O Souza 32344d06d8dSLuiz Otavio O Souza /* Update the pin flags. */ 32444d06d8dSLuiz Otavio O Souza for (i = 0; i < sc->sc_gpio_npins; i++) { 32544d06d8dSLuiz Otavio O Souza if (sc->sc_gpio_pins[i].gp_pin == pin) 32644d06d8dSLuiz Otavio O Souza break; 32744d06d8dSLuiz Otavio O Souza } 32844d06d8dSLuiz Otavio O Souza if (i < sc->sc_gpio_npins) 32944d06d8dSLuiz Otavio O Souza sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc); 33044d06d8dSLuiz Otavio O Souza 33144d06d8dSLuiz Otavio O Souza BCM_GPIO_UNLOCK(sc); 33244d06d8dSLuiz Otavio O Souza } 33344d06d8dSLuiz Otavio O Souza 3344063f925SOleksandr Tymoshenko static void 3354063f925SOleksandr Tymoshenko bcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin, 3364063f925SOleksandr Tymoshenko unsigned int flags) 3374063f925SOleksandr Tymoshenko { 3384063f925SOleksandr Tymoshenko 33990576f54SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 34090576f54SOleksandr Tymoshenko 3414063f925SOleksandr Tymoshenko /* 3424063f925SOleksandr Tymoshenko * Manage input/output. 3434063f925SOleksandr Tymoshenko */ 3444063f925SOleksandr Tymoshenko if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { 3454063f925SOleksandr Tymoshenko pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); 3464063f925SOleksandr Tymoshenko if (flags & GPIO_PIN_OUTPUT) { 3474063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_OUTPUT; 3484063f925SOleksandr Tymoshenko bcm_gpio_set_function(sc, pin->gp_pin, 3494063f925SOleksandr Tymoshenko BCM_GPIO_OUTPUT); 3504063f925SOleksandr Tymoshenko } else { 3514063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_INPUT; 3524063f925SOleksandr Tymoshenko bcm_gpio_set_function(sc, pin->gp_pin, 3534063f925SOleksandr Tymoshenko BCM_GPIO_INPUT); 3544063f925SOleksandr Tymoshenko } 3554063f925SOleksandr Tymoshenko } 3564063f925SOleksandr Tymoshenko 3574063f925SOleksandr Tymoshenko /* Manage Pull-up/pull-down. */ 3584063f925SOleksandr Tymoshenko pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN); 3594063f925SOleksandr Tymoshenko if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) { 3604063f925SOleksandr Tymoshenko if (flags & GPIO_PIN_PULLUP) { 3614063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_PULLUP; 3624063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLUP); 3634063f925SOleksandr Tymoshenko } else { 3644063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_PULLDOWN; 3654063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLDOWN); 3664063f925SOleksandr Tymoshenko } 3674063f925SOleksandr Tymoshenko } else 3684063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE); 36990576f54SOleksandr Tymoshenko 37090576f54SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 3714063f925SOleksandr Tymoshenko } 3724063f925SOleksandr Tymoshenko 3737836352bSLuiz Otavio O Souza static device_t 3747836352bSLuiz Otavio O Souza bcm_gpio_get_bus(device_t dev) 3757836352bSLuiz Otavio O Souza { 3767836352bSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 3777836352bSLuiz Otavio O Souza 3787836352bSLuiz Otavio O Souza sc = device_get_softc(dev); 3797836352bSLuiz Otavio O Souza 3807836352bSLuiz Otavio O Souza return (sc->sc_busdev); 3817836352bSLuiz Otavio O Souza } 3827836352bSLuiz Otavio O Souza 3834063f925SOleksandr Tymoshenko static int 3844063f925SOleksandr Tymoshenko bcm_gpio_pin_max(device_t dev, int *maxpin) 3854063f925SOleksandr Tymoshenko { 3864063f925SOleksandr Tymoshenko 3874063f925SOleksandr Tymoshenko *maxpin = BCM_GPIO_PINS - 1; 3884063f925SOleksandr Tymoshenko return (0); 3894063f925SOleksandr Tymoshenko } 3904063f925SOleksandr Tymoshenko 3914063f925SOleksandr Tymoshenko static int 3924063f925SOleksandr Tymoshenko bcm_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 3934063f925SOleksandr Tymoshenko { 3944063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 3954063f925SOleksandr Tymoshenko int i; 3964063f925SOleksandr Tymoshenko 3974063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 3984063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 3994063f925SOleksandr Tymoshenko break; 4004063f925SOleksandr Tymoshenko } 4014063f925SOleksandr Tymoshenko 4024063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4034063f925SOleksandr Tymoshenko return (EINVAL); 4044063f925SOleksandr Tymoshenko 4054063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4064063f925SOleksandr Tymoshenko *caps = sc->sc_gpio_pins[i].gp_caps; 4074063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4084063f925SOleksandr Tymoshenko 4094063f925SOleksandr Tymoshenko return (0); 4104063f925SOleksandr Tymoshenko } 4114063f925SOleksandr Tymoshenko 4124063f925SOleksandr Tymoshenko static int 4134063f925SOleksandr Tymoshenko bcm_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 4144063f925SOleksandr Tymoshenko { 4154063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4164063f925SOleksandr Tymoshenko int i; 4174063f925SOleksandr Tymoshenko 4184063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4194063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4204063f925SOleksandr Tymoshenko break; 4214063f925SOleksandr Tymoshenko } 4224063f925SOleksandr Tymoshenko 4234063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4244063f925SOleksandr Tymoshenko return (EINVAL); 4254063f925SOleksandr Tymoshenko 4264063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4274063f925SOleksandr Tymoshenko *flags = sc->sc_gpio_pins[i].gp_flags; 4284063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4294063f925SOleksandr Tymoshenko 4304063f925SOleksandr Tymoshenko return (0); 4314063f925SOleksandr Tymoshenko } 4324063f925SOleksandr Tymoshenko 4334063f925SOleksandr Tymoshenko static int 4344063f925SOleksandr Tymoshenko bcm_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 4354063f925SOleksandr Tymoshenko { 4364063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4374063f925SOleksandr Tymoshenko int i; 4384063f925SOleksandr Tymoshenko 4394063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4404063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4414063f925SOleksandr Tymoshenko break; 4424063f925SOleksandr Tymoshenko } 4434063f925SOleksandr Tymoshenko 4444063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4454063f925SOleksandr Tymoshenko return (EINVAL); 4464063f925SOleksandr Tymoshenko 4474063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4484063f925SOleksandr Tymoshenko memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); 4494063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4504063f925SOleksandr Tymoshenko 4514063f925SOleksandr Tymoshenko return (0); 4524063f925SOleksandr Tymoshenko } 4534063f925SOleksandr Tymoshenko 4544063f925SOleksandr Tymoshenko static int 4554063f925SOleksandr Tymoshenko bcm_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 4564063f925SOleksandr Tymoshenko { 4574063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4584063f925SOleksandr Tymoshenko int i; 4594063f925SOleksandr Tymoshenko 4604063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4614063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4624063f925SOleksandr Tymoshenko break; 4634063f925SOleksandr Tymoshenko } 4644063f925SOleksandr Tymoshenko 4654063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4664063f925SOleksandr Tymoshenko return (EINVAL); 4674063f925SOleksandr Tymoshenko 4684063f925SOleksandr Tymoshenko /* We never touch on read-only/reserved pins. */ 4694063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 4704063f925SOleksandr Tymoshenko return (EINVAL); 4714063f925SOleksandr Tymoshenko 4724063f925SOleksandr Tymoshenko bcm_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); 4734063f925SOleksandr Tymoshenko 4744063f925SOleksandr Tymoshenko return (0); 4754063f925SOleksandr Tymoshenko } 4764063f925SOleksandr Tymoshenko 4774063f925SOleksandr Tymoshenko static int 4784063f925SOleksandr Tymoshenko bcm_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) 4794063f925SOleksandr Tymoshenko { 4804063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4815508fc88SLuiz Otavio O Souza uint32_t bank, reg; 4824063f925SOleksandr Tymoshenko int i; 4834063f925SOleksandr Tymoshenko 4844063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4854063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4864063f925SOleksandr Tymoshenko break; 4874063f925SOleksandr Tymoshenko } 4884063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4894063f925SOleksandr Tymoshenko return (EINVAL); 4904063f925SOleksandr Tymoshenko /* We never write to read-only/reserved pins. */ 4914063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 4924063f925SOleksandr Tymoshenko return (EINVAL); 4934063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4945508fc88SLuiz Otavio O Souza bank = BCM_GPIO_BANK(pin); 4954063f925SOleksandr Tymoshenko if (value) 4965508fc88SLuiz Otavio O Souza reg = BCM_GPIO_GPSET(bank); 4974063f925SOleksandr Tymoshenko else 4985508fc88SLuiz Otavio O Souza reg = BCM_GPIO_GPCLR(bank); 4995508fc88SLuiz Otavio O Souza BCM_GPIO_WRITE(sc, reg, BCM_GPIO_MASK(pin)); 5004063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 5014063f925SOleksandr Tymoshenko 5024063f925SOleksandr Tymoshenko return (0); 5034063f925SOleksandr Tymoshenko } 5044063f925SOleksandr Tymoshenko 5054063f925SOleksandr Tymoshenko static int 5064063f925SOleksandr Tymoshenko bcm_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) 5074063f925SOleksandr Tymoshenko { 5084063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 5095508fc88SLuiz Otavio O Souza uint32_t bank, reg_data; 5104063f925SOleksandr Tymoshenko int i; 5114063f925SOleksandr Tymoshenko 5124063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 5134063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 5144063f925SOleksandr Tymoshenko break; 5154063f925SOleksandr Tymoshenko } 5164063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 5174063f925SOleksandr Tymoshenko return (EINVAL); 5185508fc88SLuiz Otavio O Souza bank = BCM_GPIO_BANK(pin); 5194063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 5204063f925SOleksandr Tymoshenko reg_data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); 5214063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 5225508fc88SLuiz Otavio O Souza *val = (reg_data & BCM_GPIO_MASK(pin)) ? 1 : 0; 5234063f925SOleksandr Tymoshenko 5244063f925SOleksandr Tymoshenko return (0); 5254063f925SOleksandr Tymoshenko } 5264063f925SOleksandr Tymoshenko 5274063f925SOleksandr Tymoshenko static int 5284063f925SOleksandr Tymoshenko bcm_gpio_pin_toggle(device_t dev, uint32_t pin) 5294063f925SOleksandr Tymoshenko { 5304063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 5315508fc88SLuiz Otavio O Souza uint32_t bank, data, reg; 5324063f925SOleksandr Tymoshenko int i; 5334063f925SOleksandr Tymoshenko 5344063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 5354063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 5364063f925SOleksandr Tymoshenko break; 5374063f925SOleksandr Tymoshenko } 5384063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 5394063f925SOleksandr Tymoshenko return (EINVAL); 5404063f925SOleksandr Tymoshenko /* We never write to read-only/reserved pins. */ 5414063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 5424063f925SOleksandr Tymoshenko return (EINVAL); 5434063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 5445508fc88SLuiz Otavio O Souza bank = BCM_GPIO_BANK(pin); 5454063f925SOleksandr Tymoshenko data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); 5465508fc88SLuiz Otavio O Souza if (data & BCM_GPIO_MASK(pin)) 5475508fc88SLuiz Otavio O Souza reg = BCM_GPIO_GPCLR(bank); 5484063f925SOleksandr Tymoshenko else 5495508fc88SLuiz Otavio O Souza reg = BCM_GPIO_GPSET(bank); 5505508fc88SLuiz Otavio O Souza BCM_GPIO_WRITE(sc, reg, BCM_GPIO_MASK(pin)); 5514063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 5524063f925SOleksandr Tymoshenko 5534063f925SOleksandr Tymoshenko return (0); 5544063f925SOleksandr Tymoshenko } 5554063f925SOleksandr Tymoshenko 5564063f925SOleksandr Tymoshenko static int 55790576f54SOleksandr Tymoshenko bcm_gpio_func_proc(SYSCTL_HANDLER_ARGS) 55890576f54SOleksandr Tymoshenko { 55990576f54SOleksandr Tymoshenko char buf[16]; 56090576f54SOleksandr Tymoshenko struct bcm_gpio_softc *sc; 56190576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl *sc_sysctl; 56290576f54SOleksandr Tymoshenko uint32_t nfunc; 56344d06d8dSLuiz Otavio O Souza int error; 56490576f54SOleksandr Tymoshenko 56590576f54SOleksandr Tymoshenko sc_sysctl = arg1; 56690576f54SOleksandr Tymoshenko sc = sc_sysctl->sc; 56790576f54SOleksandr Tymoshenko 56890576f54SOleksandr Tymoshenko /* Get the current pin function. */ 56990576f54SOleksandr Tymoshenko nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin); 57090576f54SOleksandr Tymoshenko bcm_gpio_func_str(nfunc, buf, sizeof(buf)); 57190576f54SOleksandr Tymoshenko 57290576f54SOleksandr Tymoshenko error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 57390576f54SOleksandr Tymoshenko if (error != 0 || req->newptr == NULL) 57490576f54SOleksandr Tymoshenko return (error); 57599a20111SLuiz Otavio O Souza /* Ignore changes on read-only pins. */ 57699a20111SLuiz Otavio O Souza if (bcm_gpio_pin_is_ro(sc, sc_sysctl->pin)) 57799a20111SLuiz Otavio O Souza return (0); 57890576f54SOleksandr Tymoshenko /* Parse the user supplied string and check for a valid pin function. */ 57990576f54SOleksandr Tymoshenko if (bcm_gpio_str_func(buf, &nfunc) != 0) 58090576f54SOleksandr Tymoshenko return (EINVAL); 58190576f54SOleksandr Tymoshenko 58244d06d8dSLuiz Otavio O Souza /* Update the pin alternate function. */ 58344d06d8dSLuiz Otavio O Souza bcm_gpio_set_alternate(sc->sc_dev, sc_sysctl->pin, nfunc); 58490576f54SOleksandr Tymoshenko 58590576f54SOleksandr Tymoshenko return (0); 58690576f54SOleksandr Tymoshenko } 58790576f54SOleksandr Tymoshenko 58890576f54SOleksandr Tymoshenko static void 58990576f54SOleksandr Tymoshenko bcm_gpio_sysctl_init(struct bcm_gpio_softc *sc) 59090576f54SOleksandr Tymoshenko { 59190576f54SOleksandr Tymoshenko char pinbuf[3]; 59290576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl *sc_sysctl; 59390576f54SOleksandr Tymoshenko struct sysctl_ctx_list *ctx; 59490576f54SOleksandr Tymoshenko struct sysctl_oid *tree_node, *pin_node, *pinN_node; 59590576f54SOleksandr Tymoshenko struct sysctl_oid_list *tree, *pin_tree, *pinN_tree; 59690576f54SOleksandr Tymoshenko int i; 59790576f54SOleksandr Tymoshenko 59890576f54SOleksandr Tymoshenko /* 59990576f54SOleksandr Tymoshenko * Add per-pin sysctl tree/handlers. 60090576f54SOleksandr Tymoshenko */ 60190576f54SOleksandr Tymoshenko ctx = device_get_sysctl_ctx(sc->sc_dev); 60290576f54SOleksandr Tymoshenko tree_node = device_get_sysctl_tree(sc->sc_dev); 60390576f54SOleksandr Tymoshenko tree = SYSCTL_CHILDREN(tree_node); 60490576f54SOleksandr Tymoshenko pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin", 6051fbabb48SLuiz Otavio O Souza CTLFLAG_RD, NULL, "GPIO Pins"); 60690576f54SOleksandr Tymoshenko pin_tree = SYSCTL_CHILDREN(pin_node); 60790576f54SOleksandr Tymoshenko 60890576f54SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 60990576f54SOleksandr Tymoshenko 61090576f54SOleksandr Tymoshenko snprintf(pinbuf, sizeof(pinbuf), "%d", i); 61190576f54SOleksandr Tymoshenko pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf, 61290576f54SOleksandr Tymoshenko CTLFLAG_RD, NULL, "GPIO Pin"); 61390576f54SOleksandr Tymoshenko pinN_tree = SYSCTL_CHILDREN(pinN_node); 61490576f54SOleksandr Tymoshenko 61590576f54SOleksandr Tymoshenko sc->sc_sysctl[i].sc = sc; 61690576f54SOleksandr Tymoshenko sc_sysctl = &sc->sc_sysctl[i]; 61790576f54SOleksandr Tymoshenko sc_sysctl->sc = sc; 61890576f54SOleksandr Tymoshenko sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin; 61990576f54SOleksandr Tymoshenko SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function", 62090576f54SOleksandr Tymoshenko CTLFLAG_RW | CTLTYPE_STRING, sc_sysctl, 62190576f54SOleksandr Tymoshenko sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc, 62290576f54SOleksandr Tymoshenko "A", "Pin Function"); 62390576f54SOleksandr Tymoshenko } 62490576f54SOleksandr Tymoshenko } 62590576f54SOleksandr Tymoshenko 62690576f54SOleksandr Tymoshenko static int 62799a20111SLuiz Otavio O Souza bcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc, phandle_t node, 62899a20111SLuiz Otavio O Souza const char *propname, const char *label) 62999a20111SLuiz Otavio O Souza { 63099a20111SLuiz Otavio O Souza int i, need_comma, npins, range_start, range_stop; 63199a20111SLuiz Otavio O Souza pcell_t *pins; 63299a20111SLuiz Otavio O Souza 63399a20111SLuiz Otavio O Souza /* Get the property data. */ 63499a20111SLuiz Otavio O Souza npins = OF_getencprop_alloc(node, propname, sizeof(*pins), 63599a20111SLuiz Otavio O Souza (void **)&pins); 63699a20111SLuiz Otavio O Souza if (npins < 0) 63799a20111SLuiz Otavio O Souza return (-1); 63899a20111SLuiz Otavio O Souza if (npins == 0) { 639bc90a48cSOleksandr Tymoshenko OF_prop_free(pins); 64099a20111SLuiz Otavio O Souza return (0); 64199a20111SLuiz Otavio O Souza } 64299a20111SLuiz Otavio O Souza for (i = 0; i < npins; i++) 64399a20111SLuiz Otavio O Souza sc->sc_ro_pins[i + sc->sc_ro_npins] = pins[i]; 64499a20111SLuiz Otavio O Souza sc->sc_ro_npins += npins; 64599a20111SLuiz Otavio O Souza need_comma = 0; 64699a20111SLuiz Otavio O Souza device_printf(sc->sc_dev, "%s pins: ", label); 64799a20111SLuiz Otavio O Souza range_start = range_stop = pins[0]; 64899a20111SLuiz Otavio O Souza for (i = 1; i < npins; i++) { 64999a20111SLuiz Otavio O Souza if (pins[i] != range_stop + 1) { 65099a20111SLuiz Otavio O Souza if (need_comma) 65199a20111SLuiz Otavio O Souza printf(","); 65299a20111SLuiz Otavio O Souza if (range_start != range_stop) 65399a20111SLuiz Otavio O Souza printf("%d-%d", range_start, range_stop); 65499a20111SLuiz Otavio O Souza else 65599a20111SLuiz Otavio O Souza printf("%d", range_start); 65699a20111SLuiz Otavio O Souza range_start = range_stop = pins[i]; 65799a20111SLuiz Otavio O Souza need_comma = 1; 65899a20111SLuiz Otavio O Souza } else 65999a20111SLuiz Otavio O Souza range_stop++; 66099a20111SLuiz Otavio O Souza } 66199a20111SLuiz Otavio O Souza if (need_comma) 66299a20111SLuiz Otavio O Souza printf(","); 66399a20111SLuiz Otavio O Souza if (range_start != range_stop) 66499a20111SLuiz Otavio O Souza printf("%d-%d.\n", range_start, range_stop); 66599a20111SLuiz Otavio O Souza else 66699a20111SLuiz Otavio O Souza printf("%d.\n", range_start); 667bc90a48cSOleksandr Tymoshenko OF_prop_free(pins); 66899a20111SLuiz Otavio O Souza 66999a20111SLuiz Otavio O Souza return (0); 67099a20111SLuiz Otavio O Souza } 67199a20111SLuiz Otavio O Souza 67299a20111SLuiz Otavio O Souza static int 6734063f925SOleksandr Tymoshenko bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc) 6744063f925SOleksandr Tymoshenko { 67599a20111SLuiz Otavio O Souza char *name; 6764063f925SOleksandr Tymoshenko phandle_t gpio, node, reserved; 67799a20111SLuiz Otavio O Souza ssize_t len; 6784063f925SOleksandr Tymoshenko 679*0e3cfd98SOleksandr Tymoshenko /* Get read-only pins if they're provided */ 6804063f925SOleksandr Tymoshenko gpio = ofw_bus_get_node(sc->sc_dev); 68199a20111SLuiz Otavio O Souza if (bcm_gpio_get_ro_pins(sc, gpio, "broadcom,read-only", 68299a20111SLuiz Otavio O Souza "read-only") != 0) 683aa2959baSOleksandr Tymoshenko return (0); 68499a20111SLuiz Otavio O Souza /* Traverse the GPIO subnodes to find the reserved pins node. */ 6854063f925SOleksandr Tymoshenko reserved = 0; 68699a20111SLuiz Otavio O Souza node = OF_child(gpio); 6874063f925SOleksandr Tymoshenko while ((node != 0) && (reserved == 0)) { 68899a20111SLuiz Otavio O Souza len = OF_getprop_alloc(node, "name", 1, (void **)&name); 68999a20111SLuiz Otavio O Souza if (len == -1) 69099a20111SLuiz Otavio O Souza return (-1); 6914063f925SOleksandr Tymoshenko if (strcmp(name, "reserved") == 0) 6924063f925SOleksandr Tymoshenko reserved = node; 693bc90a48cSOleksandr Tymoshenko OF_prop_free(name); 6944063f925SOleksandr Tymoshenko node = OF_peer(node); 6954063f925SOleksandr Tymoshenko } 6964063f925SOleksandr Tymoshenko if (reserved == 0) 6974063f925SOleksandr Tymoshenko return (-1); 6984063f925SOleksandr Tymoshenko /* Get the reserved pins. */ 69999a20111SLuiz Otavio O Souza if (bcm_gpio_get_ro_pins(sc, reserved, "broadcom,pins", 70099a20111SLuiz Otavio O Souza "reserved") != 0) 7014063f925SOleksandr Tymoshenko return (-1); 7024063f925SOleksandr Tymoshenko 7034063f925SOleksandr Tymoshenko return (0); 7044063f925SOleksandr Tymoshenko } 7054063f925SOleksandr Tymoshenko 70659c3cb81SAndrew Turner #ifndef INTRNG 7074063f925SOleksandr Tymoshenko static int 70812471cecSLuiz Otavio O Souza bcm_gpio_intr(void *arg) 70912471cecSLuiz Otavio O Souza { 71012471cecSLuiz Otavio O Souza int bank_last, irq; 71112471cecSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 71212471cecSLuiz Otavio O Souza struct intr_event *event; 71312471cecSLuiz Otavio O Souza uint32_t bank, mask, reg; 71412471cecSLuiz Otavio O Souza 71512471cecSLuiz Otavio O Souza sc = (struct bcm_gpio_softc *)arg; 71612471cecSLuiz Otavio O Souza reg = 0; 71712471cecSLuiz Otavio O Souza bank_last = -1; 71812471cecSLuiz Otavio O Souza for (irq = 0; irq < BCM_GPIO_PINS; irq++) { 71912471cecSLuiz Otavio O Souza bank = BCM_GPIO_BANK(irq); 72012471cecSLuiz Otavio O Souza mask = BCM_GPIO_MASK(irq); 72112471cecSLuiz Otavio O Souza if (bank != bank_last) { 72212471cecSLuiz Otavio O Souza reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank)); 72312471cecSLuiz Otavio O Souza bank_last = bank; 72412471cecSLuiz Otavio O Souza } 72512471cecSLuiz Otavio O Souza if (reg & mask) { 72612471cecSLuiz Otavio O Souza event = sc->sc_events[irq]; 72712471cecSLuiz Otavio O Souza if (event != NULL && !TAILQ_EMPTY(&event->ie_handlers)) 72812471cecSLuiz Otavio O Souza intr_event_handle(event, NULL); 72912471cecSLuiz Otavio O Souza else { 73012471cecSLuiz Otavio O Souza device_printf(sc->sc_dev, "Stray IRQ %d\n", 73112471cecSLuiz Otavio O Souza irq); 73212471cecSLuiz Otavio O Souza } 73312471cecSLuiz Otavio O Souza /* Clear the Status bit by writing '1' to it. */ 73412471cecSLuiz Otavio O Souza BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), mask); 73512471cecSLuiz Otavio O Souza } 73612471cecSLuiz Otavio O Souza } 73712471cecSLuiz Otavio O Souza 73812471cecSLuiz Otavio O Souza return (FILTER_HANDLED); 73912471cecSLuiz Otavio O Souza } 74089de2fb6SSvatopluk Kraus #endif 74112471cecSLuiz Otavio O Souza 74212471cecSLuiz Otavio O Souza static int 7434063f925SOleksandr Tymoshenko bcm_gpio_probe(device_t dev) 7444063f925SOleksandr Tymoshenko { 745add35ed5SIan Lepore 746add35ed5SIan Lepore if (!ofw_bus_status_okay(dev)) 747add35ed5SIan Lepore return (ENXIO); 748add35ed5SIan Lepore 749aa2959baSOleksandr Tymoshenko if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0) 7504063f925SOleksandr Tymoshenko return (ENXIO); 7514063f925SOleksandr Tymoshenko 7524063f925SOleksandr Tymoshenko device_set_desc(dev, "BCM2708/2835 GPIO controller"); 7534063f925SOleksandr Tymoshenko return (BUS_PROBE_DEFAULT); 7544063f925SOleksandr Tymoshenko } 7554063f925SOleksandr Tymoshenko 75659c3cb81SAndrew Turner #ifdef INTRNG 75789de2fb6SSvatopluk Kraus static int 75889de2fb6SSvatopluk Kraus bcm_gpio_intr_attach(device_t dev) 75989de2fb6SSvatopluk Kraus { 76089de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc; 76189de2fb6SSvatopluk Kraus 76289de2fb6SSvatopluk Kraus /* 76389de2fb6SSvatopluk Kraus * Only first two interrupt lines are used. Third line is 76489de2fb6SSvatopluk Kraus * mirrored second line and forth line is common for all banks. 76589de2fb6SSvatopluk Kraus */ 76689de2fb6SSvatopluk Kraus sc = device_get_softc(dev); 76789de2fb6SSvatopluk Kraus if (sc->sc_res[1] == NULL || sc->sc_res[2] == NULL) 76889de2fb6SSvatopluk Kraus return (-1); 76989de2fb6SSvatopluk Kraus 77089de2fb6SSvatopluk Kraus if (bcm_gpio_pic_attach(sc) != 0) { 77189de2fb6SSvatopluk Kraus device_printf(dev, "unable to attach PIC\n"); 77289de2fb6SSvatopluk Kraus return (-1); 77389de2fb6SSvatopluk Kraus } 77489de2fb6SSvatopluk Kraus if (bus_setup_intr(dev, sc->sc_res[1], INTR_TYPE_MISC | INTR_MPSAFE, 77589de2fb6SSvatopluk Kraus bcm_gpio_intr_bank0, NULL, sc, &sc->sc_intrhand[0]) != 0) 77689de2fb6SSvatopluk Kraus return (-1); 77789de2fb6SSvatopluk Kraus if (bus_setup_intr(dev, sc->sc_res[2], INTR_TYPE_MISC | INTR_MPSAFE, 77889de2fb6SSvatopluk Kraus bcm_gpio_intr_bank1, NULL, sc, &sc->sc_intrhand[1]) != 0) 77989de2fb6SSvatopluk Kraus return (-1); 78089de2fb6SSvatopluk Kraus 78189de2fb6SSvatopluk Kraus return (0); 78289de2fb6SSvatopluk Kraus } 78389de2fb6SSvatopluk Kraus 78489de2fb6SSvatopluk Kraus static void 78589de2fb6SSvatopluk Kraus bcm_gpio_intr_detach(device_t dev) 78689de2fb6SSvatopluk Kraus { 78789de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc; 78889de2fb6SSvatopluk Kraus 78989de2fb6SSvatopluk Kraus sc = device_get_softc(dev); 79089de2fb6SSvatopluk Kraus if (sc->sc_intrhand[0] != NULL) 79189de2fb6SSvatopluk Kraus bus_teardown_intr(dev, sc->sc_res[1], sc->sc_intrhand[0]); 79289de2fb6SSvatopluk Kraus if (sc->sc_intrhand[1] != NULL) 79389de2fb6SSvatopluk Kraus bus_teardown_intr(dev, sc->sc_res[2], sc->sc_intrhand[1]); 79489de2fb6SSvatopluk Kraus 79589de2fb6SSvatopluk Kraus bcm_gpio_pic_detach(sc); 79689de2fb6SSvatopluk Kraus } 79789de2fb6SSvatopluk Kraus 79889de2fb6SSvatopluk Kraus #else 7994063f925SOleksandr Tymoshenko static int 80012471cecSLuiz Otavio O Souza bcm_gpio_intr_attach(device_t dev) 80112471cecSLuiz Otavio O Souza { 80212471cecSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 80312471cecSLuiz Otavio O Souza int i; 80412471cecSLuiz Otavio O Souza 80512471cecSLuiz Otavio O Souza sc = device_get_softc(dev); 80612471cecSLuiz Otavio O Souza for (i = 0; i < BCM_GPIO_IRQS; i++) { 80712471cecSLuiz Otavio O Souza if (bus_setup_intr(dev, sc->sc_res[i + 1], 80812471cecSLuiz Otavio O Souza INTR_TYPE_MISC | INTR_MPSAFE, bcm_gpio_intr, 80912471cecSLuiz Otavio O Souza NULL, sc, &sc->sc_intrhand[i]) != 0) { 81012471cecSLuiz Otavio O Souza return (-1); 81112471cecSLuiz Otavio O Souza } 81212471cecSLuiz Otavio O Souza } 81312471cecSLuiz Otavio O Souza 81412471cecSLuiz Otavio O Souza return (0); 81512471cecSLuiz Otavio O Souza } 81612471cecSLuiz Otavio O Souza 81712471cecSLuiz Otavio O Souza static void 81812471cecSLuiz Otavio O Souza bcm_gpio_intr_detach(device_t dev) 81912471cecSLuiz Otavio O Souza { 82012471cecSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 82112471cecSLuiz Otavio O Souza int i; 82212471cecSLuiz Otavio O Souza 82312471cecSLuiz Otavio O Souza sc = device_get_softc(dev); 82412471cecSLuiz Otavio O Souza for (i = 0; i < BCM_GPIO_IRQS; i++) { 82512471cecSLuiz Otavio O Souza if (sc->sc_intrhand[i]) { 82612471cecSLuiz Otavio O Souza bus_teardown_intr(dev, sc->sc_res[i + 1], 82712471cecSLuiz Otavio O Souza sc->sc_intrhand[i]); 82812471cecSLuiz Otavio O Souza } 82912471cecSLuiz Otavio O Souza } 83012471cecSLuiz Otavio O Souza } 83189de2fb6SSvatopluk Kraus #endif 83212471cecSLuiz Otavio O Souza 83312471cecSLuiz Otavio O Souza static int 8344063f925SOleksandr Tymoshenko bcm_gpio_attach(device_t dev) 8354063f925SOleksandr Tymoshenko { 836c2136589SLuiz Otavio O Souza int i, j; 8374063f925SOleksandr Tymoshenko phandle_t gpio; 838c2136589SLuiz Otavio O Souza struct bcm_gpio_softc *sc; 839c2136589SLuiz Otavio O Souza uint32_t func; 8404063f925SOleksandr Tymoshenko 84112471cecSLuiz Otavio O Souza if (bcm_gpio_sc != NULL) 84212471cecSLuiz Otavio O Souza return (ENXIO); 84312471cecSLuiz Otavio O Souza 84412471cecSLuiz Otavio O Souza bcm_gpio_sc = sc = device_get_softc(dev); 8454063f925SOleksandr Tymoshenko sc->sc_dev = dev; 84612471cecSLuiz Otavio O Souza mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_SPIN); 847c2136589SLuiz Otavio O Souza if (bus_alloc_resources(dev, bcm_gpio_res_spec, sc->sc_res) != 0) { 848c2136589SLuiz Otavio O Souza device_printf(dev, "cannot allocate resources\n"); 849c2136589SLuiz Otavio O Souza goto fail; 8504063f925SOleksandr Tymoshenko } 851c2136589SLuiz Otavio O Souza sc->sc_bst = rman_get_bustag(sc->sc_res[0]); 852c2136589SLuiz Otavio O Souza sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); 85312471cecSLuiz Otavio O Souza /* Setup the GPIO interrupt handler. */ 85412471cecSLuiz Otavio O Souza if (bcm_gpio_intr_attach(dev)) { 85512471cecSLuiz Otavio O Souza device_printf(dev, "unable to setup the gpio irq handler\n"); 85612471cecSLuiz Otavio O Souza goto fail; 85712471cecSLuiz Otavio O Souza } 8584063f925SOleksandr Tymoshenko /* Find our node. */ 8594063f925SOleksandr Tymoshenko gpio = ofw_bus_get_node(sc->sc_dev); 8604063f925SOleksandr Tymoshenko if (!OF_hasprop(gpio, "gpio-controller")) 8614063f925SOleksandr Tymoshenko /* Node is not a GPIO controller. */ 8624063f925SOleksandr Tymoshenko goto fail; 8634063f925SOleksandr Tymoshenko /* 8644063f925SOleksandr Tymoshenko * Find the read-only pins. These are pins we never touch or bad 8654063f925SOleksandr Tymoshenko * things could happen. 8664063f925SOleksandr Tymoshenko */ 8674063f925SOleksandr Tymoshenko if (bcm_gpio_get_reserved_pins(sc) == -1) 8684063f925SOleksandr Tymoshenko goto fail; 8694063f925SOleksandr Tymoshenko /* Initialize the software controlled pins. */ 8708d900240SLuiz Otavio O Souza for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) { 8714063f925SOleksandr Tymoshenko snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, 8724063f925SOleksandr Tymoshenko "pin %d", j); 87390576f54SOleksandr Tymoshenko func = bcm_gpio_get_function(sc, j); 8744063f925SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_pin = j; 8754063f925SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS; 87690576f54SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func); 87759c3cb81SAndrew Turner #ifndef INTRNG 87812471cecSLuiz Otavio O Souza /* The default is active-low interrupts. */ 87912471cecSLuiz Otavio O Souza sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL; 88012471cecSLuiz Otavio O Souza sc->sc_irq_polarity[i] = INTR_POLARITY_LOW; 88189de2fb6SSvatopluk Kraus #endif 8824063f925SOleksandr Tymoshenko i++; 8834063f925SOleksandr Tymoshenko } 8844063f925SOleksandr Tymoshenko sc->sc_gpio_npins = i; 88590576f54SOleksandr Tymoshenko bcm_gpio_sysctl_init(sc); 8867836352bSLuiz Otavio O Souza sc->sc_busdev = gpiobus_attach_bus(dev); 8877836352bSLuiz Otavio O Souza if (sc->sc_busdev == NULL) 8887836352bSLuiz Otavio O Souza goto fail; 88990576f54SOleksandr Tymoshenko 8907836352bSLuiz Otavio O Souza return (0); 8914063f925SOleksandr Tymoshenko 8924063f925SOleksandr Tymoshenko fail: 89312471cecSLuiz Otavio O Souza bcm_gpio_intr_detach(dev); 894c2136589SLuiz Otavio O Souza bus_release_resources(dev, bcm_gpio_res_spec, sc->sc_res); 895c2136589SLuiz Otavio O Souza mtx_destroy(&sc->sc_mtx); 896c2136589SLuiz Otavio O Souza 8974063f925SOleksandr Tymoshenko return (ENXIO); 8984063f925SOleksandr Tymoshenko } 8994063f925SOleksandr Tymoshenko 9004063f925SOleksandr Tymoshenko static int 9014063f925SOleksandr Tymoshenko bcm_gpio_detach(device_t dev) 9024063f925SOleksandr Tymoshenko { 9034063f925SOleksandr Tymoshenko 9044063f925SOleksandr Tymoshenko return (EBUSY); 9054063f925SOleksandr Tymoshenko } 9064063f925SOleksandr Tymoshenko 90759c3cb81SAndrew Turner #ifdef INTRNG 90889de2fb6SSvatopluk Kraus static inline void 909c28b681cSSvatopluk Kraus bcm_gpio_modify(struct bcm_gpio_softc *sc, uint32_t reg, uint32_t mask, 910c28b681cSSvatopluk Kraus bool set_bits) 911c28b681cSSvatopluk Kraus { 912c28b681cSSvatopluk Kraus 913c28b681cSSvatopluk Kraus if (set_bits) 914c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, reg, mask); 915c28b681cSSvatopluk Kraus else 916c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, reg, mask); 917c28b681cSSvatopluk Kraus } 918c28b681cSSvatopluk Kraus 919c28b681cSSvatopluk Kraus static inline void 92089de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) 92189de2fb6SSvatopluk Kraus { 92289de2fb6SSvatopluk Kraus uint32_t bank; 92389de2fb6SSvatopluk Kraus 92489de2fb6SSvatopluk Kraus /* Write 1 to clear. */ 92589de2fb6SSvatopluk Kraus bank = BCM_GPIO_BANK(bgi->bgi_irq); 92689de2fb6SSvatopluk Kraus BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), bgi->bgi_mask); 92789de2fb6SSvatopluk Kraus } 92889de2fb6SSvatopluk Kraus 92989de2fb6SSvatopluk Kraus static inline bool 93089de2fb6SSvatopluk Kraus bcm_gpio_isrc_is_level(struct bcm_gpio_irqsrc *bgi) 93189de2fb6SSvatopluk Kraus { 93289de2fb6SSvatopluk Kraus 933c28b681cSSvatopluk Kraus return (bgi->bgi_mode == GPIO_INTR_LEVEL_LOW || 934c28b681cSSvatopluk Kraus bgi->bgi_mode == GPIO_INTR_LEVEL_HIGH); 93589de2fb6SSvatopluk Kraus } 93689de2fb6SSvatopluk Kraus 93789de2fb6SSvatopluk Kraus static inline void 93889de2fb6SSvatopluk Kraus bcm_gpio_isrc_mask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) 93989de2fb6SSvatopluk Kraus { 940c28b681cSSvatopluk Kraus uint32_t bank; 94189de2fb6SSvatopluk Kraus 942c28b681cSSvatopluk Kraus bank = BCM_GPIO_BANK(bgi->bgi_irq); 94389de2fb6SSvatopluk Kraus BCM_GPIO_LOCK(sc); 944c28b681cSSvatopluk Kraus switch (bgi->bgi_mode) { 945c28b681cSSvatopluk Kraus case GPIO_INTR_LEVEL_LOW: 946c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask); 947c28b681cSSvatopluk Kraus break; 948c28b681cSSvatopluk Kraus case GPIO_INTR_LEVEL_HIGH: 949c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask); 950c28b681cSSvatopluk Kraus break; 951c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_RISING: 952c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); 953c28b681cSSvatopluk Kraus break; 954c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_FALLING: 955c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); 956c28b681cSSvatopluk Kraus break; 957c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_BOTH: 958c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); 959c28b681cSSvatopluk Kraus BCM_GPIO_CLEAR_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); 960c28b681cSSvatopluk Kraus break; 961c28b681cSSvatopluk Kraus } 962c28b681cSSvatopluk Kraus BCM_GPIO_UNLOCK(sc); 96389de2fb6SSvatopluk Kraus } 96489de2fb6SSvatopluk Kraus 96589de2fb6SSvatopluk Kraus static inline void 96689de2fb6SSvatopluk Kraus bcm_gpio_isrc_unmask(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi) 96789de2fb6SSvatopluk Kraus { 968c28b681cSSvatopluk Kraus uint32_t bank; 96989de2fb6SSvatopluk Kraus 970c28b681cSSvatopluk Kraus bank = BCM_GPIO_BANK(bgi->bgi_irq); 97189de2fb6SSvatopluk Kraus BCM_GPIO_LOCK(sc); 972c28b681cSSvatopluk Kraus switch (bgi->bgi_mode) { 973c28b681cSSvatopluk Kraus case GPIO_INTR_LEVEL_LOW: 974c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask); 975c28b681cSSvatopluk Kraus break; 976c28b681cSSvatopluk Kraus case GPIO_INTR_LEVEL_HIGH: 977c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask); 978c28b681cSSvatopluk Kraus break; 979c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_RISING: 980c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); 981c28b681cSSvatopluk Kraus break; 982c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_FALLING: 983c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); 984c28b681cSSvatopluk Kraus break; 985c28b681cSSvatopluk Kraus case GPIO_INTR_EDGE_BOTH: 986c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask); 987c28b681cSSvatopluk Kraus BCM_GPIO_SET_BITS(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask); 988c28b681cSSvatopluk Kraus break; 989c28b681cSSvatopluk Kraus } 99089de2fb6SSvatopluk Kraus BCM_GPIO_UNLOCK(sc); 99189de2fb6SSvatopluk Kraus } 99289de2fb6SSvatopluk Kraus 99389de2fb6SSvatopluk Kraus static int 99489de2fb6SSvatopluk Kraus bcm_gpio_intr_internal(struct bcm_gpio_softc *sc, uint32_t bank) 99589de2fb6SSvatopluk Kraus { 99689de2fb6SSvatopluk Kraus u_int irq; 99789de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi; 99889de2fb6SSvatopluk Kraus uint32_t reg; 99989de2fb6SSvatopluk Kraus 100089de2fb6SSvatopluk Kraus /* Do not care of spurious interrupt on GPIO. */ 100189de2fb6SSvatopluk Kraus reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank)); 100289de2fb6SSvatopluk Kraus while (reg != 0) { 100389de2fb6SSvatopluk Kraus irq = BCM_GPIO_PINS_PER_BANK * bank + ffs(reg) - 1; 100489de2fb6SSvatopluk Kraus bgi = sc->sc_isrcs + irq; 100589de2fb6SSvatopluk Kraus if (!bcm_gpio_isrc_is_level(bgi)) 100689de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(sc, bgi); 100789de2fb6SSvatopluk Kraus if (intr_isrc_dispatch(&bgi->bgi_isrc, 100889de2fb6SSvatopluk Kraus curthread->td_intr_frame) != 0) { 100989de2fb6SSvatopluk Kraus bcm_gpio_isrc_mask(sc, bgi); 101089de2fb6SSvatopluk Kraus if (bcm_gpio_isrc_is_level(bgi)) 101189de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(sc, bgi); 101289de2fb6SSvatopluk Kraus device_printf(sc->sc_dev, "Stray irq %u disabled\n", 101389de2fb6SSvatopluk Kraus irq); 101489de2fb6SSvatopluk Kraus } 101589de2fb6SSvatopluk Kraus reg &= ~bgi->bgi_mask; 101689de2fb6SSvatopluk Kraus } 101789de2fb6SSvatopluk Kraus return (FILTER_HANDLED); 101889de2fb6SSvatopluk Kraus } 101989de2fb6SSvatopluk Kraus 102089de2fb6SSvatopluk Kraus static int 102189de2fb6SSvatopluk Kraus bcm_gpio_intr_bank0(void *arg) 102289de2fb6SSvatopluk Kraus { 102389de2fb6SSvatopluk Kraus 102489de2fb6SSvatopluk Kraus return (bcm_gpio_intr_internal(arg, 0)); 102589de2fb6SSvatopluk Kraus } 102689de2fb6SSvatopluk Kraus 102789de2fb6SSvatopluk Kraus static int 102889de2fb6SSvatopluk Kraus bcm_gpio_intr_bank1(void *arg) 102989de2fb6SSvatopluk Kraus { 103089de2fb6SSvatopluk Kraus 103189de2fb6SSvatopluk Kraus return (bcm_gpio_intr_internal(arg, 1)); 103289de2fb6SSvatopluk Kraus } 103389de2fb6SSvatopluk Kraus 103489de2fb6SSvatopluk Kraus static int 103589de2fb6SSvatopluk Kraus bcm_gpio_pic_attach(struct bcm_gpio_softc *sc) 103689de2fb6SSvatopluk Kraus { 103789de2fb6SSvatopluk Kraus int error; 103889de2fb6SSvatopluk Kraus uint32_t irq; 103989de2fb6SSvatopluk Kraus const char *name; 104089de2fb6SSvatopluk Kraus 104189de2fb6SSvatopluk Kraus name = device_get_nameunit(sc->sc_dev); 104289de2fb6SSvatopluk Kraus for (irq = 0; irq < BCM_GPIO_PINS; irq++) { 104389de2fb6SSvatopluk Kraus sc->sc_isrcs[irq].bgi_irq = irq; 104489de2fb6SSvatopluk Kraus sc->sc_isrcs[irq].bgi_mask = BCM_GPIO_MASK(irq); 1045c28b681cSSvatopluk Kraus sc->sc_isrcs[irq].bgi_mode = GPIO_INTR_CONFORM; 104689de2fb6SSvatopluk Kraus 104789de2fb6SSvatopluk Kraus error = intr_isrc_register(&sc->sc_isrcs[irq].bgi_isrc, 104889de2fb6SSvatopluk Kraus sc->sc_dev, 0, "%s,%u", name, irq); 104989de2fb6SSvatopluk Kraus if (error != 0) 105089de2fb6SSvatopluk Kraus return (error); /* XXX deregister ISRCs */ 105189de2fb6SSvatopluk Kraus } 10529346e913SAndrew Turner if (intr_pic_register(sc->sc_dev, 10539346e913SAndrew Turner OF_xref_from_node(ofw_bus_get_node(sc->sc_dev))) == NULL) 10549346e913SAndrew Turner return (ENXIO); 10559346e913SAndrew Turner 10569346e913SAndrew Turner return (0); 105789de2fb6SSvatopluk Kraus } 105889de2fb6SSvatopluk Kraus 105989de2fb6SSvatopluk Kraus static int 106089de2fb6SSvatopluk Kraus bcm_gpio_pic_detach(struct bcm_gpio_softc *sc) 106189de2fb6SSvatopluk Kraus { 106289de2fb6SSvatopluk Kraus 106389de2fb6SSvatopluk Kraus /* 106489de2fb6SSvatopluk Kraus * There has not been established any procedure yet 106589de2fb6SSvatopluk Kraus * how to detach PIC from living system correctly. 106689de2fb6SSvatopluk Kraus */ 106789de2fb6SSvatopluk Kraus device_printf(sc->sc_dev, "%s: not implemented yet\n", __func__); 106889de2fb6SSvatopluk Kraus return (EBUSY); 106989de2fb6SSvatopluk Kraus } 107089de2fb6SSvatopluk Kraus 107189de2fb6SSvatopluk Kraus static void 1072c28b681cSSvatopluk Kraus bcm_gpio_pic_config_intr(struct bcm_gpio_softc *sc, struct bcm_gpio_irqsrc *bgi, 1073c28b681cSSvatopluk Kraus uint32_t mode) 1074c28b681cSSvatopluk Kraus { 1075c28b681cSSvatopluk Kraus uint32_t bank; 1076c28b681cSSvatopluk Kraus 1077c28b681cSSvatopluk Kraus bank = BCM_GPIO_BANK(bgi->bgi_irq); 1078c28b681cSSvatopluk Kraus BCM_GPIO_LOCK(sc); 1079c28b681cSSvatopluk Kraus bcm_gpio_modify(sc, BCM_GPIO_GPREN(bank), bgi->bgi_mask, 1080c28b681cSSvatopluk Kraus mode == GPIO_INTR_EDGE_RISING || mode == GPIO_INTR_EDGE_BOTH); 1081c28b681cSSvatopluk Kraus bcm_gpio_modify(sc, BCM_GPIO_GPFEN(bank), bgi->bgi_mask, 1082c28b681cSSvatopluk Kraus mode == GPIO_INTR_EDGE_FALLING || mode == GPIO_INTR_EDGE_BOTH); 1083c28b681cSSvatopluk Kraus bcm_gpio_modify(sc, BCM_GPIO_GPHEN(bank), bgi->bgi_mask, 1084c28b681cSSvatopluk Kraus mode == GPIO_INTR_LEVEL_HIGH); 1085c28b681cSSvatopluk Kraus bcm_gpio_modify(sc, BCM_GPIO_GPLEN(bank), bgi->bgi_mask, 1086c28b681cSSvatopluk Kraus mode == GPIO_INTR_LEVEL_LOW); 1087c28b681cSSvatopluk Kraus bgi->bgi_mode = mode; 1088c28b681cSSvatopluk Kraus BCM_GPIO_UNLOCK(sc); 1089c28b681cSSvatopluk Kraus } 1090c28b681cSSvatopluk Kraus 1091c28b681cSSvatopluk Kraus static void 109289de2fb6SSvatopluk Kraus bcm_gpio_pic_disable_intr(device_t dev, struct intr_irqsrc *isrc) 109389de2fb6SSvatopluk Kraus { 109489de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 109589de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 109689de2fb6SSvatopluk Kraus 109789de2fb6SSvatopluk Kraus bcm_gpio_isrc_mask(sc, bgi); 109889de2fb6SSvatopluk Kraus } 109989de2fb6SSvatopluk Kraus 110089de2fb6SSvatopluk Kraus static void 110189de2fb6SSvatopluk Kraus bcm_gpio_pic_enable_intr(device_t dev, struct intr_irqsrc *isrc) 110289de2fb6SSvatopluk Kraus { 110389de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 110489de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 110589de2fb6SSvatopluk Kraus 110689de2fb6SSvatopluk Kraus arm_irq_memory_barrier(bgi->bgi_irq); 110789de2fb6SSvatopluk Kraus bcm_gpio_isrc_unmask(sc, bgi); 110889de2fb6SSvatopluk Kraus } 110989de2fb6SSvatopluk Kraus 111089de2fb6SSvatopluk Kraus static int 11116247277aSSvatopluk Kraus bcm_gpio_pic_map_fdt(struct bcm_gpio_softc *sc, struct intr_map_data_fdt *daf, 1112c28b681cSSvatopluk Kraus u_int *irqp, uint32_t *modep) 111389de2fb6SSvatopluk Kraus { 111489de2fb6SSvatopluk Kraus u_int irq; 1115c28b681cSSvatopluk Kraus uint32_t mode, bank; 111689de2fb6SSvatopluk Kraus 111789de2fb6SSvatopluk Kraus /* 111889de2fb6SSvatopluk Kraus * The first cell is the interrupt number. 111989de2fb6SSvatopluk Kraus * The second cell is used to specify flags: 112089de2fb6SSvatopluk Kraus * bits[3:0] trigger type and level flags: 112189de2fb6SSvatopluk Kraus * 1 = low-to-high edge triggered. 112289de2fb6SSvatopluk Kraus * 2 = high-to-low edge triggered. 112389de2fb6SSvatopluk Kraus * 4 = active high level-sensitive. 112489de2fb6SSvatopluk Kraus * 8 = active low level-sensitive. 112589de2fb6SSvatopluk Kraus */ 11266247277aSSvatopluk Kraus if (daf->ncells != 2) 112789de2fb6SSvatopluk Kraus return (EINVAL); 112889de2fb6SSvatopluk Kraus 11296247277aSSvatopluk Kraus irq = daf->cells[0]; 113089de2fb6SSvatopluk Kraus if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq)) 113189de2fb6SSvatopluk Kraus return (EINVAL); 113289de2fb6SSvatopluk Kraus 1133c28b681cSSvatopluk Kraus /* Only reasonable modes are supported. */ 113489de2fb6SSvatopluk Kraus bank = BCM_GPIO_BANK(irq); 11356247277aSSvatopluk Kraus if (daf->cells[1] == 1) 1136c28b681cSSvatopluk Kraus mode = GPIO_INTR_EDGE_RISING; 11376247277aSSvatopluk Kraus else if (daf->cells[1] == 2) 1138c28b681cSSvatopluk Kraus mode = GPIO_INTR_EDGE_FALLING; 11396247277aSSvatopluk Kraus else if (daf->cells[1] == 3) 1140c28b681cSSvatopluk Kraus mode = GPIO_INTR_EDGE_BOTH; 11416247277aSSvatopluk Kraus else if (daf->cells[1] == 4) 1142c28b681cSSvatopluk Kraus mode = GPIO_INTR_LEVEL_HIGH; 11436247277aSSvatopluk Kraus else if (daf->cells[1] == 8) 1144c28b681cSSvatopluk Kraus mode = GPIO_INTR_LEVEL_LOW; 114589de2fb6SSvatopluk Kraus else 114689de2fb6SSvatopluk Kraus return (EINVAL); 114789de2fb6SSvatopluk Kraus 114889de2fb6SSvatopluk Kraus *irqp = irq; 1149c28b681cSSvatopluk Kraus if (modep != NULL) 1150c28b681cSSvatopluk Kraus *modep = mode; 115189de2fb6SSvatopluk Kraus return (0); 115289de2fb6SSvatopluk Kraus } 115389de2fb6SSvatopluk Kraus 115489de2fb6SSvatopluk Kraus static int 11556247277aSSvatopluk Kraus bcm_gpio_pic_map_gpio(struct bcm_gpio_softc *sc, struct intr_map_data_gpio *dag, 11566247277aSSvatopluk Kraus u_int *irqp, uint32_t *modep) 11576247277aSSvatopluk Kraus { 11586247277aSSvatopluk Kraus u_int irq; 11596247277aSSvatopluk Kraus uint32_t mode; 11606247277aSSvatopluk Kraus 11616247277aSSvatopluk Kraus irq = dag->gpio_pin_num; 11626247277aSSvatopluk Kraus if (irq >= BCM_GPIO_PINS || bcm_gpio_pin_is_ro(sc, irq)) 11636247277aSSvatopluk Kraus return (EINVAL); 11646247277aSSvatopluk Kraus 11656247277aSSvatopluk Kraus mode = dag->gpio_intr_mode; 11666247277aSSvatopluk Kraus if (mode != GPIO_INTR_LEVEL_LOW && mode != GPIO_INTR_LEVEL_HIGH && 11676247277aSSvatopluk Kraus mode != GPIO_INTR_EDGE_RISING && mode != GPIO_INTR_EDGE_FALLING && 11686247277aSSvatopluk Kraus mode != GPIO_INTR_EDGE_BOTH) 11696247277aSSvatopluk Kraus return (EINVAL); 11706247277aSSvatopluk Kraus 11716247277aSSvatopluk Kraus *irqp = irq; 11726247277aSSvatopluk Kraus if (modep != NULL) 11736247277aSSvatopluk Kraus *modep = mode; 11746247277aSSvatopluk Kraus return (0); 11756247277aSSvatopluk Kraus } 11766247277aSSvatopluk Kraus 11776247277aSSvatopluk Kraus static int 11786247277aSSvatopluk Kraus bcm_gpio_pic_map(struct bcm_gpio_softc *sc, struct intr_map_data *data, 11796247277aSSvatopluk Kraus u_int *irqp, uint32_t *modep) 11806247277aSSvatopluk Kraus { 11816247277aSSvatopluk Kraus 11826247277aSSvatopluk Kraus switch (data->type) { 11836247277aSSvatopluk Kraus case INTR_MAP_DATA_FDT: 11846247277aSSvatopluk Kraus return (bcm_gpio_pic_map_fdt(sc, 11856247277aSSvatopluk Kraus (struct intr_map_data_fdt *)data, irqp, modep)); 11866247277aSSvatopluk Kraus case INTR_MAP_DATA_GPIO: 11876247277aSSvatopluk Kraus return (bcm_gpio_pic_map_gpio(sc, 11886247277aSSvatopluk Kraus (struct intr_map_data_gpio *)data, irqp, modep)); 11896247277aSSvatopluk Kraus default: 11906247277aSSvatopluk Kraus return (ENOTSUP); 11916247277aSSvatopluk Kraus } 11926247277aSSvatopluk Kraus } 11936247277aSSvatopluk Kraus 11946247277aSSvatopluk Kraus static int 119589de2fb6SSvatopluk Kraus bcm_gpio_pic_map_intr(device_t dev, struct intr_map_data *data, 119689de2fb6SSvatopluk Kraus struct intr_irqsrc **isrcp) 119789de2fb6SSvatopluk Kraus { 119889de2fb6SSvatopluk Kraus int error; 119989de2fb6SSvatopluk Kraus u_int irq; 12006247277aSSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 120189de2fb6SSvatopluk Kraus 12026247277aSSvatopluk Kraus error = bcm_gpio_pic_map(sc, data, &irq, NULL); 120389de2fb6SSvatopluk Kraus if (error == 0) 120489de2fb6SSvatopluk Kraus *isrcp = &sc->sc_isrcs[irq].bgi_isrc; 120589de2fb6SSvatopluk Kraus return (error); 120689de2fb6SSvatopluk Kraus } 120789de2fb6SSvatopluk Kraus 120889de2fb6SSvatopluk Kraus static void 120989de2fb6SSvatopluk Kraus bcm_gpio_pic_post_filter(device_t dev, struct intr_irqsrc *isrc) 121089de2fb6SSvatopluk Kraus { 121189de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 121289de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 121389de2fb6SSvatopluk Kraus 121489de2fb6SSvatopluk Kraus if (bcm_gpio_isrc_is_level(bgi)) 121589de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(sc, bgi); 121689de2fb6SSvatopluk Kraus } 121789de2fb6SSvatopluk Kraus 121889de2fb6SSvatopluk Kraus static void 121989de2fb6SSvatopluk Kraus bcm_gpio_pic_post_ithread(device_t dev, struct intr_irqsrc *isrc) 122089de2fb6SSvatopluk Kraus { 122189de2fb6SSvatopluk Kraus 122289de2fb6SSvatopluk Kraus bcm_gpio_pic_enable_intr(dev, isrc); 122389de2fb6SSvatopluk Kraus } 122489de2fb6SSvatopluk Kraus 122589de2fb6SSvatopluk Kraus static void 122689de2fb6SSvatopluk Kraus bcm_gpio_pic_pre_ithread(device_t dev, struct intr_irqsrc *isrc) 122789de2fb6SSvatopluk Kraus { 122889de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 122989de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 123089de2fb6SSvatopluk Kraus 123189de2fb6SSvatopluk Kraus bcm_gpio_isrc_mask(sc, bgi); 123289de2fb6SSvatopluk Kraus if (bcm_gpio_isrc_is_level(bgi)) 123389de2fb6SSvatopluk Kraus bcm_gpio_isrc_eoi(sc, bgi); 123489de2fb6SSvatopluk Kraus } 123589de2fb6SSvatopluk Kraus 123689de2fb6SSvatopluk Kraus static int 123789de2fb6SSvatopluk Kraus bcm_gpio_pic_setup_intr(device_t dev, struct intr_irqsrc *isrc, 123889de2fb6SSvatopluk Kraus struct resource *res, struct intr_map_data *data) 123989de2fb6SSvatopluk Kraus { 124089de2fb6SSvatopluk Kraus u_int irq; 1241c28b681cSSvatopluk Kraus uint32_t mode; 124289de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc; 124389de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi; 124489de2fb6SSvatopluk Kraus 12456247277aSSvatopluk Kraus if (data == NULL) 124689de2fb6SSvatopluk Kraus return (ENOTSUP); 124789de2fb6SSvatopluk Kraus 124889de2fb6SSvatopluk Kraus sc = device_get_softc(dev); 124989de2fb6SSvatopluk Kraus bgi = (struct bcm_gpio_irqsrc *)isrc; 125089de2fb6SSvatopluk Kraus 125189de2fb6SSvatopluk Kraus /* Get and check config for an interrupt. */ 12526247277aSSvatopluk Kraus if (bcm_gpio_pic_map(sc, data, &irq, &mode) != 0 || bgi->bgi_irq != irq) 125389de2fb6SSvatopluk Kraus return (EINVAL); 125489de2fb6SSvatopluk Kraus 125589de2fb6SSvatopluk Kraus /* 125689de2fb6SSvatopluk Kraus * If this is a setup for another handler, 125789de2fb6SSvatopluk Kraus * only check that its configuration match. 125889de2fb6SSvatopluk Kraus */ 125989de2fb6SSvatopluk Kraus if (isrc->isrc_handlers != 0) 1260c28b681cSSvatopluk Kraus return (bgi->bgi_mode == mode ? 0 : EINVAL); 126189de2fb6SSvatopluk Kraus 1262c28b681cSSvatopluk Kraus bcm_gpio_pic_config_intr(sc, bgi, mode); 126389de2fb6SSvatopluk Kraus return (0); 126489de2fb6SSvatopluk Kraus } 126589de2fb6SSvatopluk Kraus 126689de2fb6SSvatopluk Kraus static int 126789de2fb6SSvatopluk Kraus bcm_gpio_pic_teardown_intr(device_t dev, struct intr_irqsrc *isrc, 126889de2fb6SSvatopluk Kraus struct resource *res, struct intr_map_data *data) 126989de2fb6SSvatopluk Kraus { 127089de2fb6SSvatopluk Kraus struct bcm_gpio_softc *sc = device_get_softc(dev); 127189de2fb6SSvatopluk Kraus struct bcm_gpio_irqsrc *bgi = (struct bcm_gpio_irqsrc *)isrc; 127289de2fb6SSvatopluk Kraus 1273c28b681cSSvatopluk Kraus if (isrc->isrc_handlers == 0) 1274c28b681cSSvatopluk Kraus bcm_gpio_pic_config_intr(sc, bgi, GPIO_INTR_CONFORM); 127589de2fb6SSvatopluk Kraus return (0); 127689de2fb6SSvatopluk Kraus } 127789de2fb6SSvatopluk Kraus 127889de2fb6SSvatopluk Kraus #else 127912471cecSLuiz Otavio O Souza static uint32_t 128012471cecSLuiz Otavio O Souza bcm_gpio_intr_reg(struct bcm_gpio_softc *sc, unsigned int irq, uint32_t bank) 128112471cecSLuiz Otavio O Souza { 128212471cecSLuiz Otavio O Souza 128312471cecSLuiz Otavio O Souza if (irq > BCM_GPIO_PINS) 128412471cecSLuiz Otavio O Souza return (0); 128512471cecSLuiz Otavio O Souza if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_LEVEL) { 128612471cecSLuiz Otavio O Souza if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW) 128712471cecSLuiz Otavio O Souza return (BCM_GPIO_GPLEN(bank)); 128812471cecSLuiz Otavio O Souza else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH) 128912471cecSLuiz Otavio O Souza return (BCM_GPIO_GPHEN(bank)); 129012471cecSLuiz Otavio O Souza } else if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_EDGE) { 129112471cecSLuiz Otavio O Souza if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW) 129212471cecSLuiz Otavio O Souza return (BCM_GPIO_GPFEN(bank)); 129312471cecSLuiz Otavio O Souza else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH) 129412471cecSLuiz Otavio O Souza return (BCM_GPIO_GPREN(bank)); 129512471cecSLuiz Otavio O Souza } 129612471cecSLuiz Otavio O Souza 129712471cecSLuiz Otavio O Souza return (0); 129812471cecSLuiz Otavio O Souza } 129912471cecSLuiz Otavio O Souza 130012471cecSLuiz Otavio O Souza static void 130112471cecSLuiz Otavio O Souza bcm_gpio_mask_irq(void *source) 130212471cecSLuiz Otavio O Souza { 130312471cecSLuiz Otavio O Souza uint32_t bank, mask, reg; 130412471cecSLuiz Otavio O Souza unsigned int irq; 130512471cecSLuiz Otavio O Souza 130612471cecSLuiz Otavio O Souza irq = (unsigned int)source; 130712471cecSLuiz Otavio O Souza if (irq > BCM_GPIO_PINS) 130812471cecSLuiz Otavio O Souza return; 130912471cecSLuiz Otavio O Souza if (bcm_gpio_pin_is_ro(bcm_gpio_sc, irq)) 131012471cecSLuiz Otavio O Souza return; 131112471cecSLuiz Otavio O Souza bank = BCM_GPIO_BANK(irq); 131212471cecSLuiz Otavio O Souza mask = BCM_GPIO_MASK(irq); 131312471cecSLuiz Otavio O Souza BCM_GPIO_LOCK(bcm_gpio_sc); 131412471cecSLuiz Otavio O Souza reg = bcm_gpio_intr_reg(bcm_gpio_sc, irq, bank); 131512471cecSLuiz Otavio O Souza if (reg != 0) 131612471cecSLuiz Otavio O Souza BCM_GPIO_CLEAR_BITS(bcm_gpio_sc, reg, mask); 131712471cecSLuiz Otavio O Souza BCM_GPIO_UNLOCK(bcm_gpio_sc); 131812471cecSLuiz Otavio O Souza } 131912471cecSLuiz Otavio O Souza 132012471cecSLuiz Otavio O Souza static void 132112471cecSLuiz Otavio O Souza bcm_gpio_unmask_irq(void *source) 132212471cecSLuiz Otavio O Souza { 132312471cecSLuiz Otavio O Souza uint32_t bank, mask, reg; 132412471cecSLuiz Otavio O Souza unsigned int irq; 132512471cecSLuiz Otavio O Souza 132612471cecSLuiz Otavio O Souza irq = (unsigned int)source; 132712471cecSLuiz Otavio O Souza if (irq > BCM_GPIO_PINS) 132812471cecSLuiz Otavio O Souza return; 132912471cecSLuiz Otavio O Souza if (bcm_gpio_pin_is_ro(bcm_gpio_sc, irq)) 133012471cecSLuiz Otavio O Souza return; 133112471cecSLuiz Otavio O Souza bank = BCM_GPIO_BANK(irq); 133212471cecSLuiz Otavio O Souza mask = BCM_GPIO_MASK(irq); 133312471cecSLuiz Otavio O Souza BCM_GPIO_LOCK(bcm_gpio_sc); 133412471cecSLuiz Otavio O Souza reg = bcm_gpio_intr_reg(bcm_gpio_sc, irq, bank); 133512471cecSLuiz Otavio O Souza if (reg != 0) 133612471cecSLuiz Otavio O Souza BCM_GPIO_SET_BITS(bcm_gpio_sc, reg, mask); 133712471cecSLuiz Otavio O Souza BCM_GPIO_UNLOCK(bcm_gpio_sc); 133812471cecSLuiz Otavio O Souza } 133912471cecSLuiz Otavio O Souza 134012471cecSLuiz Otavio O Souza static int 134112471cecSLuiz Otavio O Souza bcm_gpio_activate_resource(device_t bus, device_t child, int type, int rid, 134212471cecSLuiz Otavio O Souza struct resource *res) 134312471cecSLuiz Otavio O Souza { 134412471cecSLuiz Otavio O Souza int pin; 134512471cecSLuiz Otavio O Souza 134612471cecSLuiz Otavio O Souza if (type != SYS_RES_IRQ) 134712471cecSLuiz Otavio O Souza return (ENXIO); 134812471cecSLuiz Otavio O Souza /* Unmask the interrupt. */ 134912471cecSLuiz Otavio O Souza pin = rman_get_start(res); 135012471cecSLuiz Otavio O Souza bcm_gpio_unmask_irq((void *)pin); 135112471cecSLuiz Otavio O Souza 135212471cecSLuiz Otavio O Souza return (0); 135312471cecSLuiz Otavio O Souza } 135412471cecSLuiz Otavio O Souza 135512471cecSLuiz Otavio O Souza static int 135612471cecSLuiz Otavio O Souza bcm_gpio_deactivate_resource(device_t bus, device_t child, int type, int rid, 135712471cecSLuiz Otavio O Souza struct resource *res) 135812471cecSLuiz Otavio O Souza { 135912471cecSLuiz Otavio O Souza int pin; 136012471cecSLuiz Otavio O Souza 136112471cecSLuiz Otavio O Souza if (type != SYS_RES_IRQ) 136212471cecSLuiz Otavio O Souza return (ENXIO); 136312471cecSLuiz Otavio O Souza /* Mask the interrupt. */ 136412471cecSLuiz Otavio O Souza pin = rman_get_start(res); 136512471cecSLuiz Otavio O Souza bcm_gpio_mask_irq((void *)pin); 136612471cecSLuiz Otavio O Souza 136712471cecSLuiz Otavio O Souza return (0); 136812471cecSLuiz Otavio O Souza } 136912471cecSLuiz Otavio O Souza 137012471cecSLuiz Otavio O Souza static int 137112471cecSLuiz Otavio O Souza bcm_gpio_config_intr(device_t dev, int irq, enum intr_trigger trig, 137212471cecSLuiz Otavio O Souza enum intr_polarity pol) 137312471cecSLuiz Otavio O Souza { 137412471cecSLuiz Otavio O Souza int bank; 137512471cecSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 137612471cecSLuiz Otavio O Souza uint32_t mask, oldreg, reg; 137712471cecSLuiz Otavio O Souza 137812471cecSLuiz Otavio O Souza if (irq > BCM_GPIO_PINS) 137912471cecSLuiz Otavio O Souza return (EINVAL); 138012471cecSLuiz Otavio O Souza /* There is no standard trigger or polarity. */ 138112471cecSLuiz Otavio O Souza if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM) 138212471cecSLuiz Otavio O Souza return (EINVAL); 138312471cecSLuiz Otavio O Souza sc = device_get_softc(dev); 138412471cecSLuiz Otavio O Souza if (bcm_gpio_pin_is_ro(sc, irq)) 138512471cecSLuiz Otavio O Souza return (EINVAL); 138612471cecSLuiz Otavio O Souza bank = BCM_GPIO_BANK(irq); 138712471cecSLuiz Otavio O Souza mask = BCM_GPIO_MASK(irq); 138812471cecSLuiz Otavio O Souza BCM_GPIO_LOCK(sc); 138912471cecSLuiz Otavio O Souza oldreg = bcm_gpio_intr_reg(sc, irq, bank); 139012471cecSLuiz Otavio O Souza sc->sc_irq_trigger[irq] = trig; 139112471cecSLuiz Otavio O Souza sc->sc_irq_polarity[irq] = pol; 139212471cecSLuiz Otavio O Souza reg = bcm_gpio_intr_reg(sc, irq, bank); 139312471cecSLuiz Otavio O Souza if (reg != 0) 139412471cecSLuiz Otavio O Souza BCM_GPIO_SET_BITS(sc, reg, mask); 139512471cecSLuiz Otavio O Souza if (reg != oldreg && oldreg != 0) 139612471cecSLuiz Otavio O Souza BCM_GPIO_CLEAR_BITS(sc, oldreg, mask); 139712471cecSLuiz Otavio O Souza BCM_GPIO_UNLOCK(sc); 139812471cecSLuiz Otavio O Souza 139912471cecSLuiz Otavio O Souza return (0); 140012471cecSLuiz Otavio O Souza } 140112471cecSLuiz Otavio O Souza 140212471cecSLuiz Otavio O Souza static int 140312471cecSLuiz Otavio O Souza bcm_gpio_setup_intr(device_t bus, device_t child, struct resource *ires, 140412471cecSLuiz Otavio O Souza int flags, driver_filter_t *filt, driver_intr_t *handler, 140512471cecSLuiz Otavio O Souza void *arg, void **cookiep) 140612471cecSLuiz Otavio O Souza { 140712471cecSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 140812471cecSLuiz Otavio O Souza struct intr_event *event; 140912471cecSLuiz Otavio O Souza int pin, error; 141012471cecSLuiz Otavio O Souza 141112471cecSLuiz Otavio O Souza sc = device_get_softc(bus); 141212471cecSLuiz Otavio O Souza pin = rman_get_start(ires); 141312471cecSLuiz Otavio O Souza if (pin > BCM_GPIO_PINS) 141412471cecSLuiz Otavio O Souza panic("%s: bad pin %d", __func__, pin); 141512471cecSLuiz Otavio O Souza event = sc->sc_events[pin]; 141612471cecSLuiz Otavio O Souza if (event == NULL) { 141712471cecSLuiz Otavio O Souza error = intr_event_create(&event, (void *)pin, 0, pin, 141812471cecSLuiz Otavio O Souza bcm_gpio_mask_irq, bcm_gpio_unmask_irq, NULL, NULL, 141912471cecSLuiz Otavio O Souza "gpio%d pin%d:", device_get_unit(bus), pin); 142012471cecSLuiz Otavio O Souza if (error != 0) 142112471cecSLuiz Otavio O Souza return (error); 142212471cecSLuiz Otavio O Souza sc->sc_events[pin] = event; 142312471cecSLuiz Otavio O Souza } 142412471cecSLuiz Otavio O Souza intr_event_add_handler(event, device_get_nameunit(child), filt, 142512471cecSLuiz Otavio O Souza handler, arg, intr_priority(flags), flags, cookiep); 142612471cecSLuiz Otavio O Souza 142712471cecSLuiz Otavio O Souza return (0); 142812471cecSLuiz Otavio O Souza } 142912471cecSLuiz Otavio O Souza 143012471cecSLuiz Otavio O Souza static int 143112471cecSLuiz Otavio O Souza bcm_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires, 143212471cecSLuiz Otavio O Souza void *cookie) 143312471cecSLuiz Otavio O Souza { 143412471cecSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 143512471cecSLuiz Otavio O Souza int pin, err; 143612471cecSLuiz Otavio O Souza 143712471cecSLuiz Otavio O Souza sc = device_get_softc(dev); 143812471cecSLuiz Otavio O Souza pin = rman_get_start(ires); 143912471cecSLuiz Otavio O Souza if (pin > BCM_GPIO_PINS) 144012471cecSLuiz Otavio O Souza panic("%s: bad pin %d", __func__, pin); 144112471cecSLuiz Otavio O Souza if (sc->sc_events[pin] == NULL) 144212471cecSLuiz Otavio O Souza panic("Trying to teardown unoccupied IRQ"); 144312471cecSLuiz Otavio O Souza err = intr_event_remove_handler(cookie); 144412471cecSLuiz Otavio O Souza if (!err) 144512471cecSLuiz Otavio O Souza sc->sc_events[pin] = NULL; 144612471cecSLuiz Otavio O Souza 144712471cecSLuiz Otavio O Souza return (err); 144812471cecSLuiz Otavio O Souza } 144989de2fb6SSvatopluk Kraus #endif 145012471cecSLuiz Otavio O Souza 14518c705c2cSLuiz Otavio O Souza static phandle_t 14528c705c2cSLuiz Otavio O Souza bcm_gpio_get_node(device_t bus, device_t dev) 14538c705c2cSLuiz Otavio O Souza { 14548c705c2cSLuiz Otavio O Souza 14558c705c2cSLuiz Otavio O Souza /* We only have one child, the GPIO bus, which needs our own node. */ 14568c705c2cSLuiz Otavio O Souza return (ofw_bus_get_node(bus)); 14578c705c2cSLuiz Otavio O Souza } 14588c705c2cSLuiz Otavio O Souza 14594063f925SOleksandr Tymoshenko static device_method_t bcm_gpio_methods[] = { 14604063f925SOleksandr Tymoshenko /* Device interface */ 14614063f925SOleksandr Tymoshenko DEVMETHOD(device_probe, bcm_gpio_probe), 14624063f925SOleksandr Tymoshenko DEVMETHOD(device_attach, bcm_gpio_attach), 14634063f925SOleksandr Tymoshenko DEVMETHOD(device_detach, bcm_gpio_detach), 14644063f925SOleksandr Tymoshenko 14654063f925SOleksandr Tymoshenko /* GPIO protocol */ 14667836352bSLuiz Otavio O Souza DEVMETHOD(gpio_get_bus, bcm_gpio_get_bus), 14674063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_max, bcm_gpio_pin_max), 14684063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getname, bcm_gpio_pin_getname), 14694063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getflags, bcm_gpio_pin_getflags), 14704063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getcaps, bcm_gpio_pin_getcaps), 14714063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_setflags, bcm_gpio_pin_setflags), 14724063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_get, bcm_gpio_pin_get), 14734063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set), 14744063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle), 14754063f925SOleksandr Tymoshenko 147659c3cb81SAndrew Turner #ifdef INTRNG 147789de2fb6SSvatopluk Kraus /* Interrupt controller interface */ 147889de2fb6SSvatopluk Kraus DEVMETHOD(pic_disable_intr, bcm_gpio_pic_disable_intr), 147989de2fb6SSvatopluk Kraus DEVMETHOD(pic_enable_intr, bcm_gpio_pic_enable_intr), 148089de2fb6SSvatopluk Kraus DEVMETHOD(pic_map_intr, bcm_gpio_pic_map_intr), 148189de2fb6SSvatopluk Kraus DEVMETHOD(pic_post_filter, bcm_gpio_pic_post_filter), 148289de2fb6SSvatopluk Kraus DEVMETHOD(pic_post_ithread, bcm_gpio_pic_post_ithread), 148389de2fb6SSvatopluk Kraus DEVMETHOD(pic_pre_ithread, bcm_gpio_pic_pre_ithread), 148489de2fb6SSvatopluk Kraus DEVMETHOD(pic_setup_intr, bcm_gpio_pic_setup_intr), 148589de2fb6SSvatopluk Kraus DEVMETHOD(pic_teardown_intr, bcm_gpio_pic_teardown_intr), 148689de2fb6SSvatopluk Kraus #else 148712471cecSLuiz Otavio O Souza /* Bus interface */ 148812471cecSLuiz Otavio O Souza DEVMETHOD(bus_activate_resource, bcm_gpio_activate_resource), 148912471cecSLuiz Otavio O Souza DEVMETHOD(bus_deactivate_resource, bcm_gpio_deactivate_resource), 149012471cecSLuiz Otavio O Souza DEVMETHOD(bus_config_intr, bcm_gpio_config_intr), 149112471cecSLuiz Otavio O Souza DEVMETHOD(bus_setup_intr, bcm_gpio_setup_intr), 149212471cecSLuiz Otavio O Souza DEVMETHOD(bus_teardown_intr, bcm_gpio_teardown_intr), 149389de2fb6SSvatopluk Kraus #endif 14948c705c2cSLuiz Otavio O Souza /* ofw_bus interface */ 14958c705c2cSLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node), 14968c705c2cSLuiz Otavio O Souza 14974063f925SOleksandr Tymoshenko DEVMETHOD_END 14984063f925SOleksandr Tymoshenko }; 14994063f925SOleksandr Tymoshenko 15004063f925SOleksandr Tymoshenko static devclass_t bcm_gpio_devclass; 15014063f925SOleksandr Tymoshenko 15024063f925SOleksandr Tymoshenko static driver_t bcm_gpio_driver = { 15034063f925SOleksandr Tymoshenko "gpio", 15044063f925SOleksandr Tymoshenko bcm_gpio_methods, 15054063f925SOleksandr Tymoshenko sizeof(struct bcm_gpio_softc), 15064063f925SOleksandr Tymoshenko }; 15074063f925SOleksandr Tymoshenko 15084063f925SOleksandr Tymoshenko DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0); 1509