/*- * 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 128 #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 #define RK30_GPIO_INPUT 0 #define RK30_GPIO_OUTPUT 1 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_gpio_npins; struct gpio_pin sc_gpio_pins[RK30_GPIO_PINS]; }; 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_PORTS_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) { uint32_t bank, func, offset; bank = pin / 32; pin = pin % 32; offset = 1 << pin; func = RK30_GPIO_READ(sc, RK30_GPIO_SWPORT_DDR); func &= offset; return (func); } static uint32_t rk30_gpio_func_flag(uint32_t nfunc) { switch (nfunc) { case RK30_GPIO_INPUT: return (GPIO_PIN_INPUT); case RK30_GPIO_OUTPUT: return (GPIO_PIN_OUTPUT); } return (0); } static void rk30_gpio_set_function(struct rk30_gpio_softc *sc, uint32_t pin, uint32_t f) { uint32_t bank, data, offset; /* Must be called with lock held. */ RK30_GPIO_LOCK_ASSERT(sc); bank = pin / 32; pin = pin % 32; offset = 1 << pin; data = RK30_GPIO_READ(sc, RK30_GPIO_SWPORT_DDR); if (f) data |= offset; else data &= ~offset; 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 bank; bank = pin / 32; /* Must be called with lock held. */ RK30_GPIO_LOCK_ASSERT(sc); if (bank == 0 && pin < 12) rk30_pmu_gpio_pud(pin, state); else rk30_grf_gpio_pud(bank, pin, state); } 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; rk30_gpio_set_function(sc, pin->gp_pin, RK30_GPIO_OUTPUT); } else { pin->gp_flags |= GPIO_PIN_INPUT; rk30_gpio_set_function(sc, pin->gp_pin, RK30_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; rk30_gpio_set_pud(sc, pin->gp_pin, RK30_GPIO_PULLUP); } else { pin->gp_flags |= GPIO_PIN_PULLDOWN; rk30_gpio_set_pud(sc, pin->gp_pin, RK30_GPIO_PULLDOWN); } } else rk30_gpio_set_pud(sc, pin->gp_pin, RK30_GPIO_NONE); 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) { struct rk30_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; offset = 1 << pin; RK30_GPIO_LOCK(sc); data = RK30_GPIO_READ(sc, RK30_GPIO_SWPORT_DDR); data |= offset; RK30_GPIO_WRITE(sc, RK30_GPIO_SWPORT_DDR, data); data = RK30_GPIO_READ(sc, RK30_GPIO_SWPORT_DR); if (value) data |= offset; else data &= ~offset; 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) { struct rk30_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; offset = 1 << pin; RK30_GPIO_LOCK(sc); reg_data = RK30_GPIO_READ(sc, RK30_GPIO_EXT_PORT); RK30_GPIO_UNLOCK(sc); *val = (reg_data & offset) ? 1 : 0; return (0); } static int rk30_gpio_pin_toggle(device_t dev, uint32_t pin) { struct rk30_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; offset = 1 << pin; RK30_GPIO_LOCK(sc); data = RK30_GPIO_READ(sc, RK30_GPIO_SWPORT_DDR); if (data & offset) data &= ~offset; else data |= offset; RK30_GPIO_WRITE(sc, RK30_GPIO_SWPORT_DDR, data); data = RK30_GPIO_READ(sc, RK30_GPIO_SWPORT_DR); if (data & offset) data &= ~offset; else data |= offset; 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); uint32_t func; int i, rid; phandle_t gpio; 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); } 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); } /* 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 < RK30_GPIO_PINS; i++) { snprintf(sc->sc_gpio_pins[i].gp_name, GPIOMAXNAME, "pin %d", i); func = rk30_gpio_get_function(sc, 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_func_flag(func); } 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); 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, RK30_GPIO_INPUT); } else { /* Output. */ rk30_gpio_pin_set(sc->sc_dev, pin, RK30_GPIO_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); }