Index: head/sys/arm/allwinner/a10_gpio.c =================================================================== --- head/sys/arm/allwinner/a10_gpio.c (revision 277967) +++ head/sys/arm/allwinner/a10_gpio.c (revision 277968) @@ -1,528 +1,529 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2012 Luiz Otavio O Souza. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include "a10_gpio.h" /* * A10 have 9 banks of gpio. * 32 pins per bank: * PA0 - PA17 | PB0 - PB23 | PC0 - PC24 * PD0 - PD27 | PE0 - PE31 | PF0 - PF5 * PG0 - PG9 | PH0 - PH27 | PI0 - PI12 */ #define A10_GPIO_PINS 288 #define A10_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) #define A10_GPIO_NONE 0 #define A10_GPIO_PULLUP 1 #define A10_GPIO_PULLDOWN 2 #define A10_GPIO_INPUT 0 #define A10_GPIO_OUTPUT 1 struct a10_gpio_softc { device_t sc_dev; struct mtx sc_mtx; struct resource * sc_mem_res; struct resource * sc_irq_res; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; void * sc_intrhand; int sc_gpio_npins; struct gpio_pin sc_gpio_pins[A10_GPIO_PINS]; }; #define A10_GPIO_LOCK(_sc) mtx_lock(&_sc->sc_mtx) #define A10_GPIO_UNLOCK(_sc) mtx_unlock(&_sc->sc_mtx) #define A10_GPIO_LOCK_ASSERT(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED) #define A10_GPIO_GP_CFG(_bank, _pin) 0x00 + ((_bank) * 0x24) + ((_pin)<<2) #define A10_GPIO_GP_DAT(_bank) 0x10 + ((_bank) * 0x24) #define A10_GPIO_GP_DRV(_bank, _pin) 0x14 + ((_bank) * 0x24) + ((_pin)<<2) #define A10_GPIO_GP_PUL(_bank, _pin) 0x1c + ((_bank) * 0x24) + ((_pin)<<2) #define A10_GPIO_GP_INT_CFG0 0x200 #define A10_GPIO_GP_INT_CFG1 0x204 #define A10_GPIO_GP_INT_CFG2 0x208 #define A10_GPIO_GP_INT_CFG3 0x20c #define A10_GPIO_GP_INT_CTL 0x210 #define A10_GPIO_GP_INT_STA 0x214 #define A10_GPIO_GP_INT_DEB 0x218 static struct a10_gpio_softc *a10_gpio_sc; #define A10_GPIO_WRITE(_sc, _off, _val) \ bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) #define A10_GPIO_READ(_sc, _off) \ bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off) static uint32_t a10_gpio_get_function(struct a10_gpio_softc *sc, uint32_t pin) { uint32_t bank, func, offset; bank = pin / 32; pin = pin - 32 * bank; func = pin >> 3; offset = ((pin & 0x07) << 2); A10_GPIO_LOCK(sc); func = (A10_GPIO_READ(sc, A10_GPIO_GP_CFG(bank, func)) >> offset) & 7; A10_GPIO_UNLOCK(sc); return (func); } static uint32_t a10_gpio_func_flag(uint32_t nfunc) { switch (nfunc) { case A10_GPIO_INPUT: return (GPIO_PIN_INPUT); case A10_GPIO_OUTPUT: return (GPIO_PIN_OUTPUT); } return (0); } static void a10_gpio_set_function(struct a10_gpio_softc *sc, uint32_t pin, uint32_t f) { uint32_t bank, func, data, offset; /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); bank = pin / 32; pin = pin - 32 * bank; func = pin >> 3; offset = ((pin & 0x07) << 2); data = A10_GPIO_READ(sc, A10_GPIO_GP_CFG(bank, func)); data &= ~(7 << offset); data |= (f << offset); A10_GPIO_WRITE(sc, A10_GPIO_GP_CFG(bank, func), data); } static void a10_gpio_set_pud(struct a10_gpio_softc *sc, uint32_t pin, uint32_t state) { uint32_t bank, offset, pull, val; /* Must be called with lock held. */ A10_GPIO_LOCK_ASSERT(sc); bank = pin / 32; pin = pin - 32 * bank; pull = pin >> 4; offset = ((pin & 0x0f) << 1); val = A10_GPIO_READ(sc, A10_GPIO_GP_PUL(bank, pull)); val &= ~(0x03 << offset); val |= (state << offset); A10_GPIO_WRITE(sc, A10_GPIO_GP_PUL(bank, pull), val); } static void a10_gpio_pin_configure(struct a10_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { A10_GPIO_LOCK(sc); /* * Manage input/output. */ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; a10_gpio_set_function(sc, pin->gp_pin, A10_GPIO_OUTPUT); } else { pin->gp_flags |= GPIO_PIN_INPUT; a10_gpio_set_function(sc, pin->gp_pin, A10_GPIO_INPUT); } } /* Manage Pull-up/pull-down. */ pin->gp_flags &= ~(GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN); if (flags & (GPIO_PIN_PULLUP|GPIO_PIN_PULLDOWN)) { if (flags & GPIO_PIN_PULLUP) { pin->gp_flags |= GPIO_PIN_PULLUP; a10_gpio_set_pud(sc, pin->gp_pin, A10_GPIO_PULLUP); } else { pin->gp_flags |= GPIO_PIN_PULLDOWN; a10_gpio_set_pud(sc, pin->gp_pin, A10_GPIO_PULLDOWN); } } else a10_gpio_set_pud(sc, pin->gp_pin, A10_GPIO_NONE); A10_GPIO_UNLOCK(sc); } static int a10_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = A10_GPIO_PINS - 1; return (0); } static int a10_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct a10_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); A10_GPIO_LOCK(sc); *caps = sc->sc_gpio_pins[i].gp_caps; A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct a10_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); A10_GPIO_LOCK(sc); *flags = sc->sc_gpio_pins[i].gp_flags; A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct a10_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); A10_GPIO_LOCK(sc); memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct a10_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); a10_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); return (0); } static int a10_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct a10_gpio_softc *sc = device_get_softc(dev); uint32_t bank, offset, data; int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); bank = pin / 32; pin = pin - 32 * bank; offset = pin & 0x1f; A10_GPIO_LOCK(sc); data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); if (value) data |= (1 << offset); else data &= ~(1 << offset); A10_GPIO_WRITE(sc, A10_GPIO_GP_DAT(bank), data); A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct a10_gpio_softc *sc = device_get_softc(dev); uint32_t bank, offset, reg_data; int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); bank = pin / 32; pin = pin - 32 * bank; offset = pin & 0x1f; A10_GPIO_LOCK(sc); reg_data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); A10_GPIO_UNLOCK(sc); *val = (reg_data & (1 << offset)) ? 1 : 0; return (0); } static int a10_gpio_pin_toggle(device_t dev, uint32_t pin) { struct a10_gpio_softc *sc = device_get_softc(dev); uint32_t bank, data, offset; int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); bank = pin / 32; pin = pin - 32 * bank; offset = pin & 0x1f; A10_GPIO_LOCK(sc); data = A10_GPIO_READ(sc, A10_GPIO_GP_DAT(bank)); if (data & (1 << offset)) data &= ~(1 << offset); else data |= (1 << offset); A10_GPIO_WRITE(sc, A10_GPIO_GP_DAT(bank), data); A10_GPIO_UNLOCK(sc); return (0); } static int a10_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "allwinner,sun4i-gpio")) return (ENXIO); device_set_desc(dev, "Allwinner GPIO controller"); return (BUS_PROBE_DEFAULT); } static int a10_gpio_attach(device_t dev) { struct a10_gpio_softc *sc = device_get_softc(dev); uint32_t func; int i, rid; phandle_t gpio; sc->sc_dev = dev; mtx_init(&sc->sc_mtx, "a10 gpio", "gpio", MTX_DEF); rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); - return (ENXIO); + goto fail; } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->sc_irq_res) { - bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate interrupt\n"); - return (ENXIO); + goto fail; } /* Find our node. */ gpio = ofw_bus_get_node(sc->sc_dev); if (!OF_hasprop(gpio, "gpio-controller")) /* Node is not a GPIO controller. */ goto fail; /* Initialize the software controlled pins. */ for (i = 0; i < A10_GPIO_PINS; i++) { snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, "pin %d", i); func = a10_gpio_get_function(sc, i); sc->sc_gpio_pins[i].gp_pin = i; sc->sc_gpio_pins[i].gp_caps = A10_GPIO_DEFAULT_CAPS; sc->sc_gpio_pins[i].gp_flags = a10_gpio_func_flag(func); } sc->sc_gpio_npins = i; device_add_child(dev, "gpioc", -1); device_add_child(dev, "gpiobus", -1); a10_gpio_sc = sc; return (bus_generic_attach(dev)); fail: if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); + mtx_destroy(&sc->sc_mtx); + return (ENXIO); } static int a10_gpio_detach(device_t dev) { return (EBUSY); } static device_method_t a10_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, a10_gpio_probe), DEVMETHOD(device_attach, a10_gpio_attach), DEVMETHOD(device_detach, a10_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_pin_max, a10_gpio_pin_max), DEVMETHOD(gpio_pin_getname, a10_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, a10_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, a10_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, a10_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, a10_gpio_pin_get), DEVMETHOD(gpio_pin_set, a10_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, a10_gpio_pin_toggle), DEVMETHOD_END }; static devclass_t a10_gpio_devclass; static driver_t a10_gpio_driver = { "gpio", a10_gpio_methods, sizeof(struct a10_gpio_softc), }; DRIVER_MODULE(a10_gpio, simplebus, a10_gpio_driver, a10_gpio_devclass, 0, 0); int a10_emac_gpio_config(uint32_t pin) { struct a10_gpio_softc *sc = a10_gpio_sc; if (sc == NULL) return (ENXIO); /* Configure pin mux settings for MII. */ A10_GPIO_LOCK(sc); a10_gpio_set_function(sc, pin, A10_GPIO_PULLDOWN); A10_GPIO_UNLOCK(sc); return (0); } Index: head/sys/arm/altera/socfpga/socfpga_gpio.c =================================================================== --- head/sys/arm/altera/socfpga/socfpga_gpio.c (revision 277967) +++ head/sys/arm/altera/socfpga/socfpga_gpio.c (revision 277968) @@ -1,437 +1,438 @@ /*- * Copyright (c) 2015 Ruslan Bukin * All rights reserved. * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) * ("CTSRD"), as part of the DARPA CRASH research programme. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * SOCFPGA General-Purpose I/O Interface. * Chapter 22, Cyclone V Device Handbook (CV-5V2 2014.07.22) */ /* * The GPIO modules are instances of the Synopsys® DesignWare® APB General * Purpose Programming I/O (DW_apb_gpio) peripheral. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #define READ4(_sc, _reg) \ bus_read_4((_sc)->res[0], _reg) #define WRITE4(_sc, _reg, _val) \ bus_write_4((_sc)->res[0], _reg, _val) #define GPIO_SWPORTA_DR 0x00 /* Port A Data Register */ #define GPIO_SWPORTA_DDR 0x04 /* Port A Data Direction Register */ #define GPIO_INTEN 0x30 /* Interrupt Enable Register */ #define GPIO_INTMASK 0x34 /* Interrupt Mask Register */ #define GPIO_INTTYPE_LEVEL 0x38 /* Interrupt Level Register */ #define GPIO_INT_POLARITY 0x3C /* Interrupt Polarity Register */ #define GPIO_INTSTATUS 0x40 /* Interrupt Status Register */ #define GPIO_RAW_INTSTATUS 0x44 /* Raw Interrupt Status Register */ #define GPIO_DEBOUNCE 0x48 /* Debounce Enable Register */ #define GPIO_PORTA_EOI 0x4C /* Clear Interrupt Register */ #define GPIO_EXT_PORTA 0x50 /* External Port A Register */ #define GPIO_LS_SYNC 0x60 /* Synchronization Level Register */ #define GPIO_ID_CODE 0x64 /* ID Code Register */ #define GPIO_VER_ID_CODE 0x6C /* GPIO Version Register */ #define GPIO_CONFIG_REG2 0x70 /* Configuration Register 2 */ #define ENCODED_ID_PWIDTH_M 0x1f /* Width of GPIO Port N Mask */ #define ENCODED_ID_PWIDTH_S(n) (5 * n) /* Width of GPIO Port N Shift */ #define GPIO_CONFIG_REG1 0x74 /* Configuration Register 1 */ enum port_no { PORTA, PORTB, PORTC, PORTD, }; #define NR_GPIO_MAX 32 /* Maximum pins per port */ #define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) /* * GPIO interface */ static int socfpga_gpio_pin_max(device_t, int *); static int socfpga_gpio_pin_getcaps(device_t, uint32_t, uint32_t *); static int socfpga_gpio_pin_getname(device_t, uint32_t, char *); static int socfpga_gpio_pin_getflags(device_t, uint32_t, uint32_t *); static int socfpga_gpio_pin_setflags(device_t, uint32_t, uint32_t); static int socfpga_gpio_pin_set(device_t, uint32_t, unsigned int); static int socfpga_gpio_pin_get(device_t, uint32_t, unsigned int *); static int socfpga_gpio_pin_toggle(device_t, uint32_t pin); struct socfpga_gpio_softc { struct resource *res[1]; bus_space_tag_t bst; bus_space_handle_t bsh; device_t dev; struct mtx sc_mtx; int gpio_npins; struct gpio_pin gpio_pins[NR_GPIO_MAX]; }; struct socfpga_gpio_softc *gpio_sc; static struct resource_spec socfpga_gpio_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static int socfpga_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "snps,dw-apb-gpio")) return (ENXIO); device_set_desc(dev, "DesignWare General-Purpose I/O Interface"); return (BUS_PROBE_DEFAULT); } static int socfpga_gpio_attach(device_t dev) { struct socfpga_gpio_softc *sc; int version; int nr_pins; int cfg2; int i; sc = device_get_softc(dev); sc->dev = dev; mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); if (bus_alloc_resources(dev, socfpga_gpio_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); + mtx_destroy(&sc->sc_mtx); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); gpio_sc = sc; version = READ4(sc, GPIO_VER_ID_CODE); #if 0 device_printf(sc->dev, "Version = 0x%08x\n", version); #endif /* * Take number of pins from hardware. * XXX: Assume we have GPIO port A only. */ cfg2 = READ4(sc, GPIO_CONFIG_REG2); nr_pins = (cfg2 >> ENCODED_ID_PWIDTH_S(PORTA)) & \ ENCODED_ID_PWIDTH_M; sc->gpio_npins = nr_pins + 1; for (i = 0; i < sc->gpio_npins; i++) { sc->gpio_pins[i].gp_pin = i; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; sc->gpio_pins[i].gp_flags = (READ4(sc, GPIO_SWPORTA_DDR) & (1 << i)) ? GPIO_PIN_OUTPUT: GPIO_PIN_INPUT; snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "socfpga_gpio%d.%d", device_get_unit(dev), i); } device_add_child(dev, "gpioc", -1); device_add_child(dev, "gpiobus", -1); return (bus_generic_attach(dev)); } static int socfpga_gpio_pin_max(device_t dev, int *maxpin) { struct socfpga_gpio_softc *sc; sc = device_get_softc(dev); *maxpin = sc->gpio_npins - 1; return (0); } static int socfpga_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct socfpga_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); GPIO_UNLOCK(sc); return (0); } static int socfpga_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct socfpga_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *caps = sc->gpio_pins[i].gp_caps; GPIO_UNLOCK(sc); return (0); } static int socfpga_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct socfpga_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *flags = sc->gpio_pins[i].gp_flags; GPIO_UNLOCK(sc); return (0); } static int socfpga_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct socfpga_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *val = (READ4(sc, GPIO_EXT_PORTA) & (1 << i)) ? 1 : 0; GPIO_UNLOCK(sc); return (0); } static int socfpga_gpio_pin_toggle(device_t dev, uint32_t pin) { struct socfpga_gpio_softc *sc; int reg; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); reg = READ4(sc, GPIO_SWPORTA_DR); if (reg & (1 << i)) reg &= ~(1 << i); else reg |= (1 << i); WRITE4(sc, GPIO_SWPORTA_DR, reg); GPIO_UNLOCK(sc); return (0); } static void socfpga_gpio_pin_configure(struct socfpga_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { int reg; GPIO_LOCK(sc); /* * Manage input/output */ reg = READ4(sc, GPIO_SWPORTA_DDR); if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; reg |= (1 << pin->gp_pin); } else { pin->gp_flags |= GPIO_PIN_INPUT; reg &= ~(1 << pin->gp_pin); } } WRITE4(sc, GPIO_SWPORTA_DDR, reg); GPIO_UNLOCK(sc); } static int socfpga_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct socfpga_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); socfpga_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); return (0); } static int socfpga_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct socfpga_gpio_softc *sc; int reg; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); reg = READ4(sc, GPIO_SWPORTA_DR); if (value) reg |= (1 << i); else reg &= ~(1 << i); WRITE4(sc, GPIO_SWPORTA_DR, reg); GPIO_UNLOCK(sc); return (0); } static device_method_t socfpga_gpio_methods[] = { DEVMETHOD(device_probe, socfpga_gpio_probe), DEVMETHOD(device_attach, socfpga_gpio_attach), /* GPIO protocol */ DEVMETHOD(gpio_pin_max, socfpga_gpio_pin_max), DEVMETHOD(gpio_pin_getname, socfpga_gpio_pin_getname), DEVMETHOD(gpio_pin_getcaps, socfpga_gpio_pin_getcaps), DEVMETHOD(gpio_pin_getflags, socfpga_gpio_pin_getflags), DEVMETHOD(gpio_pin_get, socfpga_gpio_pin_get), DEVMETHOD(gpio_pin_toggle, socfpga_gpio_pin_toggle), DEVMETHOD(gpio_pin_setflags, socfpga_gpio_pin_setflags), DEVMETHOD(gpio_pin_set, socfpga_gpio_pin_set), { 0, 0 } }; static driver_t socfpga_gpio_driver = { "gpio", socfpga_gpio_methods, sizeof(struct socfpga_gpio_softc), }; static devclass_t socfpga_gpio_devclass; DRIVER_MODULE(socfpga_gpio, simplebus, socfpga_gpio_driver, socfpga_gpio_devclass, 0, 0); Index: head/sys/arm/freescale/imx/imx_gpio.c =================================================================== --- head/sys/arm/freescale/imx/imx_gpio.c (revision 277967) +++ head/sys/arm/freescale/imx/imx_gpio.c (revision 277968) @@ -1,481 +1,484 @@ /*- * Copyright (c) 2012, 2013 The FreeBSD Foundation * All rights reserved. * * This software was developed by Oleksandr Rybalko under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Freescale i.MX515 GPIO driver. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define GPIO_LOCK_INIT(_sc) mtx_init(&_sc->sc_mtx, \ device_get_nameunit(_sc->sc_dev), "imx_gpio", MTX_DEF) #define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); #define GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); #define WRITE4(_sc, _r, _v) \ bus_space_write_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r), (_v)) #define READ4(_sc, _r) \ bus_space_read_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r)) #define SET4(_sc, _r, _m) \ WRITE4((_sc), (_r), READ4((_sc), (_r)) | (_m)) #define CLEAR4(_sc, _r, _m) \ WRITE4((_sc), (_r), READ4((_sc), (_r)) & ~(_m)) /* Registers definition for Freescale i.MX515 GPIO controller */ #define IMX_GPIO_DR_REG 0x000 /* Pin Data */ #define IMX_GPIO_OE_REG 0x004 /* Set Pin Output */ #define IMX_GPIO_PSR_REG 0x008 /* Pad Status */ #define IMX_GPIO_ICR1_REG 0x00C /* Interrupt Configuration */ #define IMX_GPIO_ICR2_REG 0x010 /* Interrupt Configuration */ #define GPIO_ICR_COND_LOW 0 #define GPIO_ICR_COND_HIGH 1 #define GPIO_ICR_COND_RISE 2 #define GPIO_ICR_COND_FALL 3 #define IMX_GPIO_IMR_REG 0x014 /* Interrupt Mask Register */ #define IMX_GPIO_ISR_REG 0x018 /* Interrupt Status Register */ #define IMX_GPIO_EDGE_REG 0x01C /* Edge Detect Register */ #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) #define NGPIO 32 struct imx51_gpio_softc { device_t dev; struct mtx sc_mtx; struct resource *sc_res[11]; /* 1 x mem, 2 x IRQ, 8 x IRQ */ void *gpio_ih[11]; /* 1 ptr is not a big waste */ int sc_l_irq; /* Last irq resource */ bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; int gpio_npins; struct gpio_pin gpio_pins[NGPIO]; }; static struct ofw_compat_data compat_data[] = { {"fsl,imx6q-gpio", 1}, {"fsl,imx53-gpio", 1}, {"fsl,imx51-gpio", 1}, {NULL, 0} }; static struct resource_spec imx_gpio_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { -1, 0 } }; static struct resource_spec imx_gpio0irq_spec[] = { { SYS_RES_IRQ, 2, RF_ACTIVE }, { SYS_RES_IRQ, 3, RF_ACTIVE }, { SYS_RES_IRQ, 4, RF_ACTIVE }, { SYS_RES_IRQ, 5, RF_ACTIVE }, { SYS_RES_IRQ, 6, RF_ACTIVE }, { SYS_RES_IRQ, 7, RF_ACTIVE }, { SYS_RES_IRQ, 8, RF_ACTIVE }, { SYS_RES_IRQ, 9, RF_ACTIVE }, { -1, 0 } }; /* * Helpers */ static void imx51_gpio_pin_configure(struct imx51_gpio_softc *, struct gpio_pin *, uint32_t); /* * Driver stuff */ static int imx51_gpio_probe(device_t); static int imx51_gpio_attach(device_t); static int imx51_gpio_detach(device_t); static int imx51_gpio_intr(void *); /* * GPIO interface */ static int imx51_gpio_pin_max(device_t, int *); static int imx51_gpio_pin_getcaps(device_t, uint32_t, uint32_t *); static int imx51_gpio_pin_getflags(device_t, uint32_t, uint32_t *); static int imx51_gpio_pin_getname(device_t, uint32_t, char *); static int imx51_gpio_pin_setflags(device_t, uint32_t, uint32_t); static int imx51_gpio_pin_set(device_t, uint32_t, unsigned int); static int imx51_gpio_pin_get(device_t, uint32_t, unsigned int *); static int imx51_gpio_pin_toggle(device_t, uint32_t pin); static void imx51_gpio_pin_configure(struct imx51_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { GPIO_LOCK(sc); /* * Manage input/output */ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; SET4(sc, IMX_GPIO_OE_REG, (1 << pin->gp_pin)); } else { pin->gp_flags |= GPIO_PIN_INPUT; CLEAR4(sc, IMX_GPIO_OE_REG, (1 << pin->gp_pin)); } } GPIO_UNLOCK(sc); } static int imx51_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = NGPIO - 1; return (0); } static int imx51_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *caps = sc->gpio_pins[i].gp_caps; GPIO_UNLOCK(sc); return (0); } static int imx51_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *flags = sc->gpio_pins[i].gp_flags; GPIO_UNLOCK(sc); return (0); } static int imx51_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); GPIO_UNLOCK(sc); return (0); } static int imx51_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); imx51_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); return (0); } static int imx51_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); if (value) SET4(sc, IMX_GPIO_DR_REG, (1 << i)); else CLEAR4(sc, IMX_GPIO_DR_REG, (1 << i)); GPIO_UNLOCK(sc); return (0); } static int imx51_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *val = (READ4(sc, IMX_GPIO_DR_REG) >> i) & 1; GPIO_UNLOCK(sc); return (0); } static int imx51_gpio_pin_toggle(device_t dev, uint32_t pin) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); WRITE4(sc, IMX_GPIO_DR_REG, (READ4(sc, IMX_GPIO_DR_REG) ^ (1 << i))); GPIO_UNLOCK(sc); return (0); } static int imx51_gpio_intr(void *arg) { struct imx51_gpio_softc *sc; uint32_t input, value; sc = arg; input = READ4(sc, IMX_GPIO_ISR_REG); value = input & READ4(sc, IMX_GPIO_IMR_REG); WRITE4(sc, IMX_GPIO_ISR_REG, input); if (!value) goto intr_done; /* TODO: interrupt handling */ intr_done: return (FILTER_HANDLED); } static int imx51_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "Freescale i.MX GPIO Controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int imx51_gpio_attach(device_t dev) { struct imx51_gpio_softc *sc; int i, irq; sc = device_get_softc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); if (bus_alloc_resources(dev, imx_gpio_spec, sc->sc_res)) { device_printf(dev, "could not allocate resources\n"); + bus_release_resources(dev, imx_gpio_spec, sc->sc_res); + mtx_destroy(&sc->sc_mtx); return (ENXIO); } sc->dev = dev; sc->gpio_npins = NGPIO; sc->sc_l_irq = 2; sc->sc_iot = rman_get_bustag(sc->sc_res[0]); sc->sc_ioh = rman_get_bushandle(sc->sc_res[0]); if (bus_alloc_resources(dev, imx_gpio0irq_spec, &sc->sc_res[3]) == 0) { /* * First GPIO unit able to serve +8 interrupts for 8 first * pins. */ sc->sc_l_irq = 10; } for (irq = 1; irq <= sc->sc_l_irq; irq ++) { if ((bus_setup_intr(dev, sc->sc_res[irq], INTR_TYPE_MISC, imx51_gpio_intr, NULL, sc, &sc->gpio_ih[irq]))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); + imx51_gpio_detach(dev); return (ENXIO); } } for (i = 0; i < sc->gpio_npins; i++) { sc->gpio_pins[i].gp_pin = i; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; sc->gpio_pins[i].gp_flags = (READ4(sc, IMX_GPIO_OE_REG) & (1 << i)) ? GPIO_PIN_OUTPUT: GPIO_PIN_INPUT; snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "imx_gpio%d.%d", device_get_unit(dev), i); } device_add_child(dev, "gpioc", -1); device_add_child(dev, "gpiobus", -1); return (bus_generic_attach(dev)); } static int imx51_gpio_detach(device_t dev) { + int irq; struct imx51_gpio_softc *sc; sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); bus_generic_detach(dev); - - if (sc->sc_res[3]) - bus_release_resources(dev, imx_gpio0irq_spec, &sc->sc_res[3]); - - if (sc->sc_res[0]) - bus_release_resources(dev, imx_gpio_spec, sc->sc_res); - + for (irq = 1; irq <= sc->sc_l_irq; irq ++) { + if (sc->gpio_ih[irq]) + bus_teardown_intr(dev, sc->sc_res[irq], sc->gpio_ih[irq]); + } + bus_release_resources(dev, imx_gpio0irq_spec, &sc->sc_res[3]); + bus_release_resources(dev, imx_gpio_spec, sc->sc_res); mtx_destroy(&sc->sc_mtx); return(0); } static device_method_t imx51_gpio_methods[] = { DEVMETHOD(device_probe, imx51_gpio_probe), DEVMETHOD(device_attach, imx51_gpio_attach), DEVMETHOD(device_detach, imx51_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_pin_max, imx51_gpio_pin_max), DEVMETHOD(gpio_pin_getname, imx51_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, imx51_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, imx51_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, imx51_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, imx51_gpio_pin_get), DEVMETHOD(gpio_pin_set, imx51_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, imx51_gpio_pin_toggle), {0, 0}, }; static driver_t imx51_gpio_driver = { "gpio", imx51_gpio_methods, sizeof(struct imx51_gpio_softc), }; static devclass_t imx51_gpio_devclass; DRIVER_MODULE(imx51_gpio, simplebus, imx51_gpio_driver, imx51_gpio_devclass, 0, 0); Index: head/sys/arm/freescale/vybrid/vf_gpio.c =================================================================== --- head/sys/arm/freescale/vybrid/vf_gpio.c (revision 277967) +++ head/sys/arm/freescale/vybrid/vf_gpio.c (revision 277968) @@ -1,369 +1,370 @@ /*- * Copyright (c) 2013 Ruslan Bukin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Vybrid Family General-Purpose Input/Output (GPIO) * Chapter 7, Vybrid Reference Manual, Rev. 5, 07/2013 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include #include #define GPIO_PDOR(n) (0x00 + 0x40 * (n >> 5)) #define GPIO_PSOR(n) (0x04 + 0x40 * (n >> 5)) #define GPIO_PCOR(n) (0x08 + 0x40 * (n >> 5)) #define GPIO_PTOR(n) (0x0C + 0x40 * (n >> 5)) #define GPIO_PDIR(n) (0x10 + 0x40 * (n >> 5)) #define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) /* * GPIO interface */ static int vf_gpio_pin_max(device_t, int *); static int vf_gpio_pin_getcaps(device_t, uint32_t, uint32_t *); static int vf_gpio_pin_getname(device_t, uint32_t, char *); static int vf_gpio_pin_getflags(device_t, uint32_t, uint32_t *); static int vf_gpio_pin_setflags(device_t, uint32_t, uint32_t); static int vf_gpio_pin_set(device_t, uint32_t, unsigned int); static int vf_gpio_pin_get(device_t, uint32_t, unsigned int *); static int vf_gpio_pin_toggle(device_t, uint32_t pin); struct vf_gpio_softc { struct resource *res[1]; bus_space_tag_t bst; bus_space_handle_t bsh; struct mtx sc_mtx; int gpio_npins; struct gpio_pin gpio_pins[NGPIO]; }; struct vf_gpio_softc *gpio_sc; static struct resource_spec vf_gpio_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { -1, 0 } }; static int vf_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "fsl,mvf600-gpio")) return (ENXIO); device_set_desc(dev, "Vybrid Family GPIO Unit"); return (BUS_PROBE_DEFAULT); } static int vf_gpio_attach(device_t dev) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); if (bus_alloc_resources(dev, vf_gpio_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); + mtx_destroy(&sc->sc_mtx); return (ENXIO); } /* Memory interface */ sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); gpio_sc = sc; sc->gpio_npins = NGPIO; for (i = 0; i < sc->gpio_npins; i++) { sc->gpio_pins[i].gp_pin = i; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; sc->gpio_pins[i].gp_flags = (READ4(sc, GPIO_PDOR(i)) & (1 << (i % 32))) ? GPIO_PIN_OUTPUT: GPIO_PIN_INPUT; snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "vf_gpio%d.%d", device_get_unit(dev), i); } device_add_child(dev, "gpioc", -1); device_add_child(dev, "gpiobus", -1); return (bus_generic_attach(dev)); } static int vf_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = NGPIO - 1; return (0); } static int vf_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); GPIO_UNLOCK(sc); return (0); } static int vf_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *caps = sc->gpio_pins[i].gp_caps; GPIO_UNLOCK(sc); return (0); } static int vf_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *flags = sc->gpio_pins[i].gp_flags; GPIO_UNLOCK(sc); return (0); } static int vf_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *val = (READ4(sc, GPIO_PDIR(i)) & (1 << (i % 32))) ? 1 : 0; GPIO_UNLOCK(sc); return (0); } static int vf_gpio_pin_toggle(device_t dev, uint32_t pin) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); WRITE4(sc, GPIO_PTOR(i), (1 << (i % 32))); GPIO_UNLOCK(sc); return (0); } static void vf_gpio_pin_configure(struct vf_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { GPIO_LOCK(sc); /* * Manage input/output */ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; } else { pin->gp_flags |= GPIO_PIN_INPUT; WRITE4(sc, GPIO_PCOR(pin->gp_pin), (1 << (pin->gp_pin % 32))); } } GPIO_UNLOCK(sc); } static int vf_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); vf_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); return (0); } static int vf_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct vf_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); if (value) WRITE4(sc, GPIO_PSOR(i), (1 << (i % 32))); else WRITE4(sc, GPIO_PCOR(i), (1 << (i % 32))); GPIO_UNLOCK(sc); return (0); } static device_method_t vf_gpio_methods[] = { DEVMETHOD(device_probe, vf_gpio_probe), DEVMETHOD(device_attach, vf_gpio_attach), /* GPIO protocol */ DEVMETHOD(gpio_pin_max, vf_gpio_pin_max), DEVMETHOD(gpio_pin_getname, vf_gpio_pin_getname), DEVMETHOD(gpio_pin_getcaps, vf_gpio_pin_getcaps), DEVMETHOD(gpio_pin_getflags, vf_gpio_pin_getflags), DEVMETHOD(gpio_pin_get, vf_gpio_pin_get), DEVMETHOD(gpio_pin_toggle, vf_gpio_pin_toggle), DEVMETHOD(gpio_pin_setflags, vf_gpio_pin_setflags), DEVMETHOD(gpio_pin_set, vf_gpio_pin_set), { 0, 0 } }; static driver_t vf_gpio_driver = { "gpio", vf_gpio_methods, sizeof(struct vf_gpio_softc), }; static devclass_t vf_gpio_devclass; DRIVER_MODULE(vf_gpio, simplebus, vf_gpio_driver, vf_gpio_devclass, 0, 0); Index: head/sys/arm/rockchip/rk30xx_gpio.c =================================================================== --- head/sys/arm/rockchip/rk30xx_gpio.c (revision 277967) +++ head/sys/arm/rockchip/rk30xx_gpio.c (revision 277968) @@ -1,625 +1,624 @@ /*- * Copyright (c) 2013 Ganbold Tsagaankhuu * Copyright (c) 2012 Oleksandr Tymoshenko * Copyright (c) 2012 Luiz Otavio O Souza. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include "rk30xx_grf.h" #include "rk30xx_pmu.h" /* * RK3188 has 4 banks of gpio. * 32 pins per bank * PA0 - PA7 | PB0 - PB7 * PC0 - PC7 | PD0 - PD7 */ #define RK30_GPIO_PINS 32 #define RK30_GPIO_DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | \ GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN) #define RK30_GPIO_NONE 0 #define RK30_GPIO_PULLUP 1 #define RK30_GPIO_PULLDOWN 2 struct rk30_gpio_softc { device_t sc_dev; struct mtx sc_mtx; struct resource * sc_mem_res; struct resource * sc_irq_res; bus_space_tag_t sc_bst; bus_space_handle_t sc_bsh; void * sc_intrhand; int sc_bank; int sc_gpio_npins; struct gpio_pin sc_gpio_pins[RK30_GPIO_PINS]; }; /* We use our base address to find out our bank number. */ static unsigned long rk30_gpio_base_addr[4] = { 0x2000a000, 0x2003c000, 0x2003e000, 0x20080000 }; static struct rk30_gpio_softc *rk30_gpio_sc = NULL; typedef int (*gpios_phandler_t)(phandle_t, pcell_t *, int); struct gpio_ctrl_entry { const char *compat; gpios_phandler_t handler; }; int rk30_gpios_prop_handle(phandle_t ctrl, pcell_t *gpios, int len); static int rk30_gpio_init(void); struct gpio_ctrl_entry gpio_controllers[] = { { "rockchip,rk30xx-gpio", &rk30_gpios_prop_handle }, { "rockchip,rk30xx-gpio", &rk30_gpios_prop_handle }, { "rockchip,rk30xx-gpio", &rk30_gpios_prop_handle }, { "rockchip,rk30xx-gpio", &rk30_gpios_prop_handle }, { NULL, NULL } }; #define RK30_GPIO_LOCK(_sc) mtx_lock(&_sc->sc_mtx) #define RK30_GPIO_UNLOCK(_sc) mtx_unlock(&_sc->sc_mtx) #define RK30_GPIO_LOCK_ASSERT(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED) #define RK30_GPIO_SWPORT_DR 0x00 #define RK30_GPIO_SWPORT_DDR 0x04 #define RK30_GPIO_INTEN 0x30 #define RK30_GPIO_INTMASK 0x34 #define RK30_GPIO_INTTYPE_LEVEL 0x38 #define RK30_GPIO_INT_POLARITY 0x3c #define RK30_GPIO_INT_STATUS 0x40 #define RK30_GPIO_INT_RAWSTATUS 0x44 #define RK30_GPIO_DEBOUNCE 0x48 #define RK30_GPIO_PORT_EOI 0x4c #define RK30_GPIO_EXT_PORT 0x50 #define RK30_GPIO_LS_SYNC 0x60 #define RK30_GPIO_WRITE(_sc, _off, _val) \ bus_space_write_4(_sc->sc_bst, _sc->sc_bsh, _off, _val) #define RK30_GPIO_READ(_sc, _off) \ bus_space_read_4(_sc->sc_bst, _sc->sc_bsh, _off) static uint32_t rk30_gpio_get_function(struct rk30_gpio_softc *sc, uint32_t pin) { if (RK30_GPIO_READ(sc, RK30_GPIO_SWPORT_DDR) & (1U << pin)) return (GPIO_PIN_OUTPUT); else return (GPIO_PIN_INPUT); } static void rk30_gpio_set_function(struct rk30_gpio_softc *sc, uint32_t pin, uint32_t func) { uint32_t data; /* Must be called with lock held. */ RK30_GPIO_LOCK_ASSERT(sc); data = RK30_GPIO_READ(sc, RK30_GPIO_SWPORT_DDR); if (func == GPIO_PIN_OUTPUT) data |= (1U << pin); else data &= ~(1U << pin); RK30_GPIO_WRITE(sc, RK30_GPIO_SWPORT_DDR, data); } static void rk30_gpio_set_pud(struct rk30_gpio_softc *sc, uint32_t pin, uint32_t state) { uint32_t pud; /* Must be called with lock held. */ RK30_GPIO_LOCK_ASSERT(sc); switch (state) { case GPIO_PIN_PULLUP: pud = RK30_GPIO_PULLUP; break; case GPIO_PIN_PULLDOWN: pud = RK30_GPIO_PULLDOWN; break; default: pud = RK30_GPIO_NONE; } /* * The pull up/down registers for GPIO0A and half of GPIO0B * (the first 12 pins on bank 0) are at a different location. */ if (sc->sc_bank == 0 && pin < 12) rk30_pmu_gpio_pud(pin, pud); else rk30_grf_gpio_pud(sc->sc_bank, pin, pud); } static void rk30_gpio_pin_configure(struct rk30_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { RK30_GPIO_LOCK(sc); /* * Manage input/output. */ if (flags & (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT | GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) pin->gp_flags |= GPIO_PIN_OUTPUT; else pin->gp_flags |= GPIO_PIN_INPUT; rk30_gpio_set_function(sc, pin->gp_pin, pin->gp_flags); } /* Manage Pull-up/pull-down. */ pin->gp_flags &= ~(GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN); if (flags & (GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN)) { if (flags & GPIO_PIN_PULLUP) pin->gp_flags |= GPIO_PIN_PULLUP; else pin->gp_flags |= GPIO_PIN_PULLDOWN; } rk30_gpio_set_pud(sc, pin->gp_pin, pin->gp_flags); RK30_GPIO_UNLOCK(sc); } static int rk30_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = RK30_GPIO_PINS - 1; return (0); } static int rk30_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct rk30_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); RK30_GPIO_LOCK(sc); *caps = sc->sc_gpio_pins[i].gp_caps; RK30_GPIO_UNLOCK(sc); return (0); } static int rk30_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct rk30_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); RK30_GPIO_LOCK(sc); *flags = sc->sc_gpio_pins[i].gp_flags; RK30_GPIO_UNLOCK(sc); return (0); } static int rk30_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct rk30_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); RK30_GPIO_LOCK(sc); memcpy(name, sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME); RK30_GPIO_UNLOCK(sc); return (0); } static int rk30_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct rk30_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); rk30_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); return (0); } static int rk30_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { int i; struct rk30_gpio_softc *sc; uint32_t data; sc = device_get_softc(dev); for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); RK30_GPIO_LOCK(sc); data = RK30_GPIO_READ(sc, RK30_GPIO_SWPORT_DR); if (value) data |= (1U << pin); else data &= ~(1U << pin); RK30_GPIO_WRITE(sc, RK30_GPIO_SWPORT_DR, data); RK30_GPIO_UNLOCK(sc); return (0); } static int rk30_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { int i; struct rk30_gpio_softc *sc; uint32_t data; sc = device_get_softc(dev); for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); RK30_GPIO_LOCK(sc); data = RK30_GPIO_READ(sc, RK30_GPIO_EXT_PORT); RK30_GPIO_UNLOCK(sc); *val = (data & (1U << pin)) ? 1 : 0; return (0); } static int rk30_gpio_pin_toggle(device_t dev, uint32_t pin) { int i; struct rk30_gpio_softc *sc; uint32_t data; sc = device_get_softc(dev); for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); RK30_GPIO_LOCK(sc); data = RK30_GPIO_READ(sc, RK30_GPIO_SWPORT_DR); if (data & (1U << pin)) data &= ~(1U << pin); else data |= (1U << pin); RK30_GPIO_WRITE(sc, RK30_GPIO_SWPORT_DR, data); RK30_GPIO_UNLOCK(sc); return (0); } static int rk30_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (!ofw_bus_is_compatible(dev, "rockchip,rk30xx-gpio")) return (ENXIO); device_set_desc(dev, "Rockchip RK30XX GPIO controller"); return (BUS_PROBE_DEFAULT); } static int rk30_gpio_attach(device_t dev) { struct rk30_gpio_softc *sc = device_get_softc(dev); int i, rid; phandle_t gpio; unsigned long start; if (rk30_gpio_sc) return (ENXIO); sc->sc_dev = dev; + mtx_init(&sc->sc_mtx, "rk30 gpio", "gpio", MTX_DEF); rid = 0; sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!sc->sc_mem_res) { device_printf(dev, "cannot allocate memory window\n"); - return (ENXIO); + goto fail; } sc->sc_bst = rman_get_bustag(sc->sc_mem_res); sc->sc_bsh = rman_get_bushandle(sc->sc_mem_res); /* Check the unit we are attaching by our base address. */ sc->sc_bank = -1; start = rman_get_start(sc->sc_mem_res); for (i = 0; i < nitems(rk30_gpio_base_addr); i++) { if (rk30_gpio_base_addr[i] == start) { sc->sc_bank = i; break; } } if (sc->sc_bank == -1) { device_printf(dev, "unsupported device unit (only GPIO0..3 are supported)\n"); - bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); - return (ENXIO); + goto fail; } rid = 0; sc->sc_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_ACTIVE); if (!sc->sc_irq_res) { - bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); device_printf(dev, "cannot allocate interrupt\n"); - return (ENXIO); + goto fail; } /* Find our node. */ gpio = ofw_bus_get_node(sc->sc_dev); if (!OF_hasprop(gpio, "gpio-controller")) /* Node is not a GPIO controller. */ goto fail; - mtx_init(&sc->sc_mtx, "rk30 gpio", "gpio", MTX_DEF); - /* Initialize the software controlled pins. */ for (i = 0; i < RK30_GPIO_PINS; i++) { snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, "pin %d", i); sc->sc_gpio_pins[i].gp_pin = i; sc->sc_gpio_pins[i].gp_caps = RK30_GPIO_DEFAULT_CAPS; sc->sc_gpio_pins[i].gp_flags = rk30_gpio_get_function(sc, i); } sc->sc_gpio_npins = i; device_add_child(dev, "gpioc", -1); device_add_child(dev, "gpiobus", -1); rk30_gpio_sc = sc; rk30_gpio_init(); return (bus_generic_attach(dev)); fail: if (sc->sc_irq_res) bus_release_resource(dev, SYS_RES_IRQ, 0, sc->sc_irq_res); if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); + mtx_destroy(&sc->sc_mtx); + return (ENXIO); } static int rk30_gpio_detach(device_t dev) { return (EBUSY); } static device_method_t rk30_gpio_methods[] = { /* Device interface */ DEVMETHOD(device_probe, rk30_gpio_probe), DEVMETHOD(device_attach, rk30_gpio_attach), DEVMETHOD(device_detach, rk30_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_pin_max, rk30_gpio_pin_max), DEVMETHOD(gpio_pin_getname, rk30_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, rk30_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, rk30_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, rk30_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, rk30_gpio_pin_get), DEVMETHOD(gpio_pin_set, rk30_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, rk30_gpio_pin_toggle), DEVMETHOD_END }; static devclass_t rk30_gpio_devclass; static driver_t rk30_gpio_driver = { "gpio", rk30_gpio_methods, sizeof(struct rk30_gpio_softc), }; DRIVER_MODULE(rk30_gpio, simplebus, rk30_gpio_driver, rk30_gpio_devclass, 0, 0); int rk30_gpios_prop_handle(phandle_t ctrl, pcell_t *gpios, int len) { struct rk30_gpio_softc *sc; pcell_t gpio_cells; int inc, t, tuples, tuple_size; int dir, flags, pin, i; u_long gpio_ctrl, size; sc = rk30_gpio_sc; if (sc == NULL) return ENXIO; if (OF_getprop(ctrl, "#gpio-cells", &gpio_cells, sizeof(pcell_t)) < 0) return (ENXIO); gpio_cells = fdt32_to_cpu(gpio_cells); if (gpio_cells != 2) return (ENXIO); tuple_size = gpio_cells * sizeof(pcell_t) + sizeof(phandle_t); tuples = len / tuple_size; if (fdt_regsize(ctrl, &gpio_ctrl, &size)) return (ENXIO); /* * Skip controller reference, since controller's phandle is given * explicitly (in a function argument). */ inc = sizeof(ihandle_t) / sizeof(pcell_t); gpios += inc; for (t = 0; t < tuples; t++) { pin = fdt32_to_cpu(gpios[0]); dir = fdt32_to_cpu(gpios[1]); flags = fdt32_to_cpu(gpios[2]); for (i = 0; i < sc->sc_gpio_npins; i++) { if (sc->sc_gpio_pins[i].gp_pin == pin) break; } if (i >= sc->sc_gpio_npins) return (EINVAL); rk30_gpio_pin_configure(sc, &sc->sc_gpio_pins[i], flags); if (dir == 1) { /* Input. */ rk30_gpio_pin_set(sc->sc_dev, pin, GPIO_PIN_INPUT); } else { /* Output. */ rk30_gpio_pin_set(sc->sc_dev, pin, GPIO_PIN_OUTPUT); } gpios += gpio_cells + inc; } return (0); } #define MAX_PINS_PER_NODE 5 #define GPIOS_PROP_CELLS 4 static int rk30_gpio_init(void) { phandle_t child, parent, root, ctrl; pcell_t gpios[MAX_PINS_PER_NODE * GPIOS_PROP_CELLS]; struct gpio_ctrl_entry *e; int len, rv; root = OF_finddevice("/"); len = 0; parent = root; /* Traverse through entire tree to find nodes with 'gpios' prop */ for (child = OF_child(parent); child != 0; child = OF_peer(child)) { /* Find a 'leaf'. Start the search from this node. */ while (OF_child(child)) { parent = child; child = OF_child(child); } if ((len = OF_getproplen(child, "gpios")) > 0) { if (len > sizeof(gpios)) return (ENXIO); /* Get 'gpios' property. */ OF_getprop(child, "gpios", &gpios, len); e = (struct gpio_ctrl_entry *)&gpio_controllers; /* Find and call a handler. */ for (; e->compat; e++) { /* * First cell of 'gpios' property should * contain a ref. to a node defining GPIO * controller. */ ctrl = OF_node_from_xref(fdt32_to_cpu(gpios[0])); if (fdt_is_compatible(ctrl, e->compat)) /* Call a handler. */ if ((rv = e->handler(ctrl, (pcell_t *)&gpios, len))) return (rv); } } if (OF_peer(child) == 0) { /* No more siblings. */ child = parent; parent = OF_parent(child); } } return (0); } Index: head/sys/arm/samsung/exynos/exynos5_pad.c =================================================================== --- head/sys/arm/samsung/exynos/exynos5_pad.c (revision 277967) +++ head/sys/arm/samsung/exynos/exynos5_pad.c (revision 277968) @@ -1,828 +1,839 @@ /*- * Copyright (c) 2014 Ruslan Bukin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Samsung Exynos 5 Pad Control * Chapter 4, Exynos 5 Dual User's Manual Public Rev 1.00 */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include #include #define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) #define MAX_PORTS 5 #define MAX_NGPIO 253 #define N_EXT_INTS 16 #define EXYNOS5250 1 #define EXYNOS5420 2 #define PIN_IN 0 #define PIN_OUT 1 #define READ4(_sc, _port, _reg) \ bus_space_read_4(_sc->bst[_port], _sc->bsh[_port], _reg) #define WRITE4(_sc, _port, _reg, _val) \ bus_space_write_4(_sc->bst[_port], _sc->bsh[_port], _reg, _val) /* * GPIO interface */ static int pad_pin_max(device_t, int *); static int pad_pin_getcaps(device_t, uint32_t, uint32_t *); static int pad_pin_getname(device_t, uint32_t, char *); static int pad_pin_getflags(device_t, uint32_t, uint32_t *); static int pad_pin_setflags(device_t, uint32_t, uint32_t); static int pad_pin_set(device_t, uint32_t, unsigned int); static int pad_pin_get(device_t, uint32_t, unsigned int *); static int pad_pin_toggle(device_t, uint32_t pin); struct gpio_bank { char *name; uint32_t port; uint32_t con; uint32_t ngpio; uint32_t ext_con; uint32_t ext_flt_con; uint32_t mask; uint32_t pend; }; struct pad_softc { struct resource *res[MAX_PORTS * 2]; bus_space_tag_t bst[MAX_PORTS]; bus_space_handle_t bsh[MAX_PORTS]; struct mtx sc_mtx; int gpio_npins; struct gpio_pin gpio_pins[MAX_NGPIO]; void *gpio_ih[MAX_PORTS]; device_t dev; int model; struct resource_spec *pad_spec; struct gpio_bank *gpio_map; struct interrupt_entry *interrupt_table; int nports; }; struct pad_softc *gpio_sc; static struct resource_spec pad_spec_5250[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { SYS_RES_MEMORY, 2, RF_ACTIVE }, { SYS_RES_MEMORY, 3, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { SYS_RES_IRQ, 3, RF_ACTIVE }, { -1, 0 } }; static struct resource_spec pad_spec_5420[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE }, { SYS_RES_MEMORY, 2, RF_ACTIVE }, { SYS_RES_MEMORY, 3, RF_ACTIVE }, { SYS_RES_MEMORY, 4, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { SYS_RES_IRQ, 2, RF_ACTIVE }, { SYS_RES_IRQ, 3, RF_ACTIVE }, { SYS_RES_IRQ, 4, RF_ACTIVE }, { -1, 0 } }; static struct ofw_compat_data compat_data[] = { {"samsung,exynos5420-padctrl", EXYNOS5420}, {"samsung,exynos5250-padctrl", EXYNOS5250}, {NULL, 0} }; struct pad_intr { uint32_t enabled; void (*ih) (void *); void *ih_user; }; static struct pad_intr intr_map[MAX_NGPIO]; struct interrupt_entry { int gpio_number; char *combiner_source_name; }; struct interrupt_entry interrupt_table_5250[N_EXT_INTS] = { { 147, "EINT[15]" }, { 146, "EINT[14]" }, { 145, "EINT[13]" }, { 144, "EINT[12]" }, { 143, "EINT[11]" }, { 142, "EINT[10]" }, { 141, "EINT[9]" }, { 140, "EINT[8]" }, { 139, "EINT[7]" }, { 138, "EINT[6]" }, { 137, "EINT[5]" }, { 136, "EINT[4]" }, { 135, "EINT[3]" }, { 134, "EINT[2]" }, { 133, "EINT[1]" }, { 132, "EINT[0]" }, }; struct interrupt_entry interrupt_table_5420[N_EXT_INTS] = { { 23, "EINT[15]" }, { 22, "EINT[14]" }, { 21, "EINT[13]" }, { 20, "EINT[12]" }, { 19, "EINT[11]" }, { 18, "EINT[10]" }, { 17, "EINT[9]" }, { 16, "EINT[8]" }, { 15, "EINT[7]" }, { 14, "EINT[6]" }, { 13, "EINT[5]" }, { 12, "EINT[4]" }, { 11, "EINT[3]" }, { 10, "EINT[2]" }, { 9, "EINT[1]" }, { 8, "EINT[0]" }, }; /* * 253 multi-functional input/output ports */ static struct gpio_bank gpio_map_5250[] = { /* first 132 gpio */ { "gpa0", 0, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, { "gpa1", 0, 0x020, 6, 0x704, 0x808, 0x904, 0xA04 }, { "gpa2", 0, 0x040, 8, 0x708, 0x810, 0x908, 0xA08 }, { "gpb0", 0, 0x060, 5, 0x70C, 0x818, 0x90C, 0xA0C }, { "gpb1", 0, 0x080, 5, 0x710, 0x820, 0x910, 0xA10 }, { "gpb2", 0, 0x0A0, 4, 0x714, 0x828, 0x914, 0xA14 }, { "gpb3", 0, 0x0C0, 4, 0x718, 0x830, 0x918, 0xA18 }, { "gpc0", 0, 0x0E0, 7, 0x71C, 0x838, 0x91C, 0xA1C }, { "gpc1", 0, 0x100, 4, 0x720, 0x840, 0x920, 0xA20 }, { "gpc2", 0, 0x120, 7, 0x724, 0x848, 0x924, 0xA24 }, { "gpc3", 0, 0x140, 7, 0x728, 0x850, 0x928, 0xA28 }, { "gpd0", 0, 0x160, 4, 0x72C, 0x858, 0x92C, 0xA2C }, { "gpd1", 0, 0x180, 8, 0x730, 0x860, 0x930, 0xA30 }, { "gpy0", 0, 0x1A0, 6, 0, 0, 0, 0 }, { "gpy1", 0, 0x1C0, 4, 0, 0, 0, 0 }, { "gpy2", 0, 0x1E0, 6, 0, 0, 0, 0 }, { "gpy3", 0, 0x200, 8, 0, 0, 0, 0 }, { "gpy4", 0, 0x220, 8, 0, 0, 0, 0 }, { "gpy5", 0, 0x240, 8, 0, 0, 0, 0 }, { "gpy6", 0, 0x260, 8, 0, 0, 0, 0 }, { "gpc4", 0, 0x2E0, 7, 0x734, 0x868, 0x934, 0xA34 }, /* next 32 */ { "gpx0", 0, 0xC00, 8, 0xE00, 0xE80, 0xF00, 0xF40 }, { "gpx1", 0, 0xC20, 8, 0xE04, 0xE88, 0xF04, 0xF44 }, { "gpx2", 0, 0xC40, 8, 0xE08, 0xE90, 0xF08, 0xF48 }, { "gpx3", 0, 0xC60, 8, 0xE0C, 0xE98, 0xF0C, 0xF4C }, { "gpe0", 1, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, { "gpe1", 1, 0x020, 2, 0x704, 0x808, 0x904, 0xA04 }, { "gpf0", 1, 0x040, 4, 0x708, 0x810, 0x908, 0xA08 }, { "gpf1", 1, 0x060, 4, 0x70C, 0x818, 0x90C, 0xA0C }, { "gpg0", 1, 0x080, 8, 0x710, 0x820, 0x910, 0xA10 }, { "gpg1", 1, 0x0A0, 8, 0x714, 0x828, 0x914, 0xA14 }, { "gpg2", 1, 0x0C0, 2, 0x718, 0x830, 0x918, 0xA18 }, { "gph0", 1, 0x0E0, 4, 0x71C, 0x838, 0x91C, 0xA1C }, { "gph1", 1, 0x100, 8, 0x720, 0x840, 0x920, 0xA20 }, { "gpv0", 2, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, { "gpv1", 2, 0x020, 8, 0x704, 0x808, 0x904, 0xA04 }, { "gpv2", 2, 0x060, 8, 0x708, 0x810, 0x908, 0xA08 }, { "gpv3", 2, 0x080, 8, 0x70C, 0x818, 0x90C, 0xA0C }, { "gpv4", 2, 0x0C0, 2, 0x710, 0x820, 0x910, 0xA10 }, { "gpz", 3, 0x000, 7, 0x700, 0x800, 0x900, 0xA00 }, { NULL, -1, -1, -1, -1, -1, -1, -1 }, }; static struct gpio_bank gpio_map_5420[] = { /* First 40 */ { "gpy7", 0, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, { "gpx0", 0, 0xC00, 8, 0x704, 0xE80, 0xF00, 0xF40 }, { "gpx1", 0, 0xC20, 8, 0x708, 0xE88, 0xF04, 0xF44 }, { "gpx2", 0, 0xC40, 8, 0x70C, 0xE90, 0xF08, 0xF48 }, { "gpx3", 0, 0xC60, 8, 0x710, 0xE98, 0xF0C, 0xF4C }, /* Next 85 */ { "gpc0", 1, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, { "gpc1", 1, 0x020, 8, 0x704, 0x808, 0x904, 0xA04 }, { "gpc2", 1, 0x040, 7, 0x708, 0x810, 0x908, 0xA08 }, { "gpc3", 1, 0x060, 4, 0x70C, 0x818, 0x90C, 0xA0C }, { "gpc4", 1, 0x080, 2, 0x710, 0x820, 0x910, 0xA10 }, { "gpd1", 1, 0x0A0, 8, 0x714, 0x828, 0x914, 0xA14 }, { "gpy0", 1, 0x0C0, 6, 0x718, 0x830, 0x918, 0xA18 }, { "gpy1", 1, 0x0E0, 4, 0x71C, 0x838, 0x91C, 0xA1C }, { "gpy2", 1, 0x100, 6, 0x720, 0x840, 0x920, 0xA20 }, { "gpy3", 1, 0x120, 8, 0x724, 0x848, 0x924, 0xA24 }, { "gpy4", 1, 0x140, 8, 0x728, 0x850, 0x928, 0xA28 }, { "gpy5", 1, 0x160, 8, 0x72C, 0x858, 0x92C, 0xA2C }, { "gpy6", 1, 0x180, 8, 0x730, 0x860, 0x930, 0xA30 }, /* Next 46 */ { "gpe0", 2, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, { "gpe1", 2, 0x020, 2, 0x704, 0x808, 0x904, 0xA04 }, { "gpf0", 2, 0x040, 6, 0x708, 0x810, 0x908, 0xA08 }, { "gpf1", 2, 0x060, 8, 0x70C, 0x818, 0x90C, 0xA0C }, { "gpg0", 2, 0x080, 8, 0x710, 0x820, 0x910, 0xA10 }, { "gpg1", 2, 0x0A0, 8, 0x714, 0x828, 0x914, 0xA14 }, { "gpg2", 2, 0x0C0, 2, 0x718, 0x830, 0x918, 0xA18 }, { "gpj4", 2, 0x0E0, 4, 0x71C, 0x838, 0x91C, 0xA1C }, /* Next 54 */ { "gpa0", 3, 0x000, 8, 0x700, 0x800, 0x900, 0xA00 }, { "gpa1", 3, 0x020, 6, 0x704, 0x808, 0x904, 0xA04 }, { "gpa2", 3, 0x040, 8, 0x708, 0x810, 0x908, 0xA08 }, { "gpb0", 3, 0x060, 5, 0x70C, 0x818, 0x90C, 0xA0C }, { "gpb1", 3, 0x080, 5, 0x710, 0x820, 0x910, 0xA10 }, { "gpb2", 3, 0x0A0, 4, 0x714, 0x828, 0x914, 0xA14 }, { "gpb3", 3, 0x0C0, 8, 0x718, 0x830, 0x918, 0xA18 }, { "gpb4", 3, 0x0E0, 2, 0x71C, 0x838, 0x91C, 0xA1C }, { "gph0", 3, 0x100, 8, 0x720, 0x840, 0x920, 0xA20 }, /* Last 7 */ { "gpz", 4, 0x000, 7, 0x700, 0x800, 0x900, 0xA00 }, { NULL, -1, -1, -1, -1, -1, -1, -1 }, }; static int get_bank(struct pad_softc *sc, int gpio_number, struct gpio_bank *bank, int *pin_shift) { int ngpio; int i; int n; n = 0; for (i = 0; sc->gpio_map[i].ngpio != -1; i++) { ngpio = sc->gpio_map[i].ngpio; if ((n + ngpio) > gpio_number) { *bank = sc->gpio_map[i]; *pin_shift = (gpio_number - n); return (0); }; n += ngpio; }; return (-1); } static int port_intr(void *arg) { struct port_softc *sc; sc = arg; return (FILTER_HANDLED); } static void ext_intr(void *arg) { struct pad_softc *sc; void (*ih) (void *); void *ih_user; int ngpio; int found; int reg; int i,j; int n,k; sc = arg; n = 0; for (i = 0; sc->gpio_map[i].ngpio != -1; i++) { found = 0; ngpio = sc->gpio_map[i].ngpio; if (sc->gpio_map[i].pend == 0) { n += ngpio; continue; } reg = READ4(sc, sc->gpio_map[i].port, sc->gpio_map[i].pend); for (j = 0; j < ngpio; j++) { if (reg & (1 << j)) { found = 1; k = (n + j); if (intr_map[k].enabled == 1) { ih = intr_map[k].ih; ih_user = intr_map[k].ih_user; ih(ih_user); } } } if (found) { /* ACK */ WRITE4(sc, sc->gpio_map[i].port, sc->gpio_map[i].pend, reg); } n += ngpio; } } int pad_setup_intr(int gpio_number, void (*ih)(void *), void *ih_user) { struct interrupt_entry *entry; struct pad_intr *pad_irq; struct gpio_bank bank; struct pad_softc *sc; int pin_shift; int reg; int i; sc = gpio_sc; if (sc == NULL) { device_printf(sc->dev, "Error: pad is not attached\n"); return (-1); } if (get_bank(sc, gpio_number, &bank, &pin_shift) != 0) return (-1); entry = NULL; for (i = 0; i < N_EXT_INTS; i++) if (sc->interrupt_table[i].gpio_number == gpio_number) entry = &(sc->interrupt_table[i]); if (entry == NULL) { device_printf(sc->dev, "Cant find interrupt source for %d\n", gpio_number); return (-1); } #if 0 printf("Request interrupt name %s\n", entry->combiner_source_name); #endif pad_irq = &intr_map[gpio_number]; pad_irq->enabled = 1; pad_irq->ih = ih; pad_irq->ih_user = ih_user; /* Setup port as external interrupt source */ reg = READ4(sc, bank.port, bank.con); reg |= (0xf << (pin_shift * 4)); #if 0 printf("writing 0x%08x to 0x%08x\n", reg, bank.con); #endif WRITE4(sc, bank.port, bank.con, reg); /* * Configure interrupt pin * * 0x0 = Sets Low level * 0x1 = Sets High level * 0x2 = Triggers Falling edge * 0x3 = Triggers Rising edge * 0x4 = Triggers Both edge * * TODO: add parameter. For now configure as 0x0 */ reg = READ4(sc, bank.port, bank.ext_con); reg &= ~(0x7 << (pin_shift * 4)); WRITE4(sc, bank.port, bank.ext_con, reg); /* Unmask */ reg = READ4(sc, bank.port, bank.mask); reg &= ~(1 << pin_shift); WRITE4(sc, bank.port, bank.mask, reg); combiner_setup_intr(entry->combiner_source_name, ext_intr, sc); return (0); } static int pad_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "Exynos Pad Control"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int pad_attach(device_t dev) { struct gpio_bank bank; struct pad_softc *sc; int pin_shift; int reg; int i; sc = device_get_softc(dev); mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); sc->model = ofw_bus_search_compatible(dev, compat_data)->ocd_data; switch (sc->model) { case EXYNOS5250: sc->pad_spec = pad_spec_5250; sc->gpio_map = gpio_map_5250; sc->interrupt_table = interrupt_table_5250; sc->gpio_npins = 253; sc->nports = 4; break; case EXYNOS5420: sc->pad_spec = pad_spec_5420; sc->gpio_map = gpio_map_5420; sc->interrupt_table = interrupt_table_5420; sc->gpio_npins = 232; sc->nports = 5; break; default: - return (-1); + goto fail; }; if (bus_alloc_resources(dev, sc->pad_spec, sc->res)) { device_printf(dev, "could not allocate resources\n"); - return (ENXIO); + goto fail; } /* Memory interface */ for (i = 0; i < sc->nports; i++) { sc->bst[i] = rman_get_bustag(sc->res[i]); sc->bsh[i] = rman_get_bushandle(sc->res[i]); }; sc->dev = dev; gpio_sc = sc; for (i = 0; i < sc->nports; i++) { if ((bus_setup_intr(dev, sc->res[sc->nports + i], INTR_TYPE_BIO | INTR_MPSAFE, port_intr, NULL, sc, &sc->gpio_ih[i]))) { device_printf(dev, "ERROR: Unable to register interrupt handler\n"); - return (ENXIO); + goto fail; } - }; + } for (i = 0; i < sc->gpio_npins; i++) { sc->gpio_pins[i].gp_pin = i; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; if (get_bank(sc, i, &bank, &pin_shift) != 0) continue; pin_shift *= 4; reg = READ4(sc, bank.port, bank.con); if (reg & (PIN_OUT << pin_shift)) sc->gpio_pins[i].gp_flags = GPIO_PIN_OUTPUT; else sc->gpio_pins[i].gp_flags = GPIO_PIN_INPUT; /* TODO: add other pin statuses */ snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "pad%d.%d", device_get_unit(dev), i); } device_add_child(dev, "gpioc", -1); device_add_child(dev, "gpiobus", -1); return (bus_generic_attach(dev)); + +fail: + for (i = 0; i < sc->nports; i++) { + if (sc->gpio_ih[i]) + bus_teardown_intr(dev, sc->res[sc->nports + i], + sc->gpio_ih[i]); + } + bus_release_resources(dev, sc->pad_spec, sc->res); + mtx_destroy(&sc->sc_mtx); + + return (ENXIO); } static int pad_pin_max(device_t dev, int *maxpin) { struct pad_softc *sc; sc = device_get_softc(dev); *maxpin = sc->gpio_npins - 1; return (0); } static int pad_pin_getname(device_t dev, uint32_t pin, char *name) { struct pad_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); GPIO_UNLOCK(sc); return (0); } static int pad_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct pad_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *caps = sc->gpio_pins[i].gp_caps; GPIO_UNLOCK(sc); return (0); } static int pad_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct pad_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *flags = sc->gpio_pins[i].gp_flags; GPIO_UNLOCK(sc); return (0); } static int pad_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct gpio_bank bank; struct pad_softc *sc; int pin_shift; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); if (get_bank(sc, pin, &bank, &pin_shift) != 0) return (EINVAL); GPIO_LOCK(sc); if (READ4(sc, bank.port, bank.con + 0x4) & (1 << pin_shift)) *val = 1; else *val = 0; GPIO_UNLOCK(sc); return (0); } static int pad_pin_toggle(device_t dev, uint32_t pin) { struct gpio_bank bank; struct pad_softc *sc; int pin_shift; int reg; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); if (get_bank(sc, pin, &bank, &pin_shift) != 0) return (EINVAL); GPIO_LOCK(sc); reg = READ4(sc, bank.port, bank.con + 0x4); if (reg & (1 << pin_shift)) reg &= ~(1 << pin_shift); else reg |= (1 << pin_shift); WRITE4(sc, bank.port, bank.con + 0x4, reg); GPIO_UNLOCK(sc); return (0); } static void pad_pin_configure(struct pad_softc *sc, struct gpio_pin *pin, unsigned int flags) { struct gpio_bank bank; int pin_shift; int reg; GPIO_LOCK(sc); /* * Manage input/output */ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); if (get_bank(sc, pin->gp_pin, &bank, &pin_shift) != 0) return; pin_shift *= 4; #if 0 printf("bank is 0x%08x pin_shift %d\n", bank.con, pin_shift); #endif if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; reg = READ4(sc, bank.port, bank.con); reg &= ~(0xf << pin_shift); reg |= (PIN_OUT << pin_shift); WRITE4(sc, bank.port, bank.con, reg); } else { pin->gp_flags |= GPIO_PIN_INPUT; reg = READ4(sc, bank.port, bank.con); reg &= ~(0xf << pin_shift); WRITE4(sc, bank.port, bank.con, reg); } } GPIO_UNLOCK(sc); } static int pad_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct pad_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); pad_pin_configure(sc, &sc->gpio_pins[i], flags); return (0); } static int pad_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct pad_softc *sc; struct gpio_bank bank; int pin_shift; int reg; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); if (get_bank(sc, pin, &bank, &pin_shift) != 0) return (EINVAL); GPIO_LOCK(sc); reg = READ4(sc, bank.port, bank.con + 0x4); reg &= ~(PIN_OUT << pin_shift); if (value) reg |= (PIN_OUT << pin_shift); WRITE4(sc, bank.port, bank.con + 0x4, reg); GPIO_UNLOCK(sc); return (0); } static device_method_t pad_methods[] = { DEVMETHOD(device_probe, pad_probe), DEVMETHOD(device_attach, pad_attach), /* GPIO protocol */ DEVMETHOD(gpio_pin_max, pad_pin_max), DEVMETHOD(gpio_pin_getname, pad_pin_getname), DEVMETHOD(gpio_pin_getcaps, pad_pin_getcaps), DEVMETHOD(gpio_pin_getflags, pad_pin_getflags), DEVMETHOD(gpio_pin_get, pad_pin_get), DEVMETHOD(gpio_pin_toggle, pad_pin_toggle), DEVMETHOD(gpio_pin_setflags, pad_pin_setflags), DEVMETHOD(gpio_pin_set, pad_pin_set), { 0, 0 } }; static driver_t pad_driver = { "gpio", pad_methods, sizeof(struct pad_softc), }; static devclass_t pad_devclass; DRIVER_MODULE(pad, simplebus, pad_driver, pad_devclass, 0, 0); Index: head/sys/arm/ti/ti_gpio.c =================================================================== --- head/sys/arm/ti/ti_gpio.c (revision 277967) +++ head/sys/arm/ti/ti_gpio.c (revision 277968) @@ -1,1098 +1,1094 @@ /*- * Copyright (c) 2011 Ben Gray . * Copyright (c) 2014 Luiz Otavio O Souza . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /** * Beware that the OMAP4 datasheet(s) lists GPIO banks 1-6, whereas the code * here uses 0-5. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #include "ti_gpio_if.h" #if !defined(SOC_OMAP4) && !defined(SOC_TI_AM335X) #error "Unknown SoC" #endif /* Register definitions */ #define TI_GPIO_REVISION 0x0000 #define TI_GPIO_SYSCONFIG 0x0010 #define TI_GPIO_IRQSTATUS_RAW_0 0x0024 #define TI_GPIO_IRQSTATUS_RAW_1 0x0028 #define TI_GPIO_IRQSTATUS_0 0x002C #define TI_GPIO_IRQSTATUS_1 0x0030 #define TI_GPIO_IRQSTATUS_SET_0 0x0034 #define TI_GPIO_IRQSTATUS_SET_1 0x0038 #define TI_GPIO_IRQSTATUS_CLR_0 0x003C #define TI_GPIO_IRQSTATUS_CLR_1 0x0040 #define TI_GPIO_IRQWAKEN_0 0x0044 #define TI_GPIO_IRQWAKEN_1 0x0048 #define TI_GPIO_SYSSTATUS 0x0114 #define TI_GPIO_IRQSTATUS1 0x0118 #define TI_GPIO_IRQENABLE1 0x011C #define TI_GPIO_WAKEUPENABLE 0x0120 #define TI_GPIO_IRQSTATUS2 0x0128 #define TI_GPIO_IRQENABLE2 0x012C #define TI_GPIO_CTRL 0x0130 #define TI_GPIO_OE 0x0134 #define TI_GPIO_DATAIN 0x0138 #define TI_GPIO_DATAOUT 0x013C #define TI_GPIO_LEVELDETECT0 0x0140 #define TI_GPIO_LEVELDETECT1 0x0144 #define TI_GPIO_RISINGDETECT 0x0148 #define TI_GPIO_FALLINGDETECT 0x014C #define TI_GPIO_DEBOUNCENABLE 0x0150 #define TI_GPIO_DEBOUNCINGTIME 0x0154 #define TI_GPIO_CLEARWKUPENA 0x0180 #define TI_GPIO_SETWKUENA 0x0184 #define TI_GPIO_CLEARDATAOUT 0x0190 #define TI_GPIO_SETDATAOUT 0x0194 /* Other SoC Specific definitions */ #define OMAP4_MAX_GPIO_BANKS 6 #define OMAP4_FIRST_GPIO_BANK 1 #define OMAP4_INTR_PER_BANK 1 #define OMAP4_GPIO_REV 0x50600801 #define AM335X_MAX_GPIO_BANKS 4 #define AM335X_FIRST_GPIO_BANK 0 #define AM335X_INTR_PER_BANK 2 #define AM335X_GPIO_REV 0x50600801 #define PINS_PER_BANK 32 #define TI_GPIO_BANK(p) ((p) / PINS_PER_BANK) #define TI_GPIO_MASK(p) (1U << ((p) % PINS_PER_BANK)) static struct ti_gpio_softc *ti_gpio_sc = NULL; +static int ti_gpio_detach(device_t); static u_int ti_max_gpio_banks(void) { switch(ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: return (OMAP4_MAX_GPIO_BANKS); #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: return (AM335X_MAX_GPIO_BANKS); #endif } return (0); } static u_int ti_max_gpio_intrs(void) { switch(ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: return (OMAP4_MAX_GPIO_BANKS * OMAP4_INTR_PER_BANK); #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: return (AM335X_MAX_GPIO_BANKS * AM335X_INTR_PER_BANK); #endif } return (0); } static u_int ti_first_gpio_bank(void) { switch(ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: return (OMAP4_FIRST_GPIO_BANK); #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: return (AM335X_FIRST_GPIO_BANK); #endif } return (0); } static uint32_t ti_gpio_rev(void) { switch(ti_chip()) { #ifdef SOC_OMAP4 case CHIP_OMAP_4: return (OMAP4_GPIO_REV); #endif #ifdef SOC_TI_AM335X case CHIP_AM335X: return (AM335X_GPIO_REV); #endif } return (0); } /** * ti_gpio_mem_spec - Resource specification used when allocating resources * ti_gpio_irq_spec - Resource specification used when allocating resources * * This driver module can have up to six independent memory regions, each * region typically controls 32 GPIO pins. * * On OMAP3 and OMAP4 there is only one physical interrupt line per bank, * but there are two set of registers which control the interrupt delivery * to internal subsystems. The first set of registers control the * interrupts delivery to the MPU and the second set control the * interrupts delivery to the DSP. * * On AM335x there are two physical interrupt lines for each GPIO module. * Each interrupt line is controlled by a set of registers. */ static struct resource_spec ti_gpio_mem_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_MEMORY, 1, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_MEMORY, 2, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_MEMORY, 3, RF_ACTIVE | RF_OPTIONAL }, #if !defined(SOC_TI_AM335X) { SYS_RES_MEMORY, 4, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_MEMORY, 5, RF_ACTIVE | RF_OPTIONAL }, #endif { -1, 0, 0 } }; static struct resource_spec ti_gpio_irq_spec[] = { { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_IRQ, 2, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_IRQ, 3, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_IRQ, 4, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_IRQ, 5, RF_ACTIVE | RF_OPTIONAL }, #if defined(SOC_TI_AM335X) { SYS_RES_IRQ, 6, RF_ACTIVE | RF_OPTIONAL }, { SYS_RES_IRQ, 7, RF_ACTIVE | RF_OPTIONAL }, #endif { -1, 0, 0 } }; /** * Macros for driver mutex locking */ #define TI_GPIO_LOCK(_sc) mtx_lock_spin(&(_sc)->sc_mtx) #define TI_GPIO_UNLOCK(_sc) mtx_unlock_spin(&(_sc)->sc_mtx) #define TI_GPIO_LOCK_INIT(_sc) \ mtx_init(&_sc->sc_mtx, device_get_nameunit((_sc)->sc_dev), \ "ti_gpio", MTX_SPIN) #define TI_GPIO_LOCK_DESTROY(_sc) mtx_destroy(&(_sc)->sc_mtx) #define TI_GPIO_ASSERT_LOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_OWNED) #define TI_GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&(_sc)->sc_mtx, MA_NOTOWNED) /** * ti_gpio_read_4 - reads a 32-bit value from one of the GPIO registers * @sc: GPIO device context * @bank: The bank to read from * @off: The offset of a register from the GPIO register address range * * * RETURNS: * 32-bit value read from the register. */ static inline uint32_t ti_gpio_read_4(struct ti_gpio_softc *sc, unsigned int bank, bus_size_t off) { return (bus_read_4(sc->sc_mem_res[bank], off)); } /** * ti_gpio_write_4 - writes a 32-bit value to one of the GPIO registers * @sc: GPIO device context * @bank: The bank to write to * @off: The offset of a register from the GPIO register address range * @val: The value to write into the register * * RETURNS: * nothing */ static inline void ti_gpio_write_4(struct ti_gpio_softc *sc, unsigned int bank, bus_size_t off, uint32_t val) { bus_write_4(sc->sc_mem_res[bank], off, val); } static inline void ti_gpio_intr_clr(struct ti_gpio_softc *sc, unsigned int bank, uint32_t mask) { /* We clear both set of registers. */ ti_gpio_write_4(sc, bank, TI_GPIO_IRQSTATUS_CLR_0, mask); ti_gpio_write_4(sc, bank, TI_GPIO_IRQSTATUS_CLR_1, mask); } static inline void ti_gpio_intr_set(struct ti_gpio_softc *sc, unsigned int bank, uint32_t mask) { /* * On OMAP4 we unmask only the MPU interrupt and on AM335x we * also activate only the first interrupt. */ ti_gpio_write_4(sc, bank, TI_GPIO_IRQSTATUS_SET_0, mask); } static inline void ti_gpio_intr_ack(struct ti_gpio_softc *sc, unsigned int bank, uint32_t mask) { /* * Acknowledge the interrupt on both registers even if we use only * the first one. */ ti_gpio_write_4(sc, bank, TI_GPIO_IRQSTATUS_0, mask); ti_gpio_write_4(sc, bank, TI_GPIO_IRQSTATUS_1, mask); } static inline uint32_t ti_gpio_intr_status(struct ti_gpio_softc *sc, unsigned int bank) { uint32_t reg; /* Get the status from both registers. */ reg = ti_gpio_read_4(sc, bank, TI_GPIO_IRQSTATUS_0); reg |= ti_gpio_read_4(sc, bank, TI_GPIO_IRQSTATUS_1); return (reg); } /** * ti_gpio_pin_max - Returns the maximum number of GPIO pins * @dev: gpio device handle * @maxpin: pointer to a value that upon return will contain the maximum number * of pins in the device. * * * LOCKING: * No locking required, returns static data. * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = ti_max_gpio_banks() * PINS_PER_BANK - 1; return (0); } static int ti_gpio_valid_pin(struct ti_gpio_softc *sc, int pin) { if (pin >= sc->sc_maxpin || TI_GPIO_BANK(pin) >= ti_max_gpio_banks() || sc->sc_mem_res[TI_GPIO_BANK(pin)] == NULL) { return (EINVAL); } return (0); } /** * ti_gpio_pin_getcaps - Gets the capabilties of a given pin * @dev: gpio device handle * @pin: the number of the pin * @caps: pointer to a value that upon return will contain the capabilities * * Currently all pins have the same capability, notably: * - GPIO_PIN_INPUT * - GPIO_PIN_OUTPUT * - GPIO_PIN_PULLUP * - GPIO_PIN_PULLDOWN * * LOCKING: * No locking required, returns static data. * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct ti_gpio_softc *sc; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); *caps = (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN); return (0); } /** * ti_gpio_pin_getflags - Gets the current flags of a given pin * @dev: gpio device handle * @pin: the number of the pin * @flags: upon return will contain the current flags of the pin * * Reads the current flags of a given pin, here we actually read the H/W * registers to determine the flags, rather than storing the value in the * setflags call. * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct ti_gpio_softc *sc; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* Get the current pin state */ TI_GPIO_LOCK(sc); TI_GPIO_GET_FLAGS(dev, pin, flags); TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_getname - Gets the name of a given pin * @dev: gpio device handle * @pin: the number of the pin * @name: buffer to put the name in * * The driver simply calls the pins gpio_n, where 'n' is obviously the number * of the pin. * * LOCKING: * No locking required, returns static data. * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct ti_gpio_softc *sc; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* Set a very simple name */ snprintf(name, GPIOMAXNAME, "gpio_%u", pin); name[GPIOMAXNAME - 1] = '\0'; return (0); } /** * ti_gpio_pin_setflags - Sets the flags for a given pin * @dev: gpio device handle * @pin: the number of the pin * @flags: the flags to set * * The flags of the pin correspond to things like input/output mode, pull-ups, * pull-downs, etc. This driver doesn't support all flags, only the following: * - GPIO_PIN_INPUT * - GPIO_PIN_OUTPUT * - GPIO_PIN_PULLUP * - GPIO_PIN_PULLDOWN * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise an error code */ static int ti_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct ti_gpio_softc *sc; uint32_t oe; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* Set the GPIO mode and state */ TI_GPIO_LOCK(sc); if (TI_GPIO_SET_FLAGS(dev, pin, flags) != 0) { TI_GPIO_UNLOCK(sc); return (EINVAL); } /* If configuring as an output set the "output enable" bit */ oe = ti_gpio_read_4(sc, TI_GPIO_BANK(pin), TI_GPIO_OE); if (flags & GPIO_PIN_INPUT) oe |= TI_GPIO_MASK(pin); else oe &= ~TI_GPIO_MASK(pin); ti_gpio_write_4(sc, TI_GPIO_BANK(pin), TI_GPIO_OE, oe); TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_set - Sets the current level on a GPIO pin * @dev: gpio device handle * @pin: the number of the pin * @value: non-zero value will drive the pin high, otherwise the pin is * driven low. * * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise a error code */ static int ti_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct ti_gpio_softc *sc; uint32_t reg; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); TI_GPIO_LOCK(sc); if (value == GPIO_PIN_LOW) reg = TI_GPIO_CLEARDATAOUT; else reg = TI_GPIO_SETDATAOUT; ti_gpio_write_4(sc, TI_GPIO_BANK(pin), reg, TI_GPIO_MASK(pin)); TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_get - Gets the current level on a GPIO pin * @dev: gpio device handle * @pin: the number of the pin * @value: pointer to a value that upond return will contain the pin value * * The pin must be configured as an input pin beforehand, otherwise this * function will fail. * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise a error code */ static int ti_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *value) { struct ti_gpio_softc *sc; uint32_t oe, reg, val; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* * Return data from output latch when set as output and from the * input register otherwise. */ TI_GPIO_LOCK(sc); oe = ti_gpio_read_4(sc, TI_GPIO_BANK(pin), TI_GPIO_OE); if (oe & TI_GPIO_MASK(pin)) reg = TI_GPIO_DATAIN; else reg = TI_GPIO_DATAOUT; val = ti_gpio_read_4(sc, TI_GPIO_BANK(pin), reg); *value = (val & TI_GPIO_MASK(pin)) ? 1 : 0; TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_pin_toggle - Toggles a given GPIO pin * @dev: gpio device handle * @pin: the number of the pin * * * LOCKING: * Internally locks the context * * RETURNS: * Returns 0 on success otherwise a error code */ static int ti_gpio_pin_toggle(device_t dev, uint32_t pin) { struct ti_gpio_softc *sc; uint32_t reg, val; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, pin) != 0) return (EINVAL); /* Toggle the pin */ TI_GPIO_LOCK(sc); val = ti_gpio_read_4(sc, TI_GPIO_BANK(pin), TI_GPIO_DATAOUT); if (val & TI_GPIO_MASK(pin)) reg = TI_GPIO_CLEARDATAOUT; else reg = TI_GPIO_SETDATAOUT; ti_gpio_write_4(sc, TI_GPIO_BANK(pin), reg, TI_GPIO_MASK(pin)); TI_GPIO_UNLOCK(sc); return (0); } /** * ti_gpio_intr - ISR for all GPIO modules * @arg: the soft context pointer * * LOCKING: * Internally locks the context * */ static int ti_gpio_intr(void *arg) { int bank_last, irq; struct intr_event *event; struct ti_gpio_softc *sc; uint32_t reg; sc = (struct ti_gpio_softc *)arg; bank_last = -1; reg = 0; /* squelch bogus gcc warning */ for (irq = 0; irq < sc->sc_maxpin; irq++) { /* Read interrupt status only once for each bank. */ if (TI_GPIO_BANK(irq) != bank_last) { reg = ti_gpio_intr_status(sc, TI_GPIO_BANK(irq)); bank_last = TI_GPIO_BANK(irq); } if ((reg & TI_GPIO_MASK(irq)) == 0) continue; event = sc->sc_events[irq]; if (event != NULL && !TAILQ_EMPTY(&event->ie_handlers)) intr_event_handle(event, NULL); else device_printf(sc->sc_dev, "Stray IRQ %d\n", irq); /* Ack the IRQ Status bit. */ ti_gpio_intr_ack(sc, TI_GPIO_BANK(irq), TI_GPIO_MASK(irq)); } return (FILTER_HANDLED); } static int ti_gpio_attach_intr(device_t dev) { int i; struct ti_gpio_softc *sc; sc = device_get_softc(dev); for (i = 0; i < ti_max_gpio_intrs(); i++) { if (sc->sc_irq_res[i] == NULL) break; /* * Register our interrupt filter for each of the IRQ resources. */ if (bus_setup_intr(dev, sc->sc_irq_res[i], INTR_TYPE_MISC | INTR_MPSAFE, ti_gpio_intr, NULL, sc, &sc->sc_irq_hdl[i]) != 0) { device_printf(dev, "WARNING: unable to register interrupt filter\n"); return (-1); } } return (0); } static int ti_gpio_detach_intr(device_t dev) { int i; struct ti_gpio_softc *sc; /* Teardown our interrupt filters. */ sc = device_get_softc(dev); for (i = 0; i < ti_max_gpio_intrs(); i++) { if (sc->sc_irq_res[i] == NULL) break; if (sc->sc_irq_hdl[i]) { bus_teardown_intr(dev, sc->sc_irq_res[i], sc->sc_irq_hdl[i]); } } return (0); } static int ti_gpio_bank_init(device_t dev, int bank) { int pin; struct ti_gpio_softc *sc; uint32_t flags, reg_oe, rev; sc = device_get_softc(dev); /* Enable the interface and functional clocks for the module. */ ti_prcm_clk_enable(GPIO0_CLK + ti_first_gpio_bank() + bank); /* * Read the revision number of the module. TI don't publish the * actual revision numbers, so instead the values have been * determined by experimentation. */ rev = ti_gpio_read_4(sc, bank, TI_GPIO_REVISION); /* Check the revision. */ if (rev != ti_gpio_rev()) { device_printf(dev, "Warning: could not determine the revision " "of GPIO module %d (revision:0x%08x)\n", bank, rev); return (EINVAL); } /* Disable interrupts for all pins. */ ti_gpio_intr_clr(sc, bank, 0xffffffff); /* Init OE register based on pads configuration. */ reg_oe = 0xffffffff; for (pin = 0; pin < PINS_PER_BANK; pin++) { TI_GPIO_GET_FLAGS(dev, PINS_PER_BANK * bank + pin, &flags); if (flags & GPIO_PIN_OUTPUT) reg_oe &= ~(1UL << pin); } ti_gpio_write_4(sc, bank, TI_GPIO_OE, reg_oe); return (0); } /** * ti_gpio_attach - attach function for the driver * @dev: gpio device handle * * Allocates and sets up the driver context for all GPIO banks. This function * expects the memory ranges and IRQs to already be allocated to the driver. * * LOCKING: * None * * RETURNS: * Always returns 0 */ static int ti_gpio_attach(device_t dev) { struct ti_gpio_softc *sc; unsigned int i; int err; if (ti_gpio_sc != NULL) return (ENXIO); ti_gpio_sc = sc = device_get_softc(dev); sc->sc_dev = dev; TI_GPIO_LOCK_INIT(sc); ti_gpio_pin_max(dev, &sc->sc_maxpin); sc->sc_maxpin++; /* There are up to 6 different GPIO register sets located in different * memory areas on the chip. The memory range should have been set for * the driver when it was added as a child. */ if (bus_alloc_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res) != 0) { device_printf(dev, "Error: could not allocate mem resources\n"); + ti_gpio_detach(dev); return (ENXIO); } /* Request the IRQ resources */ if (bus_alloc_resources(dev, ti_gpio_irq_spec, sc->sc_irq_res) != 0) { - bus_release_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res); device_printf(dev, "Error: could not allocate irq resources\n"); + ti_gpio_detach(dev); return (ENXIO); } /* Setup the IRQ resources */ if (ti_gpio_attach_intr(dev) != 0) { - ti_gpio_detach_intr(dev); - bus_release_resources(dev, ti_gpio_irq_spec, sc->sc_irq_res); - bus_release_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res); + device_printf(dev, "Error: could not setup irq handlers\n"); + ti_gpio_detach(dev); return (ENXIO); } /* * Initialize the interrupt settings. The default is active-low * interrupts. */ sc->sc_irq_trigger = malloc( sizeof(*sc->sc_irq_trigger) * sc->sc_maxpin, M_DEVBUF, M_WAITOK | M_ZERO); sc->sc_irq_polarity = malloc( sizeof(*sc->sc_irq_polarity) * sc->sc_maxpin, M_DEVBUF, M_WAITOK | M_ZERO); for (i = 0; i < sc->sc_maxpin; i++) { sc->sc_irq_trigger[i] = INTR_TRIGGER_LEVEL; sc->sc_irq_polarity[i] = INTR_POLARITY_LOW; } sc->sc_events = malloc(sizeof(struct intr_event *) * sc->sc_maxpin, M_DEVBUF, M_WAITOK | M_ZERO); /* We need to go through each block and ensure the clocks are running and * the module is enabled. It might be better to do this only when the * pins are configured which would result in less power used if the GPIO * pins weren't used ... */ for (i = 0; i < ti_max_gpio_banks(); i++) { if (sc->sc_mem_res[i] != NULL) { /* Initialize the GPIO module. */ err = ti_gpio_bank_init(dev, i); if (err != 0) { - ti_gpio_detach_intr(dev); - bus_release_resources(dev, ti_gpio_irq_spec, - sc->sc_irq_res); - bus_release_resources(dev, ti_gpio_mem_spec, - sc->sc_mem_res); + ti_gpio_detach(dev); return (err); } } } /* Finish of the probe call */ device_add_child(dev, "gpioc", -1); device_add_child(dev, "gpiobus", -1); return (bus_generic_attach(dev)); } /** * ti_gpio_detach - detach function for the driver * @dev: scm device handle * * Allocates and sets up the driver context, this simply entails creating a * bus mappings for the SCM register set. * * LOCKING: * None * * RETURNS: * Always returns 0 */ static int ti_gpio_detach(device_t dev) { struct ti_gpio_softc *sc = device_get_softc(dev); unsigned int i; KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); /* Disable all interrupts */ for (i = 0; i < ti_max_gpio_banks(); i++) { if (sc->sc_mem_res[i] != NULL) ti_gpio_intr_clr(sc, i, 0xffffffff); } - bus_generic_detach(dev); - - free(sc->sc_events, M_DEVBUF); - free(sc->sc_irq_polarity, M_DEVBUF); - free(sc->sc_irq_trigger, M_DEVBUF); - + if (sc->sc_events) + free(sc->sc_events, M_DEVBUF); + if (sc->sc_irq_polarity) + free(sc->sc_irq_polarity, M_DEVBUF); + if (sc->sc_irq_trigger) + free(sc->sc_irq_trigger, M_DEVBUF); /* Release the memory and IRQ resources. */ ti_gpio_detach_intr(dev); bus_release_resources(dev, ti_gpio_irq_spec, sc->sc_irq_res); bus_release_resources(dev, ti_gpio_mem_spec, sc->sc_mem_res); - TI_GPIO_LOCK_DESTROY(sc); return (0); } static uint32_t ti_gpio_intr_reg(struct ti_gpio_softc *sc, int irq) { if (ti_gpio_valid_pin(sc, irq) != 0) return (0); if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_LEVEL) { if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW) return (TI_GPIO_LEVELDETECT0); else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH) return (TI_GPIO_LEVELDETECT1); } else if (sc->sc_irq_trigger[irq] == INTR_TRIGGER_EDGE) { if (sc->sc_irq_polarity[irq] == INTR_POLARITY_LOW) return (TI_GPIO_FALLINGDETECT); else if (sc->sc_irq_polarity[irq] == INTR_POLARITY_HIGH) return (TI_GPIO_RISINGDETECT); } return (0); } static void ti_gpio_mask_irq(void *source) { int irq; uint32_t reg, val; irq = (int)source; if (ti_gpio_valid_pin(ti_gpio_sc, irq) != 0) return; TI_GPIO_LOCK(ti_gpio_sc); ti_gpio_intr_clr(ti_gpio_sc, TI_GPIO_BANK(irq), TI_GPIO_MASK(irq)); reg = ti_gpio_intr_reg(ti_gpio_sc, irq); if (reg != 0) { val = ti_gpio_read_4(ti_gpio_sc, TI_GPIO_BANK(irq), reg); val &= ~TI_GPIO_MASK(irq); ti_gpio_write_4(ti_gpio_sc, TI_GPIO_BANK(irq), reg, val); } TI_GPIO_UNLOCK(ti_gpio_sc); } static void ti_gpio_unmask_irq(void *source) { int irq; uint32_t reg, val; irq = (int)source; if (ti_gpio_valid_pin(ti_gpio_sc, irq) != 0) return; TI_GPIO_LOCK(ti_gpio_sc); reg = ti_gpio_intr_reg(ti_gpio_sc, irq); if (reg != 0) { val = ti_gpio_read_4(ti_gpio_sc, TI_GPIO_BANK(irq), reg); val |= TI_GPIO_MASK(irq); ti_gpio_write_4(ti_gpio_sc, TI_GPIO_BANK(irq), reg, val); ti_gpio_intr_set(ti_gpio_sc, TI_GPIO_BANK(irq), TI_GPIO_MASK(irq)); } TI_GPIO_UNLOCK(ti_gpio_sc); } static int ti_gpio_activate_resource(device_t dev, device_t child, int type, int rid, struct resource *res) { int pin; if (type != SYS_RES_IRQ) return (ENXIO); /* Unmask the interrupt. */ pin = rman_get_start(res); ti_gpio_unmask_irq((void *)(uintptr_t)pin); return (0); } static int ti_gpio_deactivate_resource(device_t dev, device_t child, int type, int rid, struct resource *res) { int pin; if (type != SYS_RES_IRQ) return (ENXIO); /* Mask the interrupt. */ pin = rman_get_start(res); ti_gpio_mask_irq((void *)(uintptr_t)pin); return (0); } static int ti_gpio_config_intr(device_t dev, int irq, enum intr_trigger trig, enum intr_polarity pol) { struct ti_gpio_softc *sc; uint32_t oldreg, reg, val; sc = device_get_softc(dev); if (ti_gpio_valid_pin(sc, irq) != 0) return (EINVAL); /* There is no standard trigger or polarity. */ if (trig == INTR_TRIGGER_CONFORM || pol == INTR_POLARITY_CONFORM) return (EINVAL); TI_GPIO_LOCK(sc); /* * TRM recommends add the new event before remove the old one to * avoid losing interrupts. */ oldreg = ti_gpio_intr_reg(sc, irq); sc->sc_irq_trigger[irq] = trig; sc->sc_irq_polarity[irq] = pol; reg = ti_gpio_intr_reg(sc, irq); if (reg != 0) { /* Apply the new settings. */ val = ti_gpio_read_4(sc, TI_GPIO_BANK(irq), reg); val |= TI_GPIO_MASK(irq); ti_gpio_write_4(sc, TI_GPIO_BANK(irq), reg, val); } if (oldreg != 0) { /* Remove the old settings. */ val = ti_gpio_read_4(sc, TI_GPIO_BANK(irq), oldreg); val &= ~TI_GPIO_MASK(irq); ti_gpio_write_4(sc, TI_GPIO_BANK(irq), oldreg, val); } TI_GPIO_UNLOCK(sc); return (0); } static int ti_gpio_setup_intr(device_t dev, device_t child, struct resource *ires, int flags, driver_filter_t *filt, driver_intr_t *handler, void *arg, void **cookiep) { struct ti_gpio_softc *sc; struct intr_event *event; int pin, error; sc = device_get_softc(dev); pin = rman_get_start(ires); if (ti_gpio_valid_pin(sc, pin) != 0) panic("%s: bad pin %d", __func__, pin); event = sc->sc_events[pin]; if (event == NULL) { error = intr_event_create(&event, (void *)(uintptr_t)pin, 0, pin, ti_gpio_mask_irq, ti_gpio_unmask_irq, NULL, NULL, "gpio%d pin%d:", device_get_unit(dev), pin); if (error != 0) return (error); sc->sc_events[pin] = event; } intr_event_add_handler(event, device_get_nameunit(child), filt, handler, arg, intr_priority(flags), flags, cookiep); return (0); } static int ti_gpio_teardown_intr(device_t dev, device_t child, struct resource *ires, void *cookie) { struct ti_gpio_softc *sc; int pin, err; sc = device_get_softc(dev); pin = rman_get_start(ires); if (ti_gpio_valid_pin(sc, pin) != 0) panic("%s: bad pin %d", __func__, pin); if (sc->sc_events[pin] == NULL) panic("Trying to teardown unoccupied IRQ"); err = intr_event_remove_handler(cookie); if (!err) sc->sc_events[pin] = NULL; return (err); } static phandle_t ti_gpio_get_node(device_t bus, device_t dev) { /* We only have one child, the GPIO bus, which needs our own node. */ return (ofw_bus_get_node(bus)); } static device_method_t ti_gpio_methods[] = { DEVMETHOD(device_attach, ti_gpio_attach), DEVMETHOD(device_detach, ti_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_pin_max, ti_gpio_pin_max), DEVMETHOD(gpio_pin_getname, ti_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, ti_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, ti_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, ti_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, ti_gpio_pin_get), DEVMETHOD(gpio_pin_set, ti_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, ti_gpio_pin_toggle), /* Bus interface */ DEVMETHOD(bus_activate_resource, ti_gpio_activate_resource), DEVMETHOD(bus_deactivate_resource, ti_gpio_deactivate_resource), DEVMETHOD(bus_config_intr, ti_gpio_config_intr), DEVMETHOD(bus_setup_intr, ti_gpio_setup_intr), DEVMETHOD(bus_teardown_intr, ti_gpio_teardown_intr), /* ofw_bus interface */ DEVMETHOD(ofw_bus_get_node, ti_gpio_get_node), {0, 0}, }; driver_t ti_gpio_driver = { "gpio", ti_gpio_methods, sizeof(struct ti_gpio_softc), }; Index: head/sys/mips/atheros/ar71xx_gpio.c =================================================================== --- head/sys/mips/atheros/ar71xx_gpio.c (revision 277967) +++ head/sys/mips/atheros/ar71xx_gpio.c (revision 277968) @@ -1,485 +1,489 @@ /*- * Copyright (c) 2009, Oleksandr Tymoshenko * Copyright (c) 2009, Luiz Otavio O Souza. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * GPIO driver for AR71xx */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) /* * Helpers */ static void ar71xx_gpio_function_enable(struct ar71xx_gpio_softc *sc, uint32_t mask); static void ar71xx_gpio_function_disable(struct ar71xx_gpio_softc *sc, uint32_t mask); static void ar71xx_gpio_pin_configure(struct ar71xx_gpio_softc *sc, struct gpio_pin *pin, uint32_t flags); /* * Driver stuff */ static int ar71xx_gpio_probe(device_t dev); static int ar71xx_gpio_attach(device_t dev); static int ar71xx_gpio_detach(device_t dev); static int ar71xx_gpio_filter(void *arg); static void ar71xx_gpio_intr(void *arg); /* * GPIO interface */ static int ar71xx_gpio_pin_max(device_t dev, int *maxpin); static int ar71xx_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); static int ar71xx_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags); static int ar71xx_gpio_pin_getname(device_t dev, uint32_t pin, char *name); static int ar71xx_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); static int ar71xx_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); static int ar71xx_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); static int ar71xx_gpio_pin_toggle(device_t dev, uint32_t pin); static void ar71xx_gpio_function_enable(struct ar71xx_gpio_softc *sc, uint32_t mask) { if (ar71xx_soc == AR71XX_SOC_AR9341 || ar71xx_soc == AR71XX_SOC_AR9342 || ar71xx_soc == AR71XX_SOC_AR9344) GPIO_SET_BITS(sc, AR934X_GPIO_REG_FUNC, mask); else GPIO_SET_BITS(sc, AR71XX_GPIO_FUNCTION, mask); } static void ar71xx_gpio_function_disable(struct ar71xx_gpio_softc *sc, uint32_t mask) { if (ar71xx_soc == AR71XX_SOC_AR9341 || ar71xx_soc == AR71XX_SOC_AR9342 || ar71xx_soc == AR71XX_SOC_AR9344) GPIO_CLEAR_BITS(sc, AR934X_GPIO_REG_FUNC, mask); else GPIO_CLEAR_BITS(sc, AR71XX_GPIO_FUNCTION, mask); } static void ar71xx_gpio_pin_configure(struct ar71xx_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { uint32_t mask; mask = 1 << pin->gp_pin; /* * Manage input/output */ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; GPIO_SET_BITS(sc, AR71XX_GPIO_OE, mask); } else { pin->gp_flags |= GPIO_PIN_INPUT; GPIO_CLEAR_BITS(sc, AR71XX_GPIO_OE, mask); } } } static int ar71xx_gpio_pin_max(device_t dev, int *maxpin) { switch (ar71xx_soc) { case AR71XX_SOC_AR9130: case AR71XX_SOC_AR9132: *maxpin = AR91XX_GPIO_PINS - 1; break; case AR71XX_SOC_AR7240: case AR71XX_SOC_AR7241: case AR71XX_SOC_AR7242: *maxpin = AR724X_GPIO_PINS - 1; break; case AR71XX_SOC_AR9330: case AR71XX_SOC_AR9331: *maxpin = AR933X_GPIO_COUNT - 1; break; case AR71XX_SOC_AR9341: case AR71XX_SOC_AR9342: case AR71XX_SOC_AR9344: *maxpin = AR934X_GPIO_COUNT - 1; break; default: *maxpin = AR71XX_GPIO_PINS - 1; } return (0); } static int ar71xx_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct ar71xx_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *caps = sc->gpio_pins[i].gp_caps; GPIO_UNLOCK(sc); return (0); } static int ar71xx_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct ar71xx_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *flags = sc->gpio_pins[i].gp_flags; GPIO_UNLOCK(sc); return (0); } static int ar71xx_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct ar71xx_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); GPIO_UNLOCK(sc); return (0); } static int ar71xx_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { int i; struct ar71xx_gpio_softc *sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); ar71xx_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); return (0); } static int ar71xx_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct ar71xx_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); if (value) GPIO_WRITE(sc, AR71XX_GPIO_SET, (1 << pin)); else GPIO_WRITE(sc, AR71XX_GPIO_CLEAR, (1 << pin)); return (0); } static int ar71xx_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct ar71xx_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); *val = (GPIO_READ(sc, AR71XX_GPIO_IN) & (1 << pin)) ? 1 : 0; return (0); } static int ar71xx_gpio_pin_toggle(device_t dev, uint32_t pin) { int res, i; struct ar71xx_gpio_softc *sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); res = (GPIO_READ(sc, AR71XX_GPIO_IN) & (1 << pin)) ? 1 : 0; if (res) GPIO_WRITE(sc, AR71XX_GPIO_CLEAR, (1 << pin)); else GPIO_WRITE(sc, AR71XX_GPIO_SET, (1 << pin)); return (0); } static int ar71xx_gpio_filter(void *arg) { /* TODO: something useful */ return (FILTER_STRAY); } static void ar71xx_gpio_intr(void *arg) { struct ar71xx_gpio_softc *sc = arg; GPIO_LOCK(sc); /* TODO: something useful */ GPIO_UNLOCK(sc); } static int ar71xx_gpio_probe(device_t dev) { device_set_desc(dev, "Atheros AR71XX GPIO driver"); return (0); } static int ar71xx_gpio_attach(device_t dev) { struct ar71xx_gpio_softc *sc = device_get_softc(dev); - int error = 0; int i, j, maxpin; int mask, pinon; uint32_t oe; KASSERT((device_get_unit(dev) == 0), ("ar71xx_gpio: Only one gpio module supported")); mtx_init(&sc->gpio_mtx, device_get_nameunit(dev), NULL, MTX_DEF); /* Map control/status registers. */ sc->gpio_mem_rid = 0; sc->gpio_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->gpio_mem_rid, RF_ACTIVE); if (sc->gpio_mem_res == NULL) { device_printf(dev, "couldn't map memory\n"); - error = ENXIO; ar71xx_gpio_detach(dev); - return(error); + return (ENXIO); } if ((sc->gpio_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->gpio_irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { device_printf(dev, "unable to allocate IRQ resource\n"); + ar71xx_gpio_detach(dev); return (ENXIO); } if ((bus_setup_intr(dev, sc->gpio_irq_res, INTR_TYPE_MISC, ar71xx_gpio_filter, ar71xx_gpio_intr, sc, &sc->gpio_ih))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); + ar71xx_gpio_detach(dev); return (ENXIO); } sc->dev = dev; /* Enable function bits that are required */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "function_set", &mask) == 0) { device_printf(dev, "function_set: 0x%x\n", mask); ar71xx_gpio_function_enable(sc, mask); } /* Disable function bits that are required */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), "function_clear", &mask) == 0) { device_printf(dev, "function_clear: 0x%x\n", mask); ar71xx_gpio_function_disable(sc, mask); } /* Disable interrupts for all pins. */ GPIO_WRITE(sc, AR71XX_GPIO_INT_MASK, 0); /* Initialise all pins specified in the mask, up to the pin count */ (void) ar71xx_gpio_pin_max(dev, &maxpin); if (resource_int_value(device_get_name(dev), device_get_unit(dev), "pinmask", &mask) != 0) mask = 0; if (resource_int_value(device_get_name(dev), device_get_unit(dev), "pinon", &pinon) != 0) pinon = 0; device_printf(dev, "gpio pinmask=0x%x\n", mask); for (j = 0; j <= maxpin; j++) { if ((mask & (1 << j)) == 0) continue; sc->gpio_npins++; } /* Iniatilize the GPIO pins, keep the loader settings. */ oe = GPIO_READ(sc, AR71XX_GPIO_OE); sc->gpio_pins = malloc(sizeof(*sc->gpio_pins) * sc->gpio_npins, M_DEVBUF, M_WAITOK | M_ZERO); for (i = 0, j = 0; j <= maxpin; j++) { if ((mask & (1 << j)) == 0) continue; snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "pin %d", j); sc->gpio_pins[i].gp_pin = j; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; if (oe & (1 << j)) sc->gpio_pins[i].gp_flags = GPIO_PIN_OUTPUT; else sc->gpio_pins[i].gp_flags = GPIO_PIN_INPUT; i++; } /* Turn on the hinted pins. */ for (i = 0; i < sc->gpio_npins; i++) { j = sc->gpio_pins[i].gp_pin; if ((pinon & (1 << j)) != 0) { ar71xx_gpio_pin_setflags(dev, j, GPIO_PIN_OUTPUT); ar71xx_gpio_pin_set(dev, j, 1); } } device_add_child(dev, "gpioc", -1); device_add_child(dev, "gpiobus", -1); return (bus_generic_attach(dev)); } static int ar71xx_gpio_detach(device_t dev) { struct ar71xx_gpio_softc *sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->gpio_mtx), ("gpio mutex not initialized")); bus_generic_detach(dev); - + if (sc->gpio_ih) + bus_teardown_intr(dev, sc->gpio_irq_res, sc->gpio_ih); + if (sc->gpio_irq_res) + bus_release_resource(dev, SYS_RES_IRQ, sc->gpio_irq_rid, + sc->gpio_irq_res); if (sc->gpio_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, sc->gpio_mem_rid, sc->gpio_mem_res); - - free(sc->gpio_pins, M_DEVBUF); + if (sc->gpio_pins) + free(sc->gpio_pins, M_DEVBUF); mtx_destroy(&sc->gpio_mtx); return(0); } static device_method_t ar71xx_gpio_methods[] = { DEVMETHOD(device_probe, ar71xx_gpio_probe), DEVMETHOD(device_attach, ar71xx_gpio_attach), DEVMETHOD(device_detach, ar71xx_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_pin_max, ar71xx_gpio_pin_max), DEVMETHOD(gpio_pin_getname, ar71xx_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, ar71xx_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, ar71xx_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, ar71xx_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, ar71xx_gpio_pin_get), DEVMETHOD(gpio_pin_set, ar71xx_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, ar71xx_gpio_pin_toggle), {0, 0}, }; static driver_t ar71xx_gpio_driver = { "gpio", ar71xx_gpio_methods, sizeof(struct ar71xx_gpio_softc), }; static devclass_t ar71xx_gpio_devclass; DRIVER_MODULE(ar71xx_gpio, apb, ar71xx_gpio_driver, ar71xx_gpio_devclass, 0, 0); Index: head/sys/mips/cavium/octeon_gpio.c =================================================================== --- head/sys/mips/cavium/octeon_gpio.c (revision 277967) +++ head/sys/mips/cavium/octeon_gpio.c (revision 277968) @@ -1,486 +1,491 @@ /*- * Copyright (c) 2011, Oleksandr Tymoshenko * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * GPIO driver for Cavium Octeon */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) struct octeon_gpio_pin { const char *name; int pin; int flags; }; /* * on CAP100 GPIO 7 is "Factory defaults" button * */ static struct octeon_gpio_pin octeon_gpio_pins[] = { { "F/D", 7, GPIO_PIN_INPUT}, { NULL, 0, 0}, }; /* * Helpers */ static void octeon_gpio_pin_configure(struct octeon_gpio_softc *sc, struct gpio_pin *pin, uint32_t flags); /* * Driver stuff */ static void octeon_gpio_identify(driver_t *, device_t); static int octeon_gpio_probe(device_t dev); static int octeon_gpio_attach(device_t dev); static int octeon_gpio_detach(device_t dev); static int octeon_gpio_filter(void *arg); static void octeon_gpio_intr(void *arg); /* * GPIO interface */ static int octeon_gpio_pin_max(device_t dev, int *maxpin); static int octeon_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); static int octeon_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags); static int octeon_gpio_pin_getname(device_t dev, uint32_t pin, char *name); static int octeon_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); static int octeon_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); static int octeon_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); static int octeon_gpio_pin_toggle(device_t dev, uint32_t pin); static void octeon_gpio_pin_configure(struct octeon_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { uint32_t mask; cvmx_gpio_bit_cfgx_t gpio_cfgx; mask = 1 << pin->gp_pin; GPIO_LOCK(sc); /* * Manage input/output */ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(pin->gp_pin)); pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; gpio_cfgx.s.tx_oe = 1; } else { pin->gp_flags |= GPIO_PIN_INPUT; gpio_cfgx.s.tx_oe = 0; } if (flags & GPIO_PIN_INVIN) gpio_cfgx.s.rx_xor = 1; else gpio_cfgx.s.rx_xor = 0; cvmx_write_csr(CVMX_GPIO_BIT_CFGX(pin->gp_pin), gpio_cfgx.u64); } GPIO_UNLOCK(sc); } static int octeon_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = OCTEON_GPIO_PINS - 1; return (0); } static int octeon_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct octeon_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *caps = sc->gpio_pins[i].gp_caps; GPIO_UNLOCK(sc); return (0); } static int octeon_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct octeon_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *flags = sc->gpio_pins[i].gp_flags; GPIO_UNLOCK(sc); return (0); } static int octeon_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct octeon_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); GPIO_UNLOCK(sc); return (0); } static int octeon_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { int i; struct octeon_gpio_softc *sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); octeon_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); return (0); } static int octeon_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct octeon_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); if (value) cvmx_gpio_set(1 << pin); else cvmx_gpio_clear(1 << pin); GPIO_UNLOCK(sc); return (0); } static int octeon_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct octeon_gpio_softc *sc = device_get_softc(dev); int i; uint64_t state; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); state = cvmx_gpio_read(); *val = (state & (1 << pin)) ? 1 : 0; GPIO_UNLOCK(sc); return (0); } static int octeon_gpio_pin_toggle(device_t dev, uint32_t pin) { int i; uint64_t state; struct octeon_gpio_softc *sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); /* * XXX: Need to check if read returns actual state of output * pins or we need to keep this information by ourself */ state = cvmx_gpio_read(); if (state & (1 << pin)) cvmx_gpio_clear(1 << pin); else cvmx_gpio_set(1 << pin); GPIO_UNLOCK(sc); return (0); } static int octeon_gpio_filter(void *arg) { cvmx_gpio_bit_cfgx_t gpio_cfgx; void **cookie = arg; struct octeon_gpio_softc *sc = *cookie; long int irq = (cookie - sc->gpio_intr_cookies); if ((irq < 0) || (irq >= OCTEON_GPIO_IRQS)) return (FILTER_STRAY); gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(irq)); /* Clear rising edge detector */ if (gpio_cfgx.s.int_type == OCTEON_GPIO_IRQ_EDGE) cvmx_gpio_interrupt_clear(1 << irq); /* disable interrupt */ gpio_cfgx.s.int_en = 0; cvmx_write_csr(CVMX_GPIO_BIT_CFGX(irq), gpio_cfgx.u64); return (FILTER_SCHEDULE_THREAD); } static void octeon_gpio_intr(void *arg) { cvmx_gpio_bit_cfgx_t gpio_cfgx; void **cookie = arg; struct octeon_gpio_softc *sc = *cookie; long int irq = (cookie - sc->gpio_intr_cookies); if ((irq < 0) || (irq >= OCTEON_GPIO_IRQS)) { printf("%s: invalid GPIO IRQ: %ld\n", __func__, irq); return; } GPIO_LOCK(sc); gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(irq)); /* disable interrupt */ gpio_cfgx.s.int_en = 1; cvmx_write_csr(CVMX_GPIO_BIT_CFGX(irq), gpio_cfgx.u64); /* TODO: notify bus here or something */ printf("GPIO IRQ for pin %ld\n", irq); GPIO_UNLOCK(sc); } static void octeon_gpio_identify(driver_t *drv, device_t parent) { BUS_ADD_CHILD(parent, 0, "gpio", 0); } static int octeon_gpio_probe(device_t dev) { device_set_desc(dev, "Cavium Octeon GPIO driver"); return (0); } static int octeon_gpio_attach(device_t dev) { struct octeon_gpio_softc *sc = device_get_softc(dev); struct octeon_gpio_pin *pinp; cvmx_gpio_bit_cfgx_t gpio_cfgx; int i; KASSERT((device_get_unit(dev) == 0), ("octeon_gpio: Only one gpio module supported")); mtx_init(&sc->gpio_mtx, device_get_nameunit(dev), NULL, MTX_DEF); for ( i = 0; i < OCTEON_GPIO_IRQS; i++) { if ((sc->gpio_irq_res[i] = bus_alloc_resource(dev, SYS_RES_IRQ, &sc->gpio_irq_rid[i], OCTEON_IRQ_GPIO0 + i, OCTEON_IRQ_GPIO0 + i, 1, RF_SHAREABLE | RF_ACTIVE)) == NULL) { device_printf(dev, "unable to allocate IRQ resource\n"); + octeon_gpio_detach(dev); return (ENXIO); } sc->gpio_intr_cookies[i] = sc; if ((bus_setup_intr(dev, sc->gpio_irq_res[i], INTR_TYPE_MISC, octeon_gpio_filter, octeon_gpio_intr, &(sc->gpio_intr_cookies[i]), &sc->gpio_ih[i]))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); + octeon_gpio_detach(dev); return (ENXIO); } } sc->dev = dev; /* Configure all pins as input */ /* disable interrupts for all pins */ pinp = octeon_gpio_pins; i = 0; while (pinp->name) { strncpy(sc->gpio_pins[i].gp_name, pinp->name, GPIOMAXNAME); sc->gpio_pins[i].gp_pin = pinp->pin; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; sc->gpio_pins[i].gp_flags = 0; octeon_gpio_pin_configure(sc, &sc->gpio_pins[i], pinp->flags); pinp++; i++; } sc->gpio_npins = i; #if 0 /* * Sample: how to enable edge-triggered interrupt * for GPIO pin */ gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(7)); gpio_cfgx.s.int_en = 1; gpio_cfgx.s.int_type = OCTEON_GPIO_IRQ_EDGE; cvmx_write_csr(CVMX_GPIO_BIT_CFGX(7), gpio_cfgx.u64); #endif if (bootverbose) { for (i = 0; i < 16; i++) { gpio_cfgx.u64 = cvmx_read_csr(CVMX_GPIO_BIT_CFGX(i)); device_printf(dev, "[pin%d] output=%d, invinput=%d, intr=%d, intr_type=%s\n", i, gpio_cfgx.s.tx_oe, gpio_cfgx.s.rx_xor, gpio_cfgx.s.int_en, gpio_cfgx.s.int_type ? "rising edge" : "level"); } } device_add_child(dev, "gpioc", -1); device_add_child(dev, "gpiobus", -1); return (bus_generic_attach(dev)); } static int octeon_gpio_detach(device_t dev) { struct octeon_gpio_softc *sc = device_get_softc(dev); int i; KASSERT(mtx_initialized(&sc->gpio_mtx), ("gpio mutex not initialized")); for ( i = 0; i < OCTEON_GPIO_IRQS; i++) { - bus_release_resource(dev, SYS_RES_IRQ, - sc->gpio_irq_rid[i], sc->gpio_irq_res[i]); + if (sc->gpio_ih[i]) + bus_teardown_intr(dev, sc->gpio_irq_res[i], + sc->gpio_ih[i]); + if (sc->gpio_irq_res[i]) + bus_release_resource(dev, SYS_RES_IRQ, + sc->gpio_irq_rid[i], sc->gpio_irq_res[i]); } bus_generic_detach(dev); - mtx_destroy(&sc->gpio_mtx); return(0); } static device_method_t octeon_gpio_methods[] = { DEVMETHOD(device_identify, octeon_gpio_identify), DEVMETHOD(device_probe, octeon_gpio_probe), DEVMETHOD(device_attach, octeon_gpio_attach), DEVMETHOD(device_detach, octeon_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_pin_max, octeon_gpio_pin_max), DEVMETHOD(gpio_pin_getname, octeon_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, octeon_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, octeon_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, octeon_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, octeon_gpio_pin_get), DEVMETHOD(gpio_pin_set, octeon_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, octeon_gpio_pin_toggle), {0, 0}, }; static driver_t octeon_gpio_driver = { "gpio", octeon_gpio_methods, sizeof(struct octeon_gpio_softc), }; static devclass_t octeon_gpio_devclass; DRIVER_MODULE(octeon_gpio, ciu, octeon_gpio_driver, octeon_gpio_devclass, 0, 0); Index: head/sys/mips/rt305x/rt305x_gpio.c =================================================================== --- head/sys/mips/rt305x/rt305x_gpio.c (revision 277967) +++ head/sys/mips/rt305x/rt305x_gpio.c (revision 277968) @@ -1,607 +1,611 @@ /*- * Copyright (c) 2010-2011, Aleksandr Rybalko * Copyright (c) 2009, Oleksandr Tymoshenko * Copyright (c) 2009, Luiz Otavio O Souza. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice unmodified, this list of conditions, and the following * disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * GPIO driver for RT305X SoC. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpio_if.h" #ifdef notyet #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_INVIN | \ GPIO_PIN_INVOUT | GPIO_PIN_REPORT ) #else #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_INVIN | \ GPIO_PIN_INVOUT ) #endif /* * Helpers */ static void rt305x_gpio_pin_configure(struct rt305x_gpio_softc *sc, struct gpio_pin *pin, uint32_t flags); /* * Driver stuff */ static int rt305x_gpio_probe(device_t dev); static int rt305x_gpio_attach(device_t dev); static int rt305x_gpio_detach(device_t dev); static int rt305x_gpio_intr(void *arg); int rt305x_get_int_mask (device_t); void rt305x_set_int_mask (device_t, uint32_t); int rt305x_get_int_status(device_t); void rt305x_set_int_status(device_t, uint32_t); /* * GPIO interface */ static int rt305x_gpio_pin_max(device_t dev, int *maxpin); static int rt305x_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps); static int rt305x_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags); static int rt305x_gpio_pin_getname(device_t dev, uint32_t pin, char *name); static int rt305x_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags); static int rt305x_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value); static int rt305x_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val); static int rt305x_gpio_pin_toggle(device_t dev, uint32_t pin); static void rt305x_gpio_pin_configure(struct rt305x_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { GPIO_LOCK(sc); /* * Manage input/output */ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; GPIO_BIT_SET(sc, pin->gp_pin, DIR); } else { pin->gp_flags |= GPIO_PIN_INPUT; GPIO_BIT_CLR(sc, pin->gp_pin, DIR); } } if (flags & GPIO_PIN_INVOUT) { pin->gp_flags |= GPIO_PIN_INVOUT; GPIO_BIT_SET(sc, pin->gp_pin, POL); } else { pin->gp_flags &= ~GPIO_PIN_INVOUT; GPIO_BIT_CLR(sc, pin->gp_pin, POL); } if (flags & GPIO_PIN_INVIN) { pin->gp_flags |= GPIO_PIN_INVIN; GPIO_BIT_SET(sc, pin->gp_pin, POL); } else { pin->gp_flags &= ~GPIO_PIN_INVIN; GPIO_BIT_CLR(sc, pin->gp_pin, POL); } #ifdef notyet /* Enable interrupt bits for rising/falling transitions */ if (flags & GPIO_PIN_REPORT) { pin->gp_flags |= GPIO_PIN_REPORT; GPIO_BIT_SET(sc, pin->gp_pin, RENA); GPIO_BIT_SET(sc, pin->gp_pin, FENA); device_printf(sc->dev, "Will report interrupt on pin %d\n", pin->gp_pin); } else { pin->gp_flags &= ~GPIO_PIN_REPORT; GPIO_BIT_CLR(sc, pin->gp_pin, RENA); GPIO_BIT_CLR(sc, pin->gp_pin, FENA); } #else /* Disable generating interrupts for now */ GPIO_BIT_CLR(sc, pin->gp_pin, RENA); GPIO_BIT_CLR(sc, pin->gp_pin, FENA); #endif GPIO_UNLOCK(sc); } static int rt305x_gpio_pin_max(device_t dev, int *maxpin) { *maxpin = NGPIO - 1; return (0); } static int rt305x_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct rt305x_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *caps = sc->gpio_pins[i].gp_caps; GPIO_UNLOCK(sc); return (0); } static int rt305x_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct rt305x_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *flags = sc->gpio_pins[i].gp_flags; GPIO_UNLOCK(sc); return (0); } static int rt305x_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct rt305x_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); GPIO_UNLOCK(sc); return (0); } static int rt305x_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { int i; struct rt305x_gpio_softc *sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); rt305x_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); return (0); } static int rt305x_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct rt305x_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); if (value) GPIO_BIT_SET(sc, i, DATA); else GPIO_BIT_CLR(sc, i, DATA); GPIO_UNLOCK(sc); return (0); } static int rt305x_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct rt305x_gpio_softc *sc = device_get_softc(dev); int i; for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); *val = GPIO_BIT_GET(sc, i, DATA); GPIO_UNLOCK(sc); return (0); } static int rt305x_gpio_pin_toggle(device_t dev, uint32_t pin) { int i; struct rt305x_gpio_softc *sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); GPIO_LOCK(sc); GPIO_BIT_SET(sc, i, TOG); GPIO_UNLOCK(sc); return (0); } static int rt305x_gpio_intr(void *arg) { struct rt305x_gpio_softc *sc = arg; #ifdef notyet uint32_t i; #endif uint64_t input, value; #ifdef notyet uint64_t reset_pin; char notify[16]; char pinname[6]; #endif /* Read all reported pins */ input = GPIO_READ_ALL(sc, INT); /* Clear int status */ GPIO_WRITE_ALL(sc, INT, input); /* Clear report for OUTs */ input &= ~GPIO_READ_ALL(sc, DIR); value = input & GPIO_READ_ALL(sc, DATA); if (!input) goto intr_done; #ifdef notyet /* if reset_gpio and this pin is input */ if (sc->reset_gpio >= 0 && (input & (1 << sc->reset_gpio))) { /* get reset_gpio pin value */ reset_pin = (value & (1 << sc->reset_gpio))?1:0; if ( sc->reset_gpio_last != reset_pin ) { /* * if now reset is high, check how long * and do reset if less than 2 seconds */ if ( reset_pin && (time_uptime - sc->reset_gpio_ontime) < 2 ) shutdown_nice(0); sc->reset_gpio_last = reset_pin; sc->reset_gpio_ontime = time_uptime; } } for ( i = 0; i < NGPIO; i ++ ) { /* Next if output pin */ if ( !(( input >> i) & 1) ) continue; if ( (((value & input) >> i) & 1) != sc->gpio_pins[i].gp_last ) { /* !system=GPIO subsystem=pin7 type=PIN_HIGH period=3 */ snprintf(notify , sizeof(notify ), "period=%d", (uint32_t)time_uptime - sc->gpio_pins[i].gp_time); snprintf(pinname, sizeof(pinname), "pin%02d", i); devctl_notify("GPIO", pinname, (((value & input) >> i) & 1)?"PIN_HIGH":"PIN_LOW", notify); printf("GPIO[%s] %s %s\n", pinname, (((value & input) >> i) & 1)?"PIN_HIGH":"PIN_LOW", notify); sc->gpio_pins[i].gp_last = ((value & input) >> i) & 1; sc->gpio_pins[i].gp_time = time_uptime; } } #endif intr_done: return (FILTER_HANDLED); } static int rt305x_gpio_probe(device_t dev) { device_set_desc(dev, "RT305X GPIO driver"); return (0); } static uint64_t rt305x_gpio_init(device_t dev) { uint64_t avl = ~0ULL; uint32_t gmode = rt305x_sysctl_get(SYSCTL_GPIOMODE); if (!(gmode & SYSCTL_GPIOMODE_RGMII_GPIO_MODE)) avl &= ~RGMII_GPIO_MODE_MASK; if (!(gmode & SYSCTL_GPIOMODE_SDRAM_GPIO_MODE)) avl &= ~SDRAM_GPIO_MODE_MASK; if (!(gmode & SYSCTL_GPIOMODE_MDIO_GPIO_MODE)) avl &= ~MDIO_GPIO_MODE_MASK; if (!(gmode & SYSCTL_GPIOMODE_JTAG_GPIO_MODE)) avl &= ~JTAG_GPIO_MODE_MASK; if (!(gmode & SYSCTL_GPIOMODE_UARTL_GPIO_MODE)) avl &= ~UARTL_GPIO_MODE_MASK; if (!(gmode & SYSCTL_GPIOMODE_SPI_GPIO_MODE)) avl &= ~SPI_GPIO_MODE_MASK; if (!(gmode & SYSCTL_GPIOMODE_I2C_GPIO_MODE)) avl &= ~I2C_GPIO_MODE_MASK; if ((gmode & SYSCTL_GPIOMODE_UARTF_SHARE_MODE_GPIO) != SYSCTL_GPIOMODE_UARTF_SHARE_MODE_GPIO) avl &= ~I2C_GPIO_MODE_MASK; /* D-Link DAP-1350 Board have * MDIO_GPIO_MODE * UARTF_GPIO_MODE * SPI_GPIO_MODE * I2C_GPIO_MODE * So we have * 00000001 10000000 01111111 11111110 */ return (avl); } #define DAP1350_RESET_GPIO 10 static int rt305x_gpio_attach(device_t dev) { struct rt305x_gpio_softc *sc = device_get_softc(dev); - int error = 0, i; + int i; uint64_t avlpins = 0; sc->reset_gpio = DAP1350_RESET_GPIO; KASSERT((device_get_unit(dev) == 0), ("rt305x_gpio_gpio: Only one gpio module supported")); mtx_init(&sc->gpio_mtx, device_get_nameunit(dev), NULL, MTX_DEF); /* Map control/status registers. */ sc->gpio_mem_rid = 0; sc->gpio_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->gpio_mem_rid, RF_ACTIVE); if (sc->gpio_mem_res == NULL) { device_printf(dev, "couldn't map memory\n"); - error = ENXIO; rt305x_gpio_detach(dev); - return(error); + return (ENXIO); } if ((sc->gpio_irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->gpio_irq_rid, RF_SHAREABLE | RF_ACTIVE)) == NULL) { device_printf(dev, "unable to allocate IRQ resource\n"); + rt305x_gpio_detach(dev); return (ENXIO); } if ((bus_setup_intr(dev, sc->gpio_irq_res, INTR_TYPE_MISC, /* rt305x_gpio_filter, */ rt305x_gpio_intr, NULL, sc, &sc->gpio_ih))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); + rt305x_gpio_detach(dev); return (ENXIO); } sc->dev = dev; avlpins = rt305x_gpio_init(dev); /* Configure all pins as input */ /* disable interrupts for all pins */ /* TODO */ sc->gpio_npins = NGPIO; resource_int_value(device_get_name(dev), device_get_unit(dev), "pins", &sc->gpio_npins); for (i = 0; i < sc->gpio_npins; i++) { sc->gpio_pins[i].gp_pin = i; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; sc->gpio_pins[i].gp_flags = 0; } /* Setup reset pin interrupt */ if (TUNABLE_INT_FETCH("reset_gpio", &sc->reset_gpio)) { device_printf(dev, "\tHinted reset_gpio %d\n", sc->reset_gpio); } #ifdef notyet if (sc->reset_gpio != -1) { rt305x_gpio_pin_setflags(dev, sc->reset_gpio, GPIO_PIN_INPUT|GPIO_PIN_INVOUT| GPIO_PIN_INVOUT|GPIO_PIN_REPORT); device_printf(dev, "\tUse reset_gpio %d\n", sc->reset_gpio); } #else if (sc->reset_gpio != -1) { rt305x_gpio_pin_setflags(dev, sc->reset_gpio, GPIO_PIN_INPUT|GPIO_PIN_INVOUT); device_printf(dev, "\tUse reset_gpio %d\n", sc->reset_gpio); } #endif device_add_child(dev, "gpioc", -1); device_add_child(dev, "gpiobus", -1); return (bus_generic_attach(dev)); } static int rt305x_gpio_detach(device_t dev) { struct rt305x_gpio_softc *sc = device_get_softc(dev); KASSERT(mtx_initialized(&sc->gpio_mtx), ("gpio mutex not initialized")); bus_generic_detach(dev); - + if (sc->gpio_ih) + bus_teardown_intr(dev, sc->gpio_irq_res, sc->gpio_ih); + if (sc->gpio_irq_res) + bus_release_resource(dev, SYS_RES_IRQ, sc->gpio_irq_rid, + sc->gpio_irq_res); if (sc->gpio_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, sc->gpio_mem_rid, sc->gpio_mem_res); - mtx_destroy(&sc->gpio_mtx); return(0); } #ifdef notyet static struct resource * rt305x_gpio_alloc_resource(device_t bus, device_t child, int type, int *rid, u_long start, u_long end, u_long count, u_int flags) { struct obio_softc *sc = device_get_softc(bus); struct resource *rv; struct rman *rm; switch (type) { case SYS_RES_GPIO: rm = &sc->gpio_rman; break; default: printf("%s: unknown resource type %d\n", __func__, type); return (0); } rv = rman_reserve_resource(rm, start, end, count, flags, child); if (rv == 0) { printf("%s: could not reserve resource\n", __func__); return (0); } rman_set_rid(rv, *rid); return (rv); } static int rt305x_gpio_activate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (rman_activate_resource(r)); } static int rt305x_gpio_deactivate_resource(device_t bus, device_t child, int type, int rid, struct resource *r) { return (rman_deactivate_resource(r)); } static int rt305x_gpio_release_resource(device_t dev, device_t child, int type, int rid, struct resource *r) { rman_release_resource(r); return (0); } #endif static device_method_t rt305x_gpio_methods[] = { DEVMETHOD(device_probe, rt305x_gpio_probe), DEVMETHOD(device_attach, rt305x_gpio_attach), DEVMETHOD(device_detach, rt305x_gpio_detach), /* GPIO protocol */ DEVMETHOD(gpio_pin_max, rt305x_gpio_pin_max), DEVMETHOD(gpio_pin_getname, rt305x_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, rt305x_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, rt305x_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, rt305x_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, rt305x_gpio_pin_get), DEVMETHOD(gpio_pin_set, rt305x_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, rt305x_gpio_pin_toggle), {0, 0}, }; static driver_t rt305x_gpio_driver = { "gpio", rt305x_gpio_methods, sizeof(struct rt305x_gpio_softc), }; static devclass_t rt305x_gpio_devclass; DRIVER_MODULE(rt305x_gpio, obio, rt305x_gpio_driver, rt305x_gpio_devclass, 0, 0);