14063f925SOleksandr Tymoshenko /*- 24063f925SOleksandr Tymoshenko * Copyright (c) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> 34063f925SOleksandr Tymoshenko * Copyright (c) 2012 Luiz Otavio O Souza. 44063f925SOleksandr Tymoshenko * All rights reserved. 54063f925SOleksandr Tymoshenko * 64063f925SOleksandr Tymoshenko * Redistribution and use in source and binary forms, with or without 74063f925SOleksandr Tymoshenko * modification, are permitted provided that the following conditions 84063f925SOleksandr Tymoshenko * are met: 94063f925SOleksandr Tymoshenko * 1. Redistributions of source code must retain the above copyright 104063f925SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer. 114063f925SOleksandr Tymoshenko * 2. Redistributions in binary form must reproduce the above copyright 124063f925SOleksandr Tymoshenko * notice, this list of conditions and the following disclaimer in the 134063f925SOleksandr Tymoshenko * documentation and/or other materials provided with the distribution. 144063f925SOleksandr Tymoshenko * 154063f925SOleksandr Tymoshenko * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 164063f925SOleksandr Tymoshenko * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 174063f925SOleksandr Tymoshenko * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 184063f925SOleksandr Tymoshenko * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 194063f925SOleksandr Tymoshenko * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 204063f925SOleksandr Tymoshenko * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 214063f925SOleksandr Tymoshenko * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 224063f925SOleksandr Tymoshenko * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 234063f925SOleksandr Tymoshenko * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 244063f925SOleksandr Tymoshenko * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 254063f925SOleksandr Tymoshenko * SUCH DAMAGE. 264063f925SOleksandr Tymoshenko * 274063f925SOleksandr Tymoshenko */ 284063f925SOleksandr Tymoshenko #include <sys/cdefs.h> 294063f925SOleksandr Tymoshenko __FBSDID("$FreeBSD$"); 304063f925SOleksandr Tymoshenko 314063f925SOleksandr Tymoshenko #include <sys/param.h> 324063f925SOleksandr Tymoshenko #include <sys/systm.h> 334063f925SOleksandr Tymoshenko #include <sys/bus.h> 344063f925SOleksandr Tymoshenko #include <sys/gpio.h> 35*12471cecSLuiz Otavio O Souza #include <sys/interrupt.h> 36e74d6e2aSLuiz Otavio O Souza #include <sys/kernel.h> 37e74d6e2aSLuiz Otavio O Souza #include <sys/lock.h> 38e74d6e2aSLuiz Otavio O Souza #include <sys/module.h> 39e74d6e2aSLuiz Otavio O Souza #include <sys/mutex.h> 40e74d6e2aSLuiz Otavio O Souza #include <sys/rman.h> 4190576f54SOleksandr Tymoshenko #include <sys/sysctl.h> 424063f925SOleksandr Tymoshenko 434063f925SOleksandr Tymoshenko #include <machine/bus.h> 444063f925SOleksandr Tymoshenko 457836352bSLuiz Otavio O Souza #include <dev/gpio/gpiobusvar.h> 464063f925SOleksandr Tymoshenko #include <dev/ofw/ofw_bus.h> 474063f925SOleksandr Tymoshenko 4844d06d8dSLuiz Otavio O Souza #include <arm/broadcom/bcm2835/bcm2835_gpio.h> 4944d06d8dSLuiz Otavio O Souza 504063f925SOleksandr Tymoshenko #include "gpio_if.h" 514063f925SOleksandr Tymoshenko 524063f925SOleksandr Tymoshenko #ifdef DEBUG 534063f925SOleksandr Tymoshenko #define dprintf(fmt, args...) do { printf("%s(): ", __func__); \ 544063f925SOleksandr Tymoshenko printf(fmt,##args); } while (0) 554063f925SOleksandr Tymoshenko #else 564063f925SOleksandr Tymoshenko #define dprintf(fmt, args...) 574063f925SOleksandr Tymoshenko #endif 584063f925SOleksandr Tymoshenko 59c2136589SLuiz Otavio O Souza #define BCM_GPIO_IRQS 4 604063f925SOleksandr Tymoshenko #define BCM_GPIO_PINS 54 61*12471cecSLuiz Otavio O Souza #define BCM_GPIO_PINS_PER_BANK 32 624063f925SOleksandr Tymoshenko #define BCM_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ 634063f925SOleksandr Tymoshenko GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) 644063f925SOleksandr Tymoshenko 65c2136589SLuiz Otavio O Souza static struct resource_spec bcm_gpio_res_spec[] = { 66c2136589SLuiz Otavio O Souza { SYS_RES_MEMORY, 0, RF_ACTIVE }, 67c2136589SLuiz Otavio O Souza { SYS_RES_IRQ, 0, RF_ACTIVE }, 68c2136589SLuiz Otavio O Souza { SYS_RES_IRQ, 1, RF_ACTIVE }, 69c2136589SLuiz Otavio O Souza { SYS_RES_IRQ, 2, RF_ACTIVE }, 70c2136589SLuiz Otavio O Souza { SYS_RES_IRQ, 3, RF_ACTIVE }, 71c2136589SLuiz Otavio O Souza { -1, 0, 0 } 72c2136589SLuiz Otavio O Souza }; 73c2136589SLuiz Otavio O Souza 7490576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl { 7590576f54SOleksandr Tymoshenko struct bcm_gpio_softc *sc; 7690576f54SOleksandr Tymoshenko uint32_t pin; 7790576f54SOleksandr Tymoshenko }; 7890576f54SOleksandr Tymoshenko 794063f925SOleksandr Tymoshenko struct bcm_gpio_softc { 804063f925SOleksandr Tymoshenko device_t sc_dev; 817836352bSLuiz Otavio O Souza device_t sc_busdev; 824063f925SOleksandr Tymoshenko struct mtx sc_mtx; 83c2136589SLuiz Otavio O Souza struct resource * sc_res[BCM_GPIO_IRQS + 1]; 844063f925SOleksandr Tymoshenko bus_space_tag_t sc_bst; 854063f925SOleksandr Tymoshenko bus_space_handle_t sc_bsh; 86*12471cecSLuiz Otavio O Souza void * sc_intrhand[BCM_GPIO_IRQS]; 874063f925SOleksandr Tymoshenko int sc_gpio_npins; 884063f925SOleksandr Tymoshenko int sc_ro_npins; 894063f925SOleksandr Tymoshenko int sc_ro_pins[BCM_GPIO_PINS]; 904063f925SOleksandr Tymoshenko struct gpio_pin sc_gpio_pins[BCM_GPIO_PINS]; 91*12471cecSLuiz Otavio O Souza struct intr_event * sc_events[BCM_GPIO_PINS]; 9290576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl sc_sysctl[BCM_GPIO_PINS]; 93*12471cecSLuiz Otavio O Souza enum intr_trigger sc_irq_trigger[BCM_GPIO_PINS]; 94*12471cecSLuiz Otavio O Souza enum intr_polarity sc_irq_polarity[BCM_GPIO_PINS]; 954063f925SOleksandr Tymoshenko }; 964063f925SOleksandr Tymoshenko 974063f925SOleksandr Tymoshenko enum bcm_gpio_pud { 984063f925SOleksandr Tymoshenko BCM_GPIO_NONE, 994063f925SOleksandr Tymoshenko BCM_GPIO_PULLDOWN, 1004063f925SOleksandr Tymoshenko BCM_GPIO_PULLUP, 1014063f925SOleksandr Tymoshenko }; 1024063f925SOleksandr Tymoshenko 103*12471cecSLuiz Otavio O Souza #define BCM_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) 104*12471cecSLuiz Otavio O Souza #define BCM_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) 105*12471cecSLuiz Otavio O Souza #define BCM_GPIO_LOCK_ASSERT(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) 1064063f925SOleksandr Tymoshenko #define BCM_GPIO_WRITE(_sc, _off, _val) \ 107*12471cecSLuiz Otavio O Souza bus_space_write_4((_sc)->sc_bst, (_sc)->sc_bsh, _off, _val) 1084063f925SOleksandr Tymoshenko #define BCM_GPIO_READ(_sc, _off) \ 109*12471cecSLuiz Otavio O Souza bus_space_read_4((_sc)->sc_bst, (_sc)->sc_bsh, _off) 110*12471cecSLuiz Otavio O Souza #define BCM_GPIO_CLEAR_BITS(_sc, _off, _bits) \ 111*12471cecSLuiz Otavio O Souza BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) & ~(_bits)) 112*12471cecSLuiz Otavio O Souza #define BCM_GPIO_SET_BITS(_sc, _off, _bits) \ 113*12471cecSLuiz Otavio O Souza BCM_GPIO_WRITE(_sc, _off, BCM_GPIO_READ(_sc, _off) | _bits) 114*12471cecSLuiz Otavio O Souza #define BCM_GPIO_BANK(a) (a / BCM_GPIO_PINS_PER_BANK) 115*12471cecSLuiz Otavio O Souza #define BCM_GPIO_MASK(a) (1U << (a % BCM_GPIO_PINS_PER_BANK)) 116*12471cecSLuiz Otavio O Souza 117*12471cecSLuiz Otavio O Souza #define BCM_GPIO_GPFSEL(_bank) (0x00 + _bank * 4) /* Function Select */ 118*12471cecSLuiz Otavio O Souza #define BCM_GPIO_GPSET(_bank) (0x1c + _bank * 4) /* Pin Out Set */ 119*12471cecSLuiz Otavio O Souza #define BCM_GPIO_GPCLR(_bank) (0x28 + _bank * 4) /* Pin Out Clear */ 120*12471cecSLuiz Otavio O Souza #define BCM_GPIO_GPLEV(_bank) (0x34 + _bank * 4) /* Pin Level */ 121*12471cecSLuiz Otavio O Souza #define BCM_GPIO_GPEDS(_bank) (0x40 + _bank * 4) /* Event Status */ 122*12471cecSLuiz Otavio O Souza #define BCM_GPIO_GPREN(_bank) (0x4c + _bank * 4) /* Rising Edge irq */ 123*12471cecSLuiz Otavio O Souza #define BCM_GPIO_GPFEN(_bank) (0x58 + _bank * 4) /* Falling Edge irq */ 124*12471cecSLuiz Otavio O Souza #define BCM_GPIO_GPHEN(_bank) (0x64 + _bank * 4) /* High Level irq */ 125*12471cecSLuiz Otavio O Souza #define BCM_GPIO_GPLEN(_bank) (0x70 + _bank * 4) /* Low Level irq */ 126*12471cecSLuiz Otavio O Souza #define BCM_GPIO_GPAREN(_bank) (0x7c + _bank * 4) /* Async Rising Edge */ 127*12471cecSLuiz Otavio O Souza #define BCM_GPIO_GPAFEN(_bank) (0x88 + _bank * 4) /* Async Falling Egde */ 128*12471cecSLuiz Otavio O Souza #define BCM_GPIO_GPPUD(_bank) (0x94) /* Pin Pull up/down */ 129*12471cecSLuiz Otavio O Souza #define BCM_GPIO_GPPUDCLK(_bank) (0x98 + _bank * 4) /* Pin Pull up clock */ 130*12471cecSLuiz Otavio O Souza 131*12471cecSLuiz Otavio O Souza static struct bcm_gpio_softc *bcm_gpio_sc = NULL; 1324063f925SOleksandr Tymoshenko 1334063f925SOleksandr Tymoshenko static int 1344063f925SOleksandr Tymoshenko bcm_gpio_pin_is_ro(struct bcm_gpio_softc *sc, int pin) 1354063f925SOleksandr Tymoshenko { 1364063f925SOleksandr Tymoshenko int i; 1374063f925SOleksandr Tymoshenko 1384063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_ro_npins; i++) 1394063f925SOleksandr Tymoshenko if (pin == sc->sc_ro_pins[i]) 1404063f925SOleksandr Tymoshenko return (1); 1414063f925SOleksandr Tymoshenko return (0); 1424063f925SOleksandr Tymoshenko } 1434063f925SOleksandr Tymoshenko 1444063f925SOleksandr Tymoshenko static uint32_t 1454063f925SOleksandr Tymoshenko bcm_gpio_get_function(struct bcm_gpio_softc *sc, uint32_t pin) 1464063f925SOleksandr Tymoshenko { 14790576f54SOleksandr Tymoshenko uint32_t bank, func, offset; 1484063f925SOleksandr Tymoshenko 1494063f925SOleksandr Tymoshenko /* Five banks, 10 pins per bank, 3 bits per pin. */ 1504063f925SOleksandr Tymoshenko bank = pin / 10; 1514063f925SOleksandr Tymoshenko offset = (pin - bank * 10) * 3; 1524063f925SOleksandr Tymoshenko 1534063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 15490576f54SOleksandr Tymoshenko func = (BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)) >> offset) & 7; 1554063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 1564063f925SOleksandr Tymoshenko 15790576f54SOleksandr Tymoshenko return (func); 15890576f54SOleksandr Tymoshenko } 15990576f54SOleksandr Tymoshenko 16090576f54SOleksandr Tymoshenko static void 16190576f54SOleksandr Tymoshenko bcm_gpio_func_str(uint32_t nfunc, char *buf, int bufsize) 16290576f54SOleksandr Tymoshenko { 16390576f54SOleksandr Tymoshenko 16490576f54SOleksandr Tymoshenko switch (nfunc) { 1654063f925SOleksandr Tymoshenko case BCM_GPIO_INPUT: 16690576f54SOleksandr Tymoshenko strncpy(buf, "input", bufsize); 1674063f925SOleksandr Tymoshenko break; 1684063f925SOleksandr Tymoshenko case BCM_GPIO_OUTPUT: 16990576f54SOleksandr Tymoshenko strncpy(buf, "output", bufsize); 1704063f925SOleksandr Tymoshenko break; 1714063f925SOleksandr Tymoshenko case BCM_GPIO_ALT0: 17290576f54SOleksandr Tymoshenko strncpy(buf, "alt0", bufsize); 1734063f925SOleksandr Tymoshenko break; 1744063f925SOleksandr Tymoshenko case BCM_GPIO_ALT1: 17590576f54SOleksandr Tymoshenko strncpy(buf, "alt1", bufsize); 1764063f925SOleksandr Tymoshenko break; 1774063f925SOleksandr Tymoshenko case BCM_GPIO_ALT2: 17890576f54SOleksandr Tymoshenko strncpy(buf, "alt2", bufsize); 1794063f925SOleksandr Tymoshenko break; 1804063f925SOleksandr Tymoshenko case BCM_GPIO_ALT3: 18190576f54SOleksandr Tymoshenko strncpy(buf, "alt3", bufsize); 1824063f925SOleksandr Tymoshenko break; 1834063f925SOleksandr Tymoshenko case BCM_GPIO_ALT4: 18490576f54SOleksandr Tymoshenko strncpy(buf, "alt4", bufsize); 1854063f925SOleksandr Tymoshenko break; 1864063f925SOleksandr Tymoshenko case BCM_GPIO_ALT5: 18790576f54SOleksandr Tymoshenko strncpy(buf, "alt5", bufsize); 1884063f925SOleksandr Tymoshenko break; 18990576f54SOleksandr Tymoshenko default: 19090576f54SOleksandr Tymoshenko strncpy(buf, "invalid", bufsize); 1914063f925SOleksandr Tymoshenko } 19290576f54SOleksandr Tymoshenko } 1934063f925SOleksandr Tymoshenko 19490576f54SOleksandr Tymoshenko static int 19590576f54SOleksandr Tymoshenko bcm_gpio_str_func(char *func, uint32_t *nfunc) 19690576f54SOleksandr Tymoshenko { 19790576f54SOleksandr Tymoshenko 19890576f54SOleksandr Tymoshenko if (strcasecmp(func, "input") == 0) 19990576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_INPUT; 20090576f54SOleksandr Tymoshenko else if (strcasecmp(func, "output") == 0) 20190576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_OUTPUT; 20290576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt0") == 0) 20390576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT0; 20490576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt1") == 0) 20590576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT1; 20690576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt2") == 0) 20790576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT2; 20890576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt3") == 0) 20990576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT3; 21090576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt4") == 0) 21190576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT4; 21290576f54SOleksandr Tymoshenko else if (strcasecmp(func, "alt5") == 0) 21390576f54SOleksandr Tymoshenko *nfunc = BCM_GPIO_ALT5; 21490576f54SOleksandr Tymoshenko else 21590576f54SOleksandr Tymoshenko return (-1); 21690576f54SOleksandr Tymoshenko 21790576f54SOleksandr Tymoshenko return (0); 21890576f54SOleksandr Tymoshenko } 21990576f54SOleksandr Tymoshenko 22090576f54SOleksandr Tymoshenko static uint32_t 22190576f54SOleksandr Tymoshenko bcm_gpio_func_flag(uint32_t nfunc) 22290576f54SOleksandr Tymoshenko { 22390576f54SOleksandr Tymoshenko 22490576f54SOleksandr Tymoshenko switch (nfunc) { 2254063f925SOleksandr Tymoshenko case BCM_GPIO_INPUT: 2264063f925SOleksandr Tymoshenko return (GPIO_PIN_INPUT); 2274063f925SOleksandr Tymoshenko case BCM_GPIO_OUTPUT: 2284063f925SOleksandr Tymoshenko return (GPIO_PIN_OUTPUT); 2294063f925SOleksandr Tymoshenko } 2304063f925SOleksandr Tymoshenko return (0); 2314063f925SOleksandr Tymoshenko } 2324063f925SOleksandr Tymoshenko 2334063f925SOleksandr Tymoshenko static void 2344063f925SOleksandr Tymoshenko bcm_gpio_set_function(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t f) 2354063f925SOleksandr Tymoshenko { 2364063f925SOleksandr Tymoshenko uint32_t bank, data, offset; 2374063f925SOleksandr Tymoshenko 23890576f54SOleksandr Tymoshenko /* Must be called with lock held. */ 23990576f54SOleksandr Tymoshenko BCM_GPIO_LOCK_ASSERT(sc); 24090576f54SOleksandr Tymoshenko 2414063f925SOleksandr Tymoshenko /* Five banks, 10 pins per bank, 3 bits per pin. */ 2424063f925SOleksandr Tymoshenko bank = pin / 10; 2434063f925SOleksandr Tymoshenko offset = (pin - bank * 10) * 3; 2444063f925SOleksandr Tymoshenko 2454063f925SOleksandr Tymoshenko data = BCM_GPIO_READ(sc, BCM_GPIO_GPFSEL(bank)); 2464063f925SOleksandr Tymoshenko data &= ~(7 << offset); 2474063f925SOleksandr Tymoshenko data |= (f << offset); 2484063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPFSEL(bank), data); 2494063f925SOleksandr Tymoshenko } 2504063f925SOleksandr Tymoshenko 2514063f925SOleksandr Tymoshenko static void 2524063f925SOleksandr Tymoshenko bcm_gpio_set_pud(struct bcm_gpio_softc *sc, uint32_t pin, uint32_t state) 2534063f925SOleksandr Tymoshenko { 2544063f925SOleksandr Tymoshenko uint32_t bank, offset; 2554063f925SOleksandr Tymoshenko 25690576f54SOleksandr Tymoshenko /* Must be called with lock held. */ 25790576f54SOleksandr Tymoshenko BCM_GPIO_LOCK_ASSERT(sc); 25890576f54SOleksandr Tymoshenko 2594063f925SOleksandr Tymoshenko bank = pin / 32; 2604063f925SOleksandr Tymoshenko offset = pin - 32 * bank; 2614063f925SOleksandr Tymoshenko 2624063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), state); 2634063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), (1 << offset)); 2644063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUD(0), 0); 2654063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPPUDCLK(bank), 0); 2664063f925SOleksandr Tymoshenko } 2674063f925SOleksandr Tymoshenko 26844d06d8dSLuiz Otavio O Souza void 26944d06d8dSLuiz Otavio O Souza bcm_gpio_set_alternate(device_t dev, uint32_t pin, uint32_t nfunc) 27044d06d8dSLuiz Otavio O Souza { 27144d06d8dSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 27244d06d8dSLuiz Otavio O Souza int i; 27344d06d8dSLuiz Otavio O Souza 27444d06d8dSLuiz Otavio O Souza sc = device_get_softc(dev); 27544d06d8dSLuiz Otavio O Souza BCM_GPIO_LOCK(sc); 27644d06d8dSLuiz Otavio O Souza 27744d06d8dSLuiz Otavio O Souza /* Disable pull-up or pull-down on pin. */ 27844d06d8dSLuiz Otavio O Souza bcm_gpio_set_pud(sc, pin, BCM_GPIO_NONE); 27944d06d8dSLuiz Otavio O Souza 28044d06d8dSLuiz Otavio O Souza /* And now set the pin function. */ 28144d06d8dSLuiz Otavio O Souza bcm_gpio_set_function(sc, pin, nfunc); 28244d06d8dSLuiz Otavio O Souza 28344d06d8dSLuiz Otavio O Souza /* Update the pin flags. */ 28444d06d8dSLuiz Otavio O Souza for (i = 0; i < sc->sc_gpio_npins; i++) { 28544d06d8dSLuiz Otavio O Souza if (sc->sc_gpio_pins[i].gp_pin == pin) 28644d06d8dSLuiz Otavio O Souza break; 28744d06d8dSLuiz Otavio O Souza } 28844d06d8dSLuiz Otavio O Souza if (i < sc->sc_gpio_npins) 28944d06d8dSLuiz Otavio O Souza sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(nfunc); 29044d06d8dSLuiz Otavio O Souza 29144d06d8dSLuiz Otavio O Souza BCM_GPIO_UNLOCK(sc); 29244d06d8dSLuiz Otavio O Souza } 29344d06d8dSLuiz Otavio O Souza 2944063f925SOleksandr Tymoshenko static void 2954063f925SOleksandr Tymoshenko bcm_gpio_pin_configure(struct bcm_gpio_softc *sc, struct gpio_pin *pin, 2964063f925SOleksandr Tymoshenko unsigned int flags) 2974063f925SOleksandr Tymoshenko { 2984063f925SOleksandr Tymoshenko 29990576f54SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 30090576f54SOleksandr Tymoshenko 3014063f925SOleksandr Tymoshenko /* 3024063f925SOleksandr Tymoshenko * Manage input/output. 3034063f925SOleksandr Tymoshenko */ 3044063f925SOleksandr Tymoshenko if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { 3054063f925SOleksandr Tymoshenko pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); 3064063f925SOleksandr Tymoshenko if (flags & GPIO_PIN_OUTPUT) { 3074063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_OUTPUT; 3084063f925SOleksandr Tymoshenko bcm_gpio_set_function(sc, pin->gp_pin, 3094063f925SOleksandr Tymoshenko BCM_GPIO_OUTPUT); 3104063f925SOleksandr Tymoshenko } else { 3114063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_INPUT; 3124063f925SOleksandr Tymoshenko bcm_gpio_set_function(sc, pin->gp_pin, 3134063f925SOleksandr Tymoshenko BCM_GPIO_INPUT); 3144063f925SOleksandr Tymoshenko } 3154063f925SOleksandr Tymoshenko } 3164063f925SOleksandr Tymoshenko 3174063f925SOleksandr Tymoshenko /* Manage Pull-up/pull-down. */ 3184063f925SOleksandr Tymoshenko pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN); 3194063f925SOleksandr Tymoshenko if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) { 3204063f925SOleksandr Tymoshenko if (flags & GPIO_PIN_PULLUP) { 3214063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_PULLUP; 3224063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLUP); 3234063f925SOleksandr Tymoshenko } else { 3244063f925SOleksandr Tymoshenko pin->gp_flags |= GPIO_PIN_PULLDOWN; 3254063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_PULLDOWN); 3264063f925SOleksandr Tymoshenko } 3274063f925SOleksandr Tymoshenko } else 3284063f925SOleksandr Tymoshenko bcm_gpio_set_pud(sc, pin->gp_pin, BCM_GPIO_NONE); 32990576f54SOleksandr Tymoshenko 33090576f54SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 3314063f925SOleksandr Tymoshenko } 3324063f925SOleksandr Tymoshenko 3337836352bSLuiz Otavio O Souza static device_t 3347836352bSLuiz Otavio O Souza bcm_gpio_get_bus(device_t dev) 3357836352bSLuiz Otavio O Souza { 3367836352bSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 3377836352bSLuiz Otavio O Souza 3387836352bSLuiz Otavio O Souza sc = device_get_softc(dev); 3397836352bSLuiz Otavio O Souza 3407836352bSLuiz Otavio O Souza return (sc->sc_busdev); 3417836352bSLuiz Otavio O Souza } 3427836352bSLuiz Otavio O Souza 3434063f925SOleksandr Tymoshenko static int 3444063f925SOleksandr Tymoshenko bcm_gpio_pin_max(device_t dev, int *maxpin) 3454063f925SOleksandr Tymoshenko { 3464063f925SOleksandr Tymoshenko 3474063f925SOleksandr Tymoshenko *maxpin = BCM_GPIO_PINS - 1; 3484063f925SOleksandr Tymoshenko return (0); 3494063f925SOleksandr Tymoshenko } 3504063f925SOleksandr Tymoshenko 3514063f925SOleksandr Tymoshenko static int 3524063f925SOleksandr Tymoshenko bcm_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 3534063f925SOleksandr Tymoshenko { 3544063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 3554063f925SOleksandr Tymoshenko int i; 3564063f925SOleksandr Tymoshenko 3574063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 3584063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 3594063f925SOleksandr Tymoshenko break; 3604063f925SOleksandr Tymoshenko } 3614063f925SOleksandr Tymoshenko 3624063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 3634063f925SOleksandr Tymoshenko return (EINVAL); 3644063f925SOleksandr Tymoshenko 3654063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 3664063f925SOleksandr Tymoshenko *caps = sc->sc_gpio_pins[i].gp_caps; 3674063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 3684063f925SOleksandr Tymoshenko 3694063f925SOleksandr Tymoshenko return (0); 3704063f925SOleksandr Tymoshenko } 3714063f925SOleksandr Tymoshenko 3724063f925SOleksandr Tymoshenko static int 3734063f925SOleksandr Tymoshenko bcm_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) 3744063f925SOleksandr Tymoshenko { 3754063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 3764063f925SOleksandr Tymoshenko int i; 3774063f925SOleksandr Tymoshenko 3784063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 3794063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 3804063f925SOleksandr Tymoshenko break; 3814063f925SOleksandr Tymoshenko } 3824063f925SOleksandr Tymoshenko 3834063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 3844063f925SOleksandr Tymoshenko return (EINVAL); 3854063f925SOleksandr Tymoshenko 3864063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 3874063f925SOleksandr Tymoshenko *flags = sc->sc_gpio_pins[i].gp_flags; 3884063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 3894063f925SOleksandr Tymoshenko 3904063f925SOleksandr Tymoshenko return (0); 3914063f925SOleksandr Tymoshenko } 3924063f925SOleksandr Tymoshenko 3934063f925SOleksandr Tymoshenko static int 3944063f925SOleksandr Tymoshenko bcm_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 3954063f925SOleksandr Tymoshenko { 3964063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 3974063f925SOleksandr Tymoshenko int i; 3984063f925SOleksandr Tymoshenko 3994063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4004063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4014063f925SOleksandr Tymoshenko break; 4024063f925SOleksandr Tymoshenko } 4034063f925SOleksandr Tymoshenko 4044063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4054063f925SOleksandr Tymoshenko return (EINVAL); 4064063f925SOleksandr Tymoshenko 4074063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4084063f925SOleksandr Tymoshenko memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); 4094063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4104063f925SOleksandr Tymoshenko 4114063f925SOleksandr Tymoshenko return (0); 4124063f925SOleksandr Tymoshenko } 4134063f925SOleksandr Tymoshenko 4144063f925SOleksandr Tymoshenko static int 4154063f925SOleksandr Tymoshenko bcm_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 4164063f925SOleksandr Tymoshenko { 4174063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4184063f925SOleksandr Tymoshenko int i; 4194063f925SOleksandr Tymoshenko 4204063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4214063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4224063f925SOleksandr Tymoshenko break; 4234063f925SOleksandr Tymoshenko } 4244063f925SOleksandr Tymoshenko 4254063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4264063f925SOleksandr Tymoshenko return (EINVAL); 4274063f925SOleksandr Tymoshenko 4284063f925SOleksandr Tymoshenko /* We never touch on read-only/reserved pins. */ 4294063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 4304063f925SOleksandr Tymoshenko return (EINVAL); 4314063f925SOleksandr Tymoshenko 4324063f925SOleksandr Tymoshenko bcm_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); 4334063f925SOleksandr Tymoshenko 4344063f925SOleksandr Tymoshenko return (0); 4354063f925SOleksandr Tymoshenko } 4364063f925SOleksandr Tymoshenko 4374063f925SOleksandr Tymoshenko static int 4384063f925SOleksandr Tymoshenko bcm_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) 4394063f925SOleksandr Tymoshenko { 4404063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4414063f925SOleksandr Tymoshenko uint32_t bank, offset; 4424063f925SOleksandr Tymoshenko int i; 4434063f925SOleksandr Tymoshenko 4444063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4454063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4464063f925SOleksandr Tymoshenko break; 4474063f925SOleksandr Tymoshenko } 4484063f925SOleksandr Tymoshenko 4494063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4504063f925SOleksandr Tymoshenko return (EINVAL); 4514063f925SOleksandr Tymoshenko 4524063f925SOleksandr Tymoshenko /* We never write to read-only/reserved pins. */ 4534063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 4544063f925SOleksandr Tymoshenko return (EINVAL); 4554063f925SOleksandr Tymoshenko 4564063f925SOleksandr Tymoshenko bank = pin / 32; 4574063f925SOleksandr Tymoshenko offset = pin - 32 * bank; 4584063f925SOleksandr Tymoshenko 4594063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4604063f925SOleksandr Tymoshenko if (value) 4614063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPSET(bank), (1 << offset)); 4624063f925SOleksandr Tymoshenko else 4634063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPCLR(bank), (1 << offset)); 4644063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4654063f925SOleksandr Tymoshenko 4664063f925SOleksandr Tymoshenko return (0); 4674063f925SOleksandr Tymoshenko } 4684063f925SOleksandr Tymoshenko 4694063f925SOleksandr Tymoshenko static int 4704063f925SOleksandr Tymoshenko bcm_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) 4714063f925SOleksandr Tymoshenko { 4724063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4734063f925SOleksandr Tymoshenko uint32_t bank, offset, reg_data; 4744063f925SOleksandr Tymoshenko int i; 4754063f925SOleksandr Tymoshenko 4764063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 4774063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 4784063f925SOleksandr Tymoshenko break; 4794063f925SOleksandr Tymoshenko } 4804063f925SOleksandr Tymoshenko 4814063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 4824063f925SOleksandr Tymoshenko return (EINVAL); 4834063f925SOleksandr Tymoshenko 4844063f925SOleksandr Tymoshenko bank = pin / 32; 4854063f925SOleksandr Tymoshenko offset = pin - 32 * bank; 4864063f925SOleksandr Tymoshenko 4874063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 4884063f925SOleksandr Tymoshenko reg_data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); 4894063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 4904063f925SOleksandr Tymoshenko *val = (reg_data & (1 << offset)) ? 1 : 0; 4914063f925SOleksandr Tymoshenko 4924063f925SOleksandr Tymoshenko return (0); 4934063f925SOleksandr Tymoshenko } 4944063f925SOleksandr Tymoshenko 4954063f925SOleksandr Tymoshenko static int 4964063f925SOleksandr Tymoshenko bcm_gpio_pin_toggle(device_t dev, uint32_t pin) 4974063f925SOleksandr Tymoshenko { 4984063f925SOleksandr Tymoshenko struct bcm_gpio_softc *sc = device_get_softc(dev); 4994063f925SOleksandr Tymoshenko uint32_t bank, data, offset; 5004063f925SOleksandr Tymoshenko int i; 5014063f925SOleksandr Tymoshenko 5024063f925SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 5034063f925SOleksandr Tymoshenko if (sc->sc_gpio_pins[i].gp_pin == pin) 5044063f925SOleksandr Tymoshenko break; 5054063f925SOleksandr Tymoshenko } 5064063f925SOleksandr Tymoshenko 5074063f925SOleksandr Tymoshenko if (i >= sc->sc_gpio_npins) 5084063f925SOleksandr Tymoshenko return (EINVAL); 5094063f925SOleksandr Tymoshenko 5104063f925SOleksandr Tymoshenko /* We never write to read-only/reserved pins. */ 5114063f925SOleksandr Tymoshenko if (bcm_gpio_pin_is_ro(sc, pin)) 5124063f925SOleksandr Tymoshenko return (EINVAL); 5134063f925SOleksandr Tymoshenko 5144063f925SOleksandr Tymoshenko bank = pin / 32; 5154063f925SOleksandr Tymoshenko offset = pin - 32 * bank; 5164063f925SOleksandr Tymoshenko 5174063f925SOleksandr Tymoshenko BCM_GPIO_LOCK(sc); 5184063f925SOleksandr Tymoshenko data = BCM_GPIO_READ(sc, BCM_GPIO_GPLEV(bank)); 5194063f925SOleksandr Tymoshenko if (data & (1 << offset)) 5204063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPCLR(bank), (1 << offset)); 5214063f925SOleksandr Tymoshenko else 5224063f925SOleksandr Tymoshenko BCM_GPIO_WRITE(sc, BCM_GPIO_GPSET(bank), (1 << offset)); 5234063f925SOleksandr Tymoshenko BCM_GPIO_UNLOCK(sc); 5244063f925SOleksandr Tymoshenko 5254063f925SOleksandr Tymoshenko return (0); 5264063f925SOleksandr Tymoshenko } 5274063f925SOleksandr Tymoshenko 5284063f925SOleksandr Tymoshenko static int 52990576f54SOleksandr Tymoshenko bcm_gpio_func_proc(SYSCTL_HANDLER_ARGS) 53090576f54SOleksandr Tymoshenko { 53190576f54SOleksandr Tymoshenko char buf[16]; 53290576f54SOleksandr Tymoshenko struct bcm_gpio_softc *sc; 53390576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl *sc_sysctl; 53490576f54SOleksandr Tymoshenko uint32_t nfunc; 53544d06d8dSLuiz Otavio O Souza int error; 53690576f54SOleksandr Tymoshenko 53790576f54SOleksandr Tymoshenko sc_sysctl = arg1; 53890576f54SOleksandr Tymoshenko sc = sc_sysctl->sc; 53990576f54SOleksandr Tymoshenko 54090576f54SOleksandr Tymoshenko /* Get the current pin function. */ 54190576f54SOleksandr Tymoshenko nfunc = bcm_gpio_get_function(sc, sc_sysctl->pin); 54290576f54SOleksandr Tymoshenko bcm_gpio_func_str(nfunc, buf, sizeof(buf)); 54390576f54SOleksandr Tymoshenko 54490576f54SOleksandr Tymoshenko error = sysctl_handle_string(oidp, buf, sizeof(buf), req); 54590576f54SOleksandr Tymoshenko if (error != 0 || req->newptr == NULL) 54690576f54SOleksandr Tymoshenko return (error); 54799a20111SLuiz Otavio O Souza /* Ignore changes on read-only pins. */ 54899a20111SLuiz Otavio O Souza if (bcm_gpio_pin_is_ro(sc, sc_sysctl->pin)) 54999a20111SLuiz Otavio O Souza return (0); 55090576f54SOleksandr Tymoshenko /* Parse the user supplied string and check for a valid pin function. */ 55190576f54SOleksandr Tymoshenko if (bcm_gpio_str_func(buf, &nfunc) != 0) 55290576f54SOleksandr Tymoshenko return (EINVAL); 55390576f54SOleksandr Tymoshenko 55444d06d8dSLuiz Otavio O Souza /* Update the pin alternate function. */ 55544d06d8dSLuiz Otavio O Souza bcm_gpio_set_alternate(sc->sc_dev, sc_sysctl->pin, nfunc); 55690576f54SOleksandr Tymoshenko 55790576f54SOleksandr Tymoshenko return (0); 55890576f54SOleksandr Tymoshenko } 55990576f54SOleksandr Tymoshenko 56090576f54SOleksandr Tymoshenko static void 56190576f54SOleksandr Tymoshenko bcm_gpio_sysctl_init(struct bcm_gpio_softc *sc) 56290576f54SOleksandr Tymoshenko { 56390576f54SOleksandr Tymoshenko char pinbuf[3]; 56490576f54SOleksandr Tymoshenko struct bcm_gpio_sysctl *sc_sysctl; 56590576f54SOleksandr Tymoshenko struct sysctl_ctx_list *ctx; 56690576f54SOleksandr Tymoshenko struct sysctl_oid *tree_node, *pin_node, *pinN_node; 56790576f54SOleksandr Tymoshenko struct sysctl_oid_list *tree, *pin_tree, *pinN_tree; 56890576f54SOleksandr Tymoshenko int i; 56990576f54SOleksandr Tymoshenko 57090576f54SOleksandr Tymoshenko /* 57190576f54SOleksandr Tymoshenko * Add per-pin sysctl tree/handlers. 57290576f54SOleksandr Tymoshenko */ 57390576f54SOleksandr Tymoshenko ctx = device_get_sysctl_ctx(sc->sc_dev); 57490576f54SOleksandr Tymoshenko tree_node = device_get_sysctl_tree(sc->sc_dev); 57590576f54SOleksandr Tymoshenko tree = SYSCTL_CHILDREN(tree_node); 57690576f54SOleksandr Tymoshenko pin_node = SYSCTL_ADD_NODE(ctx, tree, OID_AUTO, "pin", 5771fbabb48SLuiz Otavio O Souza CTLFLAG_RD, NULL, "GPIO Pins"); 57890576f54SOleksandr Tymoshenko pin_tree = SYSCTL_CHILDREN(pin_node); 57990576f54SOleksandr Tymoshenko 58090576f54SOleksandr Tymoshenko for (i = 0; i < sc->sc_gpio_npins; i++) { 58190576f54SOleksandr Tymoshenko 58290576f54SOleksandr Tymoshenko snprintf(pinbuf, sizeof(pinbuf), "%d", i); 58390576f54SOleksandr Tymoshenko pinN_node = SYSCTL_ADD_NODE(ctx, pin_tree, OID_AUTO, pinbuf, 58490576f54SOleksandr Tymoshenko CTLFLAG_RD, NULL, "GPIO Pin"); 58590576f54SOleksandr Tymoshenko pinN_tree = SYSCTL_CHILDREN(pinN_node); 58690576f54SOleksandr Tymoshenko 58790576f54SOleksandr Tymoshenko sc->sc_sysctl[i].sc = sc; 58890576f54SOleksandr Tymoshenko sc_sysctl = &sc->sc_sysctl[i]; 58990576f54SOleksandr Tymoshenko sc_sysctl->sc = sc; 59090576f54SOleksandr Tymoshenko sc_sysctl->pin = sc->sc_gpio_pins[i].gp_pin; 59190576f54SOleksandr Tymoshenko SYSCTL_ADD_PROC(ctx, pinN_tree, OID_AUTO, "function", 59290576f54SOleksandr Tymoshenko CTLFLAG_RW | CTLTYPE_STRING, sc_sysctl, 59390576f54SOleksandr Tymoshenko sizeof(struct bcm_gpio_sysctl), bcm_gpio_func_proc, 59490576f54SOleksandr Tymoshenko "A", "Pin Function"); 59590576f54SOleksandr Tymoshenko } 59690576f54SOleksandr Tymoshenko } 59790576f54SOleksandr Tymoshenko 59890576f54SOleksandr Tymoshenko static int 59999a20111SLuiz Otavio O Souza bcm_gpio_get_ro_pins(struct bcm_gpio_softc *sc, phandle_t node, 60099a20111SLuiz Otavio O Souza const char *propname, const char *label) 60199a20111SLuiz Otavio O Souza { 60299a20111SLuiz Otavio O Souza int i, need_comma, npins, range_start, range_stop; 60399a20111SLuiz Otavio O Souza pcell_t *pins; 60499a20111SLuiz Otavio O Souza 60599a20111SLuiz Otavio O Souza /* Get the property data. */ 60699a20111SLuiz Otavio O Souza npins = OF_getencprop_alloc(node, propname, sizeof(*pins), 60799a20111SLuiz Otavio O Souza (void **)&pins); 60899a20111SLuiz Otavio O Souza if (npins < 0) 60999a20111SLuiz Otavio O Souza return (-1); 61099a20111SLuiz Otavio O Souza if (npins == 0) { 61199a20111SLuiz Otavio O Souza free(pins, M_OFWPROP); 61299a20111SLuiz Otavio O Souza return (0); 61399a20111SLuiz Otavio O Souza } 61499a20111SLuiz Otavio O Souza for (i = 0; i < npins; i++) 61599a20111SLuiz Otavio O Souza sc->sc_ro_pins[i + sc->sc_ro_npins] = pins[i]; 61699a20111SLuiz Otavio O Souza sc->sc_ro_npins += npins; 61799a20111SLuiz Otavio O Souza need_comma = 0; 61899a20111SLuiz Otavio O Souza device_printf(sc->sc_dev, "%s pins: ", label); 61999a20111SLuiz Otavio O Souza range_start = range_stop = pins[0]; 62099a20111SLuiz Otavio O Souza for (i = 1; i < npins; i++) { 62199a20111SLuiz Otavio O Souza if (pins[i] != range_stop + 1) { 62299a20111SLuiz Otavio O Souza if (need_comma) 62399a20111SLuiz Otavio O Souza printf(","); 62499a20111SLuiz Otavio O Souza if (range_start != range_stop) 62599a20111SLuiz Otavio O Souza printf("%d-%d", range_start, range_stop); 62699a20111SLuiz Otavio O Souza else 62799a20111SLuiz Otavio O Souza printf("%d", range_start); 62899a20111SLuiz Otavio O Souza range_start = range_stop = pins[i]; 62999a20111SLuiz Otavio O Souza need_comma = 1; 63099a20111SLuiz Otavio O Souza } else 63199a20111SLuiz Otavio O Souza range_stop++; 63299a20111SLuiz Otavio O Souza } 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.\n", range_start, range_stop); 63799a20111SLuiz Otavio O Souza else 63899a20111SLuiz Otavio O Souza printf("%d.\n", range_start); 63999a20111SLuiz Otavio O Souza free(pins, M_OFWPROP); 64099a20111SLuiz Otavio O Souza 64199a20111SLuiz Otavio O Souza return (0); 64299a20111SLuiz Otavio O Souza } 64399a20111SLuiz Otavio O Souza 64499a20111SLuiz Otavio O Souza static int 6454063f925SOleksandr Tymoshenko bcm_gpio_get_reserved_pins(struct bcm_gpio_softc *sc) 6464063f925SOleksandr Tymoshenko { 64799a20111SLuiz Otavio O Souza char *name; 6484063f925SOleksandr Tymoshenko phandle_t gpio, node, reserved; 64999a20111SLuiz Otavio O Souza ssize_t len; 6504063f925SOleksandr Tymoshenko 6514063f925SOleksandr Tymoshenko /* Get read-only pins. */ 6524063f925SOleksandr Tymoshenko gpio = ofw_bus_get_node(sc->sc_dev); 65399a20111SLuiz Otavio O Souza if (bcm_gpio_get_ro_pins(sc, gpio, "broadcom,read-only", 65499a20111SLuiz Otavio O Souza "read-only") != 0) 65599a20111SLuiz Otavio O Souza return (-1); 65699a20111SLuiz Otavio O Souza /* Traverse the GPIO subnodes to find the reserved pins node. */ 6574063f925SOleksandr Tymoshenko reserved = 0; 65899a20111SLuiz Otavio O Souza node = OF_child(gpio); 6594063f925SOleksandr Tymoshenko while ((node != 0) && (reserved == 0)) { 66099a20111SLuiz Otavio O Souza len = OF_getprop_alloc(node, "name", 1, (void **)&name); 66199a20111SLuiz Otavio O Souza if (len == -1) 66299a20111SLuiz Otavio O Souza return (-1); 6634063f925SOleksandr Tymoshenko if (strcmp(name, "reserved") == 0) 6644063f925SOleksandr Tymoshenko reserved = node; 66599a20111SLuiz Otavio O Souza free(name, M_OFWPROP); 6664063f925SOleksandr Tymoshenko node = OF_peer(node); 6674063f925SOleksandr Tymoshenko } 6684063f925SOleksandr Tymoshenko if (reserved == 0) 6694063f925SOleksandr Tymoshenko return (-1); 6704063f925SOleksandr Tymoshenko /* Get the reserved pins. */ 67199a20111SLuiz Otavio O Souza if (bcm_gpio_get_ro_pins(sc, reserved, "broadcom,pins", 67299a20111SLuiz Otavio O Souza "reserved") != 0) 6734063f925SOleksandr Tymoshenko return (-1); 6744063f925SOleksandr Tymoshenko 6754063f925SOleksandr Tymoshenko return (0); 6764063f925SOleksandr Tymoshenko } 6774063f925SOleksandr Tymoshenko 6784063f925SOleksandr Tymoshenko static int 679*12471cecSLuiz Otavio O Souza bcm_gpio_intr(void *arg) 680*12471cecSLuiz Otavio O Souza { 681*12471cecSLuiz Otavio O Souza int bank_last, irq; 682*12471cecSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 683*12471cecSLuiz Otavio O Souza struct intr_event *event; 684*12471cecSLuiz Otavio O Souza uint32_t bank, mask, reg; 685*12471cecSLuiz Otavio O Souza 686*12471cecSLuiz Otavio O Souza sc = (struct bcm_gpio_softc *)arg; 687*12471cecSLuiz Otavio O Souza reg = 0; 688*12471cecSLuiz Otavio O Souza bank_last = -1; 689*12471cecSLuiz Otavio O Souza for (irq = 0; irq < BCM_GPIO_PINS; irq++) { 690*12471cecSLuiz Otavio O Souza bank = BCM_GPIO_BANK(irq); 691*12471cecSLuiz Otavio O Souza mask = BCM_GPIO_MASK(irq); 692*12471cecSLuiz Otavio O Souza if (bank != bank_last) { 693*12471cecSLuiz Otavio O Souza reg = BCM_GPIO_READ(sc, BCM_GPIO_GPEDS(bank)); 694*12471cecSLuiz Otavio O Souza bank_last = bank; 695*12471cecSLuiz Otavio O Souza } 696*12471cecSLuiz Otavio O Souza if (reg & mask) { 697*12471cecSLuiz Otavio O Souza event = sc->sc_events[irq]; 698*12471cecSLuiz Otavio O Souza if (event != NULL && !TAILQ_EMPTY(&event->ie_handlers)) 699*12471cecSLuiz Otavio O Souza intr_event_handle(event, NULL); 700*12471cecSLuiz Otavio O Souza else { 701*12471cecSLuiz Otavio O Souza device_printf(sc->sc_dev, "Stray IRQ %d\n", 702*12471cecSLuiz Otavio O Souza irq); 703*12471cecSLuiz Otavio O Souza } 704*12471cecSLuiz Otavio O Souza /* Clear the Status bit by writing '1' to it. */ 705*12471cecSLuiz Otavio O Souza BCM_GPIO_WRITE(sc, BCM_GPIO_GPEDS(bank), mask); 706*12471cecSLuiz Otavio O Souza } 707*12471cecSLuiz Otavio O Souza } 708*12471cecSLuiz Otavio O Souza 709*12471cecSLuiz Otavio O Souza return (FILTER_HANDLED); 710*12471cecSLuiz Otavio O Souza } 711*12471cecSLuiz Otavio O Souza 712*12471cecSLuiz Otavio O Souza static int 7134063f925SOleksandr Tymoshenko bcm_gpio_probe(device_t dev) 7144063f925SOleksandr Tymoshenko { 715add35ed5SIan Lepore 716add35ed5SIan Lepore if (!ofw_bus_status_okay(dev)) 717add35ed5SIan Lepore return (ENXIO); 718add35ed5SIan Lepore 7194063f925SOleksandr Tymoshenko if (!ofw_bus_is_compatible(dev, "broadcom,bcm2835-gpio")) 7204063f925SOleksandr Tymoshenko return (ENXIO); 7214063f925SOleksandr Tymoshenko 7224063f925SOleksandr Tymoshenko device_set_desc(dev, "BCM2708/2835 GPIO controller"); 7234063f925SOleksandr Tymoshenko return (BUS_PROBE_DEFAULT); 7244063f925SOleksandr Tymoshenko } 7254063f925SOleksandr Tymoshenko 7264063f925SOleksandr Tymoshenko static int 727*12471cecSLuiz Otavio O Souza bcm_gpio_intr_attach(device_t dev) 728*12471cecSLuiz Otavio O Souza { 729*12471cecSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 730*12471cecSLuiz Otavio O Souza int i; 731*12471cecSLuiz Otavio O Souza 732*12471cecSLuiz Otavio O Souza sc = device_get_softc(dev); 733*12471cecSLuiz Otavio O Souza for (i = 0; i < BCM_GPIO_IRQS; i++) { 734*12471cecSLuiz Otavio O Souza if (bus_setup_intr(dev, sc->sc_res[i + 1], 735*12471cecSLuiz Otavio O Souza INTR_TYPE_MISC | INTR_MPSAFE, bcm_gpio_intr, 736*12471cecSLuiz Otavio O Souza NULL, sc, &sc->sc_intrhand[i]) != 0) { 737*12471cecSLuiz Otavio O Souza return (-1); 738*12471cecSLuiz Otavio O Souza } 739*12471cecSLuiz Otavio O Souza } 740*12471cecSLuiz Otavio O Souza 741*12471cecSLuiz Otavio O Souza return (0); 742*12471cecSLuiz Otavio O Souza } 743*12471cecSLuiz Otavio O Souza 744*12471cecSLuiz Otavio O Souza static void 745*12471cecSLuiz Otavio O Souza bcm_gpio_intr_detach(device_t dev) 746*12471cecSLuiz Otavio O Souza { 747*12471cecSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 748*12471cecSLuiz Otavio O Souza int i; 749*12471cecSLuiz Otavio O Souza 750*12471cecSLuiz Otavio O Souza sc = device_get_softc(dev); 751*12471cecSLuiz Otavio O Souza for (i = 0; i < BCM_GPIO_IRQS; i++) { 752*12471cecSLuiz Otavio O Souza if (sc->sc_intrhand[i]) { 753*12471cecSLuiz Otavio O Souza bus_teardown_intr(dev, sc->sc_res[i + 1], 754*12471cecSLuiz Otavio O Souza sc->sc_intrhand[i]); 755*12471cecSLuiz Otavio O Souza } 756*12471cecSLuiz Otavio O Souza } 757*12471cecSLuiz Otavio O Souza } 758*12471cecSLuiz Otavio O Souza 759*12471cecSLuiz Otavio O Souza static int 7604063f925SOleksandr Tymoshenko bcm_gpio_attach(device_t dev) 7614063f925SOleksandr Tymoshenko { 762c2136589SLuiz Otavio O Souza int i, j; 7634063f925SOleksandr Tymoshenko phandle_t gpio; 764c2136589SLuiz Otavio O Souza struct bcm_gpio_softc *sc; 765c2136589SLuiz Otavio O Souza uint32_t func; 7664063f925SOleksandr Tymoshenko 767*12471cecSLuiz Otavio O Souza if (bcm_gpio_sc != NULL) 768*12471cecSLuiz Otavio O Souza return (ENXIO); 769*12471cecSLuiz Otavio O Souza 770*12471cecSLuiz Otavio O Souza bcm_gpio_sc = sc = device_get_softc(dev); 7714063f925SOleksandr Tymoshenko sc->sc_dev = dev; 772*12471cecSLuiz Otavio O Souza mtx_init(&sc->sc_mtx, "bcm gpio", "gpio", MTX_SPIN); 773c2136589SLuiz Otavio O Souza if (bus_alloc_resources(dev, bcm_gpio_res_spec, sc->sc_res) != 0) { 774c2136589SLuiz Otavio O Souza device_printf(dev, "cannot allocate resources\n"); 775c2136589SLuiz Otavio O Souza goto fail; 7764063f925SOleksandr Tymoshenko } 777c2136589SLuiz Otavio O Souza sc->sc_bst = rman_get_bustag(sc->sc_res[0]); 778c2136589SLuiz Otavio O Souza sc->sc_bsh = rman_get_bushandle(sc->sc_res[0]); 779*12471cecSLuiz Otavio O Souza /* Setup the GPIO interrupt handler. */ 780*12471cecSLuiz Otavio O Souza if (bcm_gpio_intr_attach(dev)) { 781*12471cecSLuiz Otavio O Souza device_printf(dev, "unable to setup the gpio irq handler\n"); 782*12471cecSLuiz Otavio O Souza goto fail; 783*12471cecSLuiz Otavio O Souza } 7844063f925SOleksandr Tymoshenko /* Find our node. */ 7854063f925SOleksandr Tymoshenko gpio = ofw_bus_get_node(sc->sc_dev); 7864063f925SOleksandr Tymoshenko if (!OF_hasprop(gpio, "gpio-controller")) 7874063f925SOleksandr Tymoshenko /* Node is not a GPIO controller. */ 7884063f925SOleksandr Tymoshenko goto fail; 7894063f925SOleksandr Tymoshenko /* 7904063f925SOleksandr Tymoshenko * Find the read-only pins. These are pins we never touch or bad 7914063f925SOleksandr Tymoshenko * things could happen. 7924063f925SOleksandr Tymoshenko */ 7934063f925SOleksandr Tymoshenko if (bcm_gpio_get_reserved_pins(sc) == -1) 7944063f925SOleksandr Tymoshenko goto fail; 7954063f925SOleksandr Tymoshenko /* Initialize the software controlled pins. */ 7968d900240SLuiz Otavio O Souza for (i = 0, j = 0; j < BCM_GPIO_PINS; j++) { 7974063f925SOleksandr Tymoshenko snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, 7984063f925SOleksandr Tymoshenko "pin %d", j); 79990576f54SOleksandr Tymoshenko func = bcm_gpio_get_function(sc, j); 8004063f925SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_pin = j; 8014063f925SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_caps = BCM_GPIO_DEFAULT_CAPS; 80290576f54SOleksandr Tymoshenko sc->sc_gpio_pins[i].gp_flags = bcm_gpio_func_flag(func); 803*12471cecSLuiz Otavio O Souza /* The default is active-low interrupts. */ 804*12471cecSLuiz Otavio O Souza sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL; 805*12471cecSLuiz Otavio O Souza sc->sc_irq_polarity[i] = INTR_POLARITY_LOW; 8064063f925SOleksandr Tymoshenko i++; 8074063f925SOleksandr Tymoshenko } 8084063f925SOleksandr Tymoshenko sc->sc_gpio_npins = i; 80990576f54SOleksandr Tymoshenko bcm_gpio_sysctl_init(sc); 8107836352bSLuiz Otavio O Souza sc->sc_busdev = gpiobus_attach_bus(dev); 8117836352bSLuiz Otavio O Souza if (sc->sc_busdev == NULL) 8127836352bSLuiz Otavio O Souza goto fail; 81390576f54SOleksandr Tymoshenko 8147836352bSLuiz Otavio O Souza return (0); 8154063f925SOleksandr Tymoshenko 8164063f925SOleksandr Tymoshenko fail: 817*12471cecSLuiz Otavio O Souza bcm_gpio_intr_detach(dev); 818c2136589SLuiz Otavio O Souza bus_release_resources(dev, bcm_gpio_res_spec, sc->sc_res); 819c2136589SLuiz Otavio O Souza mtx_destroy(&sc->sc_mtx); 820c2136589SLuiz Otavio O Souza 8214063f925SOleksandr Tymoshenko return (ENXIO); 8224063f925SOleksandr Tymoshenko } 8234063f925SOleksandr Tymoshenko 8244063f925SOleksandr Tymoshenko static int 8254063f925SOleksandr Tymoshenko bcm_gpio_detach(device_t dev) 8264063f925SOleksandr Tymoshenko { 8274063f925SOleksandr Tymoshenko 8284063f925SOleksandr Tymoshenko return (EBUSY); 8294063f925SOleksandr Tymoshenko } 8304063f925SOleksandr Tymoshenko 831*12471cecSLuiz Otavio O Souza static uint32_t 832*12471cecSLuiz Otavio O Souza bcm_gpio_intr_reg(struct bcm_gpio_softc *sc, unsigned int irq, uint32_t bank) 833*12471cecSLuiz Otavio O Souza { 834*12471cecSLuiz Otavio O Souza 835*12471cecSLuiz Otavio O Souza if (irq > BCM_GPIO_PINS) 836*12471cecSLuiz Otavio O Souza return (0); 837*12471cecSLuiz Otavio O Souza if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_LEVEL) { 838*12471cecSLuiz Otavio O Souza if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW) 839*12471cecSLuiz Otavio O Souza return (BCM_GPIO_GPLEN(bank)); 840*12471cecSLuiz Otavio O Souza else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH) 841*12471cecSLuiz Otavio O Souza return (BCM_GPIO_GPHEN(bank)); 842*12471cecSLuiz Otavio O Souza } else if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_EDGE) { 843*12471cecSLuiz Otavio O Souza if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW) 844*12471cecSLuiz Otavio O Souza return (BCM_GPIO_GPFEN(bank)); 845*12471cecSLuiz Otavio O Souza else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH) 846*12471cecSLuiz Otavio O Souza return (BCM_GPIO_GPREN(bank)); 847*12471cecSLuiz Otavio O Souza } 848*12471cecSLuiz Otavio O Souza 849*12471cecSLuiz Otavio O Souza return (0); 850*12471cecSLuiz Otavio O Souza } 851*12471cecSLuiz Otavio O Souza 852*12471cecSLuiz Otavio O Souza static void 853*12471cecSLuiz Otavio O Souza bcm_gpio_mask_irq(void *source) 854*12471cecSLuiz Otavio O Souza { 855*12471cecSLuiz Otavio O Souza uint32_t bank, mask, reg; 856*12471cecSLuiz Otavio O Souza unsigned int irq; 857*12471cecSLuiz Otavio O Souza 858*12471cecSLuiz Otavio O Souza irq = (unsigned int)source; 859*12471cecSLuiz Otavio O Souza if (irq > BCM_GPIO_PINS) 860*12471cecSLuiz Otavio O Souza return; 861*12471cecSLuiz Otavio O Souza if (bcm_gpio_pin_is_ro(bcm_gpio_sc, irq)) 862*12471cecSLuiz Otavio O Souza return; 863*12471cecSLuiz Otavio O Souza bank = BCM_GPIO_BANK(irq); 864*12471cecSLuiz Otavio O Souza mask = BCM_GPIO_MASK(irq); 865*12471cecSLuiz Otavio O Souza BCM_GPIO_LOCK(bcm_gpio_sc); 866*12471cecSLuiz Otavio O Souza reg = bcm_gpio_intr_reg(bcm_gpio_sc, irq, bank); 867*12471cecSLuiz Otavio O Souza if (reg != 0) 868*12471cecSLuiz Otavio O Souza BCM_GPIO_CLEAR_BITS(bcm_gpio_sc, reg, mask); 869*12471cecSLuiz Otavio O Souza BCM_GPIO_UNLOCK(bcm_gpio_sc); 870*12471cecSLuiz Otavio O Souza } 871*12471cecSLuiz Otavio O Souza 872*12471cecSLuiz Otavio O Souza static void 873*12471cecSLuiz Otavio O Souza bcm_gpio_unmask_irq(void *source) 874*12471cecSLuiz Otavio O Souza { 875*12471cecSLuiz Otavio O Souza uint32_t bank, mask, reg; 876*12471cecSLuiz Otavio O Souza unsigned int irq; 877*12471cecSLuiz Otavio O Souza 878*12471cecSLuiz Otavio O Souza irq = (unsigned int)source; 879*12471cecSLuiz Otavio O Souza if (irq > BCM_GPIO_PINS) 880*12471cecSLuiz Otavio O Souza return; 881*12471cecSLuiz Otavio O Souza if (bcm_gpio_pin_is_ro(bcm_gpio_sc, irq)) 882*12471cecSLuiz Otavio O Souza return; 883*12471cecSLuiz Otavio O Souza bank = BCM_GPIO_BANK(irq); 884*12471cecSLuiz Otavio O Souza mask = BCM_GPIO_MASK(irq); 885*12471cecSLuiz Otavio O Souza BCM_GPIO_LOCK(bcm_gpio_sc); 886*12471cecSLuiz Otavio O Souza reg = bcm_gpio_intr_reg(bcm_gpio_sc, irq, bank); 887*12471cecSLuiz Otavio O Souza if (reg != 0) 888*12471cecSLuiz Otavio O Souza BCM_GPIO_SET_BITS(bcm_gpio_sc, reg, mask); 889*12471cecSLuiz Otavio O Souza BCM_GPIO_UNLOCK(bcm_gpio_sc); 890*12471cecSLuiz Otavio O Souza } 891*12471cecSLuiz Otavio O Souza 892*12471cecSLuiz Otavio O Souza static int 893*12471cecSLuiz Otavio O Souza bcm_gpio_activate_resource(device_t bus, device_t child, int type, int rid, 894*12471cecSLuiz Otavio O Souza struct resource *res) 895*12471cecSLuiz Otavio O Souza { 896*12471cecSLuiz Otavio O Souza int pin; 897*12471cecSLuiz Otavio O Souza 898*12471cecSLuiz Otavio O Souza if (type != SYS_RES_IRQ) 899*12471cecSLuiz Otavio O Souza return (ENXIO); 900*12471cecSLuiz Otavio O Souza /* Unmask the interrupt. */ 901*12471cecSLuiz Otavio O Souza pin = rman_get_start(res); 902*12471cecSLuiz Otavio O Souza bcm_gpio_unmask_irq((void *)pin); 903*12471cecSLuiz Otavio O Souza 904*12471cecSLuiz Otavio O Souza return (0); 905*12471cecSLuiz Otavio O Souza } 906*12471cecSLuiz Otavio O Souza 907*12471cecSLuiz Otavio O Souza static int 908*12471cecSLuiz Otavio O Souza bcm_gpio_deactivate_resource(device_t bus, device_t child, int type, int rid, 909*12471cecSLuiz Otavio O Souza struct resource *res) 910*12471cecSLuiz Otavio O Souza { 911*12471cecSLuiz Otavio O Souza int pin; 912*12471cecSLuiz Otavio O Souza 913*12471cecSLuiz Otavio O Souza if (type != SYS_RES_IRQ) 914*12471cecSLuiz Otavio O Souza return (ENXIO); 915*12471cecSLuiz Otavio O Souza /* Mask the interrupt. */ 916*12471cecSLuiz Otavio O Souza pin = rman_get_start(res); 917*12471cecSLuiz Otavio O Souza bcm_gpio_mask_irq((void *)pin); 918*12471cecSLuiz Otavio O Souza 919*12471cecSLuiz Otavio O Souza return (0); 920*12471cecSLuiz Otavio O Souza } 921*12471cecSLuiz Otavio O Souza 922*12471cecSLuiz Otavio O Souza static int 923*12471cecSLuiz Otavio O Souza bcm_gpio_config_intr(device_t dev, int irq, enum intr_trigger trig, 924*12471cecSLuiz Otavio O Souza enum intr_polarity pol) 925*12471cecSLuiz Otavio O Souza { 926*12471cecSLuiz Otavio O Souza int bank; 927*12471cecSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 928*12471cecSLuiz Otavio O Souza uint32_t mask, oldreg, reg; 929*12471cecSLuiz Otavio O Souza 930*12471cecSLuiz Otavio O Souza if (irq > BCM_GPIO_PINS) 931*12471cecSLuiz Otavio O Souza return (EINVAL); 932*12471cecSLuiz Otavio O Souza /* There is no standard trigger or polarity. */ 933*12471cecSLuiz Otavio O Souza if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM) 934*12471cecSLuiz Otavio O Souza return (EINVAL); 935*12471cecSLuiz Otavio O Souza sc = device_get_softc(dev); 936*12471cecSLuiz Otavio O Souza if (bcm_gpio_pin_is_ro(sc, irq)) 937*12471cecSLuiz Otavio O Souza return (EINVAL); 938*12471cecSLuiz Otavio O Souza bank = BCM_GPIO_BANK(irq); 939*12471cecSLuiz Otavio O Souza mask = BCM_GPIO_MASK(irq); 940*12471cecSLuiz Otavio O Souza BCM_GPIO_LOCK(sc); 941*12471cecSLuiz Otavio O Souza oldreg = bcm_gpio_intr_reg(sc, irq, bank); 942*12471cecSLuiz Otavio O Souza sc->sc_irq_trigger[irq] = trig; 943*12471cecSLuiz Otavio O Souza sc->sc_irq_polarity[irq] = pol; 944*12471cecSLuiz Otavio O Souza reg = bcm_gpio_intr_reg(sc, irq, bank); 945*12471cecSLuiz Otavio O Souza if (reg != 0) 946*12471cecSLuiz Otavio O Souza BCM_GPIO_SET_BITS(sc, reg, mask); 947*12471cecSLuiz Otavio O Souza if (reg != oldreg && oldreg != 0) 948*12471cecSLuiz Otavio O Souza BCM_GPIO_CLEAR_BITS(sc, oldreg, mask); 949*12471cecSLuiz Otavio O Souza BCM_GPIO_UNLOCK(sc); 950*12471cecSLuiz Otavio O Souza 951*12471cecSLuiz Otavio O Souza return (0); 952*12471cecSLuiz Otavio O Souza } 953*12471cecSLuiz Otavio O Souza 954*12471cecSLuiz Otavio O Souza static int 955*12471cecSLuiz Otavio O Souza bcm_gpio_setup_intr(device_t bus, device_t child, struct resource *ires, 956*12471cecSLuiz Otavio O Souza int flags, driver_filter_t *filt, driver_intr_t *handler, 957*12471cecSLuiz Otavio O Souza void *arg, void **cookiep) 958*12471cecSLuiz Otavio O Souza { 959*12471cecSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 960*12471cecSLuiz Otavio O Souza struct intr_event *event; 961*12471cecSLuiz Otavio O Souza int pin, error; 962*12471cecSLuiz Otavio O Souza 963*12471cecSLuiz Otavio O Souza sc = device_get_softc(bus); 964*12471cecSLuiz Otavio O Souza pin = rman_get_start(ires); 965*12471cecSLuiz Otavio O Souza if (pin > BCM_GPIO_PINS) 966*12471cecSLuiz Otavio O Souza panic("%s: bad pin %d", __func__, pin); 967*12471cecSLuiz Otavio O Souza event = sc->sc_events[pin]; 968*12471cecSLuiz Otavio O Souza if (event == NULL) { 969*12471cecSLuiz Otavio O Souza error = intr_event_create(&event, (void *)pin, 0, pin, 970*12471cecSLuiz Otavio O Souza bcm_gpio_mask_irq, bcm_gpio_unmask_irq, NULL, NULL, 971*12471cecSLuiz Otavio O Souza "gpio%d pin%d:", device_get_unit(bus), pin); 972*12471cecSLuiz Otavio O Souza if (error != 0) 973*12471cecSLuiz Otavio O Souza return (error); 974*12471cecSLuiz Otavio O Souza sc->sc_events[pin] = event; 975*12471cecSLuiz Otavio O Souza } 976*12471cecSLuiz Otavio O Souza intr_event_add_handler(event, device_get_nameunit(child), filt, 977*12471cecSLuiz Otavio O Souza handler, arg, intr_priority(flags), flags, cookiep); 978*12471cecSLuiz Otavio O Souza 979*12471cecSLuiz Otavio O Souza return (0); 980*12471cecSLuiz Otavio O Souza } 981*12471cecSLuiz Otavio O Souza 982*12471cecSLuiz Otavio O Souza static int 983*12471cecSLuiz Otavio O Souza bcm_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires, 984*12471cecSLuiz Otavio O Souza void *cookie) 985*12471cecSLuiz Otavio O Souza { 986*12471cecSLuiz Otavio O Souza struct bcm_gpio_softc *sc; 987*12471cecSLuiz Otavio O Souza int pin, err; 988*12471cecSLuiz Otavio O Souza 989*12471cecSLuiz Otavio O Souza sc = device_get_softc(dev); 990*12471cecSLuiz Otavio O Souza pin = rman_get_start(ires); 991*12471cecSLuiz Otavio O Souza if (pin > BCM_GPIO_PINS) 992*12471cecSLuiz Otavio O Souza panic("%s: bad pin %d", __func__, pin); 993*12471cecSLuiz Otavio O Souza if (sc->sc_events[pin] == NULL) 994*12471cecSLuiz Otavio O Souza panic("Trying to teardown unoccupied IRQ"); 995*12471cecSLuiz Otavio O Souza err = intr_event_remove_handler(cookie); 996*12471cecSLuiz Otavio O Souza if (!err) 997*12471cecSLuiz Otavio O Souza sc->sc_events[pin] = NULL; 998*12471cecSLuiz Otavio O Souza 999*12471cecSLuiz Otavio O Souza return (err); 1000*12471cecSLuiz Otavio O Souza } 1001*12471cecSLuiz Otavio O Souza 10028c705c2cSLuiz Otavio O Souza static phandle_t 10038c705c2cSLuiz Otavio O Souza bcm_gpio_get_node(device_t bus, device_t dev) 10048c705c2cSLuiz Otavio O Souza { 10058c705c2cSLuiz Otavio O Souza 10068c705c2cSLuiz Otavio O Souza /* We only have one child, the GPIO bus, which needs our own node. */ 10078c705c2cSLuiz Otavio O Souza return (ofw_bus_get_node(bus)); 10088c705c2cSLuiz Otavio O Souza } 10098c705c2cSLuiz Otavio O Souza 10104063f925SOleksandr Tymoshenko static device_method_t bcm_gpio_methods[] = { 10114063f925SOleksandr Tymoshenko /* Device interface */ 10124063f925SOleksandr Tymoshenko DEVMETHOD(device_probe, bcm_gpio_probe), 10134063f925SOleksandr Tymoshenko DEVMETHOD(device_attach, bcm_gpio_attach), 10144063f925SOleksandr Tymoshenko DEVMETHOD(device_detach, bcm_gpio_detach), 10154063f925SOleksandr Tymoshenko 10164063f925SOleksandr Tymoshenko /* GPIO protocol */ 10177836352bSLuiz Otavio O Souza DEVMETHOD(gpio_get_bus, bcm_gpio_get_bus), 10184063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_max, bcm_gpio_pin_max), 10194063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getname, bcm_gpio_pin_getname), 10204063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getflags, bcm_gpio_pin_getflags), 10214063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_getcaps, bcm_gpio_pin_getcaps), 10224063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_setflags, bcm_gpio_pin_setflags), 10234063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_get, bcm_gpio_pin_get), 10244063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_set, bcm_gpio_pin_set), 10254063f925SOleksandr Tymoshenko DEVMETHOD(gpio_pin_toggle, bcm_gpio_pin_toggle), 10264063f925SOleksandr Tymoshenko 1027*12471cecSLuiz Otavio O Souza /* Bus interface */ 1028*12471cecSLuiz Otavio O Souza DEVMETHOD(bus_activate_resource, bcm_gpio_activate_resource), 1029*12471cecSLuiz Otavio O Souza DEVMETHOD(bus_deactivate_resource, bcm_gpio_deactivate_resource), 1030*12471cecSLuiz Otavio O Souza DEVMETHOD(bus_config_intr, bcm_gpio_config_intr), 1031*12471cecSLuiz Otavio O Souza DEVMETHOD(bus_setup_intr, bcm_gpio_setup_intr), 1032*12471cecSLuiz Otavio O Souza DEVMETHOD(bus_teardown_intr, bcm_gpio_teardown_intr), 1033*12471cecSLuiz Otavio O Souza 10348c705c2cSLuiz Otavio O Souza /* ofw_bus interface */ 10358c705c2cSLuiz Otavio O Souza DEVMETHOD(ofw_bus_get_node, bcm_gpio_get_node), 10368c705c2cSLuiz Otavio O Souza 10374063f925SOleksandr Tymoshenko DEVMETHOD_END 10384063f925SOleksandr Tymoshenko }; 10394063f925SOleksandr Tymoshenko 10404063f925SOleksandr Tymoshenko static devclass_t bcm_gpio_devclass; 10414063f925SOleksandr Tymoshenko 10424063f925SOleksandr Tymoshenko static driver_t bcm_gpio_driver = { 10434063f925SOleksandr Tymoshenko "gpio", 10444063f925SOleksandr Tymoshenko bcm_gpio_methods, 10454063f925SOleksandr Tymoshenko sizeof(struct bcm_gpio_softc), 10464063f925SOleksandr Tymoshenko }; 10474063f925SOleksandr Tymoshenko 10484063f925SOleksandr Tymoshenko DRIVER_MODULE(bcm_gpio, simplebus, bcm_gpio_driver, bcm_gpio_devclass, 0, 0); 1049