1ef2ee5d0SMichal Meloun /*- 2ef2ee5d0SMichal Meloun * Copyright (c) 2016 Michal Meloun <mmel@FreeBSD.org> 3ef2ee5d0SMichal Meloun * All rights reserved. 4ef2ee5d0SMichal Meloun * 5ef2ee5d0SMichal Meloun * Redistribution and use in source and binary forms, with or without 6ef2ee5d0SMichal Meloun * modification, are permitted provided that the following conditions 7ef2ee5d0SMichal Meloun * are met: 8ef2ee5d0SMichal Meloun * 1. Redistributions of source code must retain the above copyright 9ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer. 10ef2ee5d0SMichal Meloun * 2. Redistributions in binary form must reproduce the above copyright 11ef2ee5d0SMichal Meloun * notice, this list of conditions and the following disclaimer in the 12ef2ee5d0SMichal Meloun * documentation and/or other materials provided with the distribution. 13ef2ee5d0SMichal Meloun * 14ef2ee5d0SMichal Meloun * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15ef2ee5d0SMichal Meloun * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16ef2ee5d0SMichal Meloun * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17ef2ee5d0SMichal Meloun * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18ef2ee5d0SMichal Meloun * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19ef2ee5d0SMichal Meloun * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20ef2ee5d0SMichal Meloun * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21ef2ee5d0SMichal Meloun * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22ef2ee5d0SMichal Meloun * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23ef2ee5d0SMichal Meloun * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24ef2ee5d0SMichal Meloun * SUCH DAMAGE. 25ef2ee5d0SMichal Meloun */ 26ef2ee5d0SMichal Meloun 27ef2ee5d0SMichal Meloun #include <sys/param.h> 28ef2ee5d0SMichal Meloun #include <sys/systm.h> 29ef2ee5d0SMichal Meloun #include <sys/bus.h> 30ef2ee5d0SMichal Meloun #include <sys/gpio.h> 31ef2ee5d0SMichal Meloun #include <sys/kernel.h> 32ef2ee5d0SMichal Meloun #include <sys/malloc.h> 33ef2ee5d0SMichal Meloun #include <sys/sx.h> 34ef2ee5d0SMichal Meloun 35ef2ee5d0SMichal Meloun #include <machine/bus.h> 36ef2ee5d0SMichal Meloun 37ef2ee5d0SMichal Meloun #include <dev/fdt/fdt_common.h> 38ef2ee5d0SMichal Meloun #include <dev/gpio/gpiobusvar.h> 39ef2ee5d0SMichal Meloun 40ef2ee5d0SMichal Meloun #include "as3722.h" 41ef2ee5d0SMichal Meloun 42ef2ee5d0SMichal Meloun MALLOC_DEFINE(M_AS3722_GPIO, "AS3722 gpio", "AS3722 GPIO"); 43ef2ee5d0SMichal Meloun 44ef2ee5d0SMichal Meloun /* AS3722_GPIOx_CONTROL MODE and IOSF definition. */ 45ef2ee5d0SMichal Meloun #define AS3722_IOSF_GPIO 0x00 46ef2ee5d0SMichal Meloun #define AS3722_IOSF_INTERRUPT_OUT 0x01 47ef2ee5d0SMichal Meloun #define AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT 0x02 48ef2ee5d0SMichal Meloun #define AS3722_IOSF_GPIO_IN_INTERRUPT 0x03 49ef2ee5d0SMichal Meloun #define AS3722_IOSF_PWM_IN 0x04 50ef2ee5d0SMichal Meloun #define AS3722_IOSF_VOLTAGE_IN_STANDBY 0x05 51ef2ee5d0SMichal Meloun #define AS3722_IOSF_OC_PG_SD0 0x06 52ef2ee5d0SMichal Meloun #define AS3722_IOSF_POWERGOOD_OUT 0x07 53ef2ee5d0SMichal Meloun #define AS3722_IOSF_CLK32K_OUT 0x08 54ef2ee5d0SMichal Meloun #define AS3722_IOSF_WATCHDOG_IN 0x09 55ef2ee5d0SMichal Meloun #define AS3722_IOSF_SOFT_RESET_IN 0x0b 56ef2ee5d0SMichal Meloun #define AS3722_IOSF_PWM_OUT 0x0c 57ef2ee5d0SMichal Meloun #define AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT 0x0d 58ef2ee5d0SMichal Meloun #define AS3722_IOSF_OC_PG_SD6 0x0e 59ef2ee5d0SMichal Meloun 60ef2ee5d0SMichal Meloun #define AS3722_MODE_INPUT 0 61ef2ee5d0SMichal Meloun #define AS3722_MODE_PUSH_PULL 1 62ef2ee5d0SMichal Meloun #define AS3722_MODE_OPEN_DRAIN 2 63ef2ee5d0SMichal Meloun #define AS3722_MODE_TRISTATE 3 64ef2ee5d0SMichal Meloun #define AS3722_MODE_INPUT_PULL_UP_LV 4 65ef2ee5d0SMichal Meloun #define AS3722_MODE_INPUT_PULL_DOWN 5 66ef2ee5d0SMichal Meloun #define AS3722_MODE_OPEN_DRAIN_LV 6 67ef2ee5d0SMichal Meloun #define AS3722_MODE_PUSH_PULL_LV 7 68ef2ee5d0SMichal Meloun 69ef2ee5d0SMichal Meloun #define NGPIO 8 70ef2ee5d0SMichal Meloun 71ef2ee5d0SMichal Meloun #define GPIO_LOCK(_sc) sx_slock(&(_sc)->gpio_lock) 72ef2ee5d0SMichal Meloun #define GPIO_UNLOCK(_sc) sx_unlock(&(_sc)->gpio_lock) 73ef2ee5d0SMichal Meloun #define GPIO_ASSERT(_sc) sx_assert(&(_sc)->gpio_lock, SA_LOCKED) 74ef2ee5d0SMichal Meloun 75ef2ee5d0SMichal Meloun #define AS3722_CFG_BIAS_DISABLE 0x0001 76ef2ee5d0SMichal Meloun #define AS3722_CFG_BIAS_PULL_UP 0x0002 77ef2ee5d0SMichal Meloun #define AS3722_CFG_BIAS_PULL_DOWN 0x0004 78ef2ee5d0SMichal Meloun #define AS3722_CFG_BIAS_HIGH_IMPEDANCE 0x0008 79ef2ee5d0SMichal Meloun #define AS3722_CFG_OPEN_DRAIN 0x0010 80ef2ee5d0SMichal Meloun 81ef2ee5d0SMichal Meloun static const struct { 82ef2ee5d0SMichal Meloun const char *name; 83ef2ee5d0SMichal Meloun int config; /* AS3722_CFG_ */ 84ef2ee5d0SMichal Meloun } as3722_cfg_names[] = { 85ef2ee5d0SMichal Meloun {"bias-disable", AS3722_CFG_BIAS_DISABLE}, 86ef2ee5d0SMichal Meloun {"bias-pull-up", AS3722_CFG_BIAS_PULL_UP}, 87ef2ee5d0SMichal Meloun {"bias-pull-down", AS3722_CFG_BIAS_PULL_DOWN}, 88ef2ee5d0SMichal Meloun {"bias-high-impedance", AS3722_CFG_BIAS_HIGH_IMPEDANCE}, 89ef2ee5d0SMichal Meloun {"drive-open-drain", AS3722_CFG_OPEN_DRAIN}, 90ef2ee5d0SMichal Meloun }; 91ef2ee5d0SMichal Meloun 92ef2ee5d0SMichal Meloun static struct { 93ef2ee5d0SMichal Meloun const char *name; 94ef2ee5d0SMichal Meloun int fnc_val; 95ef2ee5d0SMichal Meloun } as3722_fnc_table[] = { 96ef2ee5d0SMichal Meloun {"gpio", AS3722_IOSF_GPIO}, 97ef2ee5d0SMichal Meloun {"interrupt-out", AS3722_IOSF_INTERRUPT_OUT}, 98ef2ee5d0SMichal Meloun {"vsup-vbat-low-undebounce-out", AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT}, 99ef2ee5d0SMichal Meloun {"gpio-in-interrupt", AS3722_IOSF_GPIO_IN_INTERRUPT}, 100ef2ee5d0SMichal Meloun {"pwm-in", AS3722_IOSF_PWM_IN}, 101ef2ee5d0SMichal Meloun {"voltage-in-standby", AS3722_IOSF_VOLTAGE_IN_STANDBY}, 102ef2ee5d0SMichal Meloun {"oc-pg-sd0", AS3722_IOSF_OC_PG_SD0}, 103ef2ee5d0SMichal Meloun {"powergood-out", AS3722_IOSF_POWERGOOD_OUT}, 104ef2ee5d0SMichal Meloun {"clk32k-out", AS3722_IOSF_CLK32K_OUT}, 105ef2ee5d0SMichal Meloun {"watchdog-in", AS3722_IOSF_WATCHDOG_IN}, 106ef2ee5d0SMichal Meloun {"soft-reset-in", AS3722_IOSF_SOFT_RESET_IN}, 107ef2ee5d0SMichal Meloun {"pwm-out", AS3722_IOSF_PWM_OUT}, 108ef2ee5d0SMichal Meloun {"vsup-vbat-low-debounce-out", AS3722_IOSF_VSUP_VBAT_LOW_DEBOUNCE_OUT}, 109ef2ee5d0SMichal Meloun {"oc-pg-sd6", AS3722_IOSF_OC_PG_SD6}, 110ef2ee5d0SMichal Meloun }; 111ef2ee5d0SMichal Meloun 112ef2ee5d0SMichal Meloun struct as3722_pincfg { 113ef2ee5d0SMichal Meloun char *function; 114ef2ee5d0SMichal Meloun int flags; 115ef2ee5d0SMichal Meloun }; 116ef2ee5d0SMichal Meloun 117ef2ee5d0SMichal Meloun struct as3722_gpio_pin { 118ef2ee5d0SMichal Meloun int pin_caps; 119ef2ee5d0SMichal Meloun uint8_t pin_ctrl_reg; 120ef2ee5d0SMichal Meloun char pin_name[GPIOMAXNAME]; 121ef2ee5d0SMichal Meloun int pin_cfg_flags; 122ef2ee5d0SMichal Meloun }; 123ef2ee5d0SMichal Meloun 124ef2ee5d0SMichal Meloun /* -------------------------------------------------------------------------- 125ef2ee5d0SMichal Meloun * 126ef2ee5d0SMichal Meloun * Pinmux functions. 127ef2ee5d0SMichal Meloun */ 128ef2ee5d0SMichal Meloun static int 129ef2ee5d0SMichal Meloun as3722_pinmux_get_function(struct as3722_softc *sc, char *name) 130ef2ee5d0SMichal Meloun { 131ef2ee5d0SMichal Meloun int i; 132ef2ee5d0SMichal Meloun 133ef2ee5d0SMichal Meloun for (i = 0; i < nitems(as3722_fnc_table); i++) { 134ef2ee5d0SMichal Meloun if (strcmp(as3722_fnc_table[i].name, name) == 0) 135ef2ee5d0SMichal Meloun return (as3722_fnc_table[i].fnc_val); 136ef2ee5d0SMichal Meloun } 137ef2ee5d0SMichal Meloun return (-1); 138ef2ee5d0SMichal Meloun } 139ef2ee5d0SMichal Meloun 140ef2ee5d0SMichal Meloun static int 141ef2ee5d0SMichal Meloun as3722_pinmux_config_node(struct as3722_softc *sc, char *pin_name, 142ef2ee5d0SMichal Meloun struct as3722_pincfg *cfg) 143ef2ee5d0SMichal Meloun { 144ef2ee5d0SMichal Meloun uint8_t ctrl; 145ef2ee5d0SMichal Meloun int rv, fnc, pin; 146ef2ee5d0SMichal Meloun 147ef2ee5d0SMichal Meloun for (pin = 0; pin < sc->gpio_npins; pin++) { 148ef2ee5d0SMichal Meloun if (strcmp(sc->gpio_pins[pin]->pin_name, pin_name) == 0) 149ef2ee5d0SMichal Meloun break; 150ef2ee5d0SMichal Meloun } 151ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins) { 152ef2ee5d0SMichal Meloun device_printf(sc->dev, "Unknown pin: %s\n", pin_name); 153ef2ee5d0SMichal Meloun return (ENXIO); 154ef2ee5d0SMichal Meloun } 155ef2ee5d0SMichal Meloun 156ef2ee5d0SMichal Meloun ctrl = sc->gpio_pins[pin]->pin_ctrl_reg; 157ef2ee5d0SMichal Meloun sc->gpio_pins[pin]->pin_cfg_flags = cfg->flags; 158ef2ee5d0SMichal Meloun if (cfg->function != NULL) { 159ef2ee5d0SMichal Meloun fnc = as3722_pinmux_get_function(sc, cfg->function); 160ef2ee5d0SMichal Meloun if (fnc == -1) { 161ef2ee5d0SMichal Meloun device_printf(sc->dev, 162ef2ee5d0SMichal Meloun "Unknown function %s for pin %s\n", cfg->function, 163ef2ee5d0SMichal Meloun sc->gpio_pins[pin]->pin_name); 164ef2ee5d0SMichal Meloun return (ENXIO); 165ef2ee5d0SMichal Meloun } 166ef2ee5d0SMichal Meloun switch (fnc) { 167ef2ee5d0SMichal Meloun case AS3722_IOSF_INTERRUPT_OUT: 168ef2ee5d0SMichal Meloun case AS3722_IOSF_VSUP_VBAT_LOW_UNDEBOUNCE_OUT: 169ef2ee5d0SMichal Meloun case AS3722_IOSF_OC_PG_SD0: 170ef2ee5d0SMichal Meloun case AS3722_IOSF_POWERGOOD_OUT: 171ef2ee5d0SMichal Meloun case AS3722_IOSF_CLK32K_OUT: 172ef2ee5d0SMichal Meloun case AS3722_IOSF_PWM_OUT: 173ef2ee5d0SMichal Meloun case AS3722_IOSF_OC_PG_SD6: 174ef2ee5d0SMichal Meloun ctrl &= ~(AS3722_GPIO_MODE_MASK << 175ef2ee5d0SMichal Meloun AS3722_GPIO_MODE_SHIFT); 176ef2ee5d0SMichal Meloun ctrl |= AS3722_MODE_PUSH_PULL << AS3722_GPIO_MODE_SHIFT; 177ef2ee5d0SMichal Meloun /* XXX Handle flags (OC + pullup) */ 178ef2ee5d0SMichal Meloun break; 179ef2ee5d0SMichal Meloun case AS3722_IOSF_GPIO_IN_INTERRUPT: 180ef2ee5d0SMichal Meloun case AS3722_IOSF_PWM_IN: 181ef2ee5d0SMichal Meloun case AS3722_IOSF_VOLTAGE_IN_STANDBY: 182ef2ee5d0SMichal Meloun case AS3722_IOSF_WATCHDOG_IN: 183ef2ee5d0SMichal Meloun case AS3722_IOSF_SOFT_RESET_IN: 184ef2ee5d0SMichal Meloun ctrl &= ~(AS3722_GPIO_MODE_MASK << 185ef2ee5d0SMichal Meloun AS3722_GPIO_MODE_SHIFT); 186ef2ee5d0SMichal Meloun ctrl |= AS3722_MODE_INPUT << AS3722_GPIO_MODE_SHIFT; 187ef2ee5d0SMichal Meloun /* XXX Handle flags (pulldown + pullup) */ 188ef2ee5d0SMichal Meloun 189ef2ee5d0SMichal Meloun default: 190ef2ee5d0SMichal Meloun break; 191ef2ee5d0SMichal Meloun } 192ef2ee5d0SMichal Meloun ctrl &= ~(AS3722_GPIO_IOSF_MASK << AS3722_GPIO_IOSF_SHIFT); 193ef2ee5d0SMichal Meloun ctrl |= fnc << AS3722_GPIO_IOSF_SHIFT; 194ef2ee5d0SMichal Meloun } 195ef2ee5d0SMichal Meloun rv = 0; 196ef2ee5d0SMichal Meloun if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) { 197ef2ee5d0SMichal Meloun rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl); 198ef2ee5d0SMichal Meloun sc->gpio_pins[pin]->pin_ctrl_reg = ctrl; 199ef2ee5d0SMichal Meloun } 200ef2ee5d0SMichal Meloun return (rv); 201ef2ee5d0SMichal Meloun } 202ef2ee5d0SMichal Meloun 203ef2ee5d0SMichal Meloun static int 204ef2ee5d0SMichal Meloun as3722_pinmux_read_node(struct as3722_softc *sc, phandle_t node, 205ef2ee5d0SMichal Meloun struct as3722_pincfg *cfg, char **pins, int *lpins) 206ef2ee5d0SMichal Meloun { 207ef2ee5d0SMichal Meloun int rv, i; 208ef2ee5d0SMichal Meloun 209217d17bcSOleksandr Tymoshenko *lpins = OF_getprop_alloc(node, "pins", (void **)pins); 210ef2ee5d0SMichal Meloun if (*lpins <= 0) 211ef2ee5d0SMichal Meloun return (ENOENT); 212ef2ee5d0SMichal Meloun 213ef2ee5d0SMichal Meloun /* Read function (mux) settings. */ 214217d17bcSOleksandr Tymoshenko rv = OF_getprop_alloc(node, "function", (void **)&cfg->function); 215ef2ee5d0SMichal Meloun if (rv <= 0) 216ef2ee5d0SMichal Meloun cfg->function = NULL; 217ef2ee5d0SMichal Meloun 218ef2ee5d0SMichal Meloun /* Read boolean properties. */ 219ef2ee5d0SMichal Meloun for (i = 0; i < nitems(as3722_cfg_names); i++) { 220ef2ee5d0SMichal Meloun if (OF_hasprop(node, as3722_cfg_names[i].name)) 221ef2ee5d0SMichal Meloun cfg->flags |= as3722_cfg_names[i].config; 222ef2ee5d0SMichal Meloun } 223ef2ee5d0SMichal Meloun return (0); 224ef2ee5d0SMichal Meloun } 225ef2ee5d0SMichal Meloun 226ef2ee5d0SMichal Meloun static int 227ef2ee5d0SMichal Meloun as3722_pinmux_process_node(struct as3722_softc *sc, phandle_t node) 228ef2ee5d0SMichal Meloun { 229ef2ee5d0SMichal Meloun struct as3722_pincfg cfg; 230ef2ee5d0SMichal Meloun char *pins, *pname; 231ef2ee5d0SMichal Meloun int i, len, lpins, rv; 232ef2ee5d0SMichal Meloun 233ef2ee5d0SMichal Meloun rv = as3722_pinmux_read_node(sc, node, &cfg, &pins, &lpins); 234ef2ee5d0SMichal Meloun if (rv != 0) 235ef2ee5d0SMichal Meloun return (rv); 236ef2ee5d0SMichal Meloun 237ef2ee5d0SMichal Meloun len = 0; 238ef2ee5d0SMichal Meloun pname = pins; 239ef2ee5d0SMichal Meloun do { 240ef2ee5d0SMichal Meloun i = strlen(pname) + 1; 241ef2ee5d0SMichal Meloun rv = as3722_pinmux_config_node(sc, pname, &cfg); 242ef2ee5d0SMichal Meloun if (rv != 0) { 243ef2ee5d0SMichal Meloun device_printf(sc->dev, 244ef2ee5d0SMichal Meloun "Cannot configure pin: %s: %d\n", pname, rv); 245ef2ee5d0SMichal Meloun } 246ef2ee5d0SMichal Meloun len += i; 247ef2ee5d0SMichal Meloun pname += i; 248ef2ee5d0SMichal Meloun } while (len < lpins); 249ef2ee5d0SMichal Meloun 250ef2ee5d0SMichal Meloun if (pins != NULL) 251bebd5269SOleksandr Tymoshenko OF_prop_free(pins); 252ef2ee5d0SMichal Meloun if (cfg.function != NULL) 253bebd5269SOleksandr Tymoshenko OF_prop_free(cfg.function); 254ef2ee5d0SMichal Meloun 255ef2ee5d0SMichal Meloun return (rv); 256ef2ee5d0SMichal Meloun } 257ef2ee5d0SMichal Meloun 258ef2ee5d0SMichal Meloun int as3722_pinmux_configure(device_t dev, phandle_t cfgxref) 259ef2ee5d0SMichal Meloun { 260ef2ee5d0SMichal Meloun struct as3722_softc *sc; 261ef2ee5d0SMichal Meloun phandle_t node, cfgnode; 262ef2ee5d0SMichal Meloun int rv; 263ef2ee5d0SMichal Meloun 264ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 265ef2ee5d0SMichal Meloun cfgnode = OF_node_from_xref(cfgxref); 266ef2ee5d0SMichal Meloun 267ef2ee5d0SMichal Meloun for (node = OF_child(cfgnode); node != 0; node = OF_peer(node)) { 2687bc28467SAndrew Turner if (!ofw_bus_node_status_okay(node)) 269ef2ee5d0SMichal Meloun continue; 270ef2ee5d0SMichal Meloun rv = as3722_pinmux_process_node(sc, node); 271ef2ee5d0SMichal Meloun if (rv != 0) 272ef2ee5d0SMichal Meloun device_printf(dev, "Failed to process pinmux"); 273ef2ee5d0SMichal Meloun } 274ef2ee5d0SMichal Meloun return (0); 275ef2ee5d0SMichal Meloun } 276ef2ee5d0SMichal Meloun 277ef2ee5d0SMichal Meloun /* -------------------------------------------------------------------------- 278ef2ee5d0SMichal Meloun * 279ef2ee5d0SMichal Meloun * GPIO 280ef2ee5d0SMichal Meloun */ 281ef2ee5d0SMichal Meloun device_t 282ef2ee5d0SMichal Meloun as3722_gpio_get_bus(device_t dev) 283ef2ee5d0SMichal Meloun { 284ef2ee5d0SMichal Meloun struct as3722_softc *sc; 285ef2ee5d0SMichal Meloun 286ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 287ef2ee5d0SMichal Meloun return (sc->gpio_busdev); 288ef2ee5d0SMichal Meloun } 289ef2ee5d0SMichal Meloun 290ef2ee5d0SMichal Meloun int 291ef2ee5d0SMichal Meloun as3722_gpio_pin_max(device_t dev, int *maxpin) 292ef2ee5d0SMichal Meloun { 293ef2ee5d0SMichal Meloun 294ef2ee5d0SMichal Meloun *maxpin = NGPIO - 1; 295ef2ee5d0SMichal Meloun return (0); 296ef2ee5d0SMichal Meloun } 297ef2ee5d0SMichal Meloun 298ef2ee5d0SMichal Meloun int 299ef2ee5d0SMichal Meloun as3722_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) 300ef2ee5d0SMichal Meloun { 301ef2ee5d0SMichal Meloun struct as3722_softc *sc; 302ef2ee5d0SMichal Meloun 303ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 304ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins) 305ef2ee5d0SMichal Meloun return (EINVAL); 306ef2ee5d0SMichal Meloun GPIO_LOCK(sc); 307ef2ee5d0SMichal Meloun *caps = sc->gpio_pins[pin]->pin_caps; 308ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc); 309ef2ee5d0SMichal Meloun return (0); 310ef2ee5d0SMichal Meloun } 311ef2ee5d0SMichal Meloun 312ef2ee5d0SMichal Meloun int 313ef2ee5d0SMichal Meloun as3722_gpio_pin_getname(device_t dev, uint32_t pin, char *name) 314ef2ee5d0SMichal Meloun { 315ef2ee5d0SMichal Meloun struct as3722_softc *sc; 316ef2ee5d0SMichal Meloun 317ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 318ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins) 319ef2ee5d0SMichal Meloun return (EINVAL); 320ef2ee5d0SMichal Meloun GPIO_LOCK(sc); 321ef2ee5d0SMichal Meloun memcpy(name, sc->gpio_pins[pin]->pin_name, GPIOMAXNAME); 322ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc); 323ef2ee5d0SMichal Meloun return (0); 324ef2ee5d0SMichal Meloun } 325ef2ee5d0SMichal Meloun 326ef2ee5d0SMichal Meloun int 327ef2ee5d0SMichal Meloun as3722_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *out_flags) 328ef2ee5d0SMichal Meloun { 329ef2ee5d0SMichal Meloun struct as3722_softc *sc; 330ef2ee5d0SMichal Meloun uint8_t tmp, mode, iosf; 331ef2ee5d0SMichal Meloun uint32_t flags; 332ef2ee5d0SMichal Meloun bool inverted; 333ef2ee5d0SMichal Meloun 334ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 335ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins) 336ef2ee5d0SMichal Meloun return (EINVAL); 337ef2ee5d0SMichal Meloun 338ef2ee5d0SMichal Meloun GPIO_LOCK(sc); 339ef2ee5d0SMichal Meloun tmp = sc->gpio_pins[pin]->pin_ctrl_reg; 340ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc); 341ef2ee5d0SMichal Meloun iosf = (tmp >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK; 342ef2ee5d0SMichal Meloun mode = (tmp >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK; 343ef2ee5d0SMichal Meloun inverted = (tmp & AS3722_GPIO_INVERT) != 0; 344ef2ee5d0SMichal Meloun /* Is pin in GPIO mode ? */ 345ef2ee5d0SMichal Meloun if (iosf != AS3722_IOSF_GPIO) 346ef2ee5d0SMichal Meloun return (ENXIO); 347ef2ee5d0SMichal Meloun 348ef2ee5d0SMichal Meloun flags = 0; 349ef2ee5d0SMichal Meloun switch (mode) { 350ef2ee5d0SMichal Meloun case AS3722_MODE_INPUT: 351ef2ee5d0SMichal Meloun flags = GPIO_PIN_INPUT; 352ef2ee5d0SMichal Meloun break; 353ef2ee5d0SMichal Meloun case AS3722_MODE_PUSH_PULL: 354ef2ee5d0SMichal Meloun case AS3722_MODE_PUSH_PULL_LV: 355ef2ee5d0SMichal Meloun flags = GPIO_PIN_OUTPUT; 356ef2ee5d0SMichal Meloun break; 357ef2ee5d0SMichal Meloun case AS3722_MODE_OPEN_DRAIN: 358ef2ee5d0SMichal Meloun case AS3722_MODE_OPEN_DRAIN_LV: 359ef2ee5d0SMichal Meloun flags = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | GPIO_PIN_OPENDRAIN; 360ef2ee5d0SMichal Meloun break; 361ef2ee5d0SMichal Meloun case AS3722_MODE_TRISTATE: 362ef2ee5d0SMichal Meloun flags = GPIO_PIN_TRISTATE; 363ef2ee5d0SMichal Meloun break; 364ef2ee5d0SMichal Meloun case AS3722_MODE_INPUT_PULL_UP_LV: 365ef2ee5d0SMichal Meloun flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP; 366ef2ee5d0SMichal Meloun break; 367ef2ee5d0SMichal Meloun 368ef2ee5d0SMichal Meloun case AS3722_MODE_INPUT_PULL_DOWN: 369ef2ee5d0SMichal Meloun flags = GPIO_PIN_OUTPUT | GPIO_PIN_PULLDOWN; 370ef2ee5d0SMichal Meloun break; 371ef2ee5d0SMichal Meloun } 372ef2ee5d0SMichal Meloun if (inverted) 373ef2ee5d0SMichal Meloun flags |= GPIO_PIN_INVIN | GPIO_PIN_INVOUT; 374ef2ee5d0SMichal Meloun *out_flags = flags; 375ef2ee5d0SMichal Meloun return (0); 376ef2ee5d0SMichal Meloun } 377ef2ee5d0SMichal Meloun 378ef2ee5d0SMichal Meloun static int 379ef2ee5d0SMichal Meloun as3722_gpio_get_mode(struct as3722_softc *sc, uint32_t pin, uint32_t gpio_flags) 380ef2ee5d0SMichal Meloun { 381ef2ee5d0SMichal Meloun int flags; 382ef2ee5d0SMichal Meloun 383ef2ee5d0SMichal Meloun flags = sc->gpio_pins[pin]->pin_cfg_flags; 384ef2ee5d0SMichal Meloun 385ef2ee5d0SMichal Meloun /* Tristate mode. */ 386ef2ee5d0SMichal Meloun if (flags & AS3722_CFG_BIAS_HIGH_IMPEDANCE || 387ef2ee5d0SMichal Meloun gpio_flags & GPIO_PIN_TRISTATE) 388ef2ee5d0SMichal Meloun return (AS3722_MODE_TRISTATE); 389ef2ee5d0SMichal Meloun 390ef2ee5d0SMichal Meloun /* Open drain modes. */ 391ef2ee5d0SMichal Meloun if (flags & AS3722_CFG_OPEN_DRAIN || gpio_flags & GPIO_PIN_OPENDRAIN) { 392ef2ee5d0SMichal Meloun /* Only pull up have effect */ 393ef2ee5d0SMichal Meloun if (flags & AS3722_CFG_BIAS_PULL_UP || 394ef2ee5d0SMichal Meloun gpio_flags & GPIO_PIN_PULLUP) 395ef2ee5d0SMichal Meloun return (AS3722_MODE_OPEN_DRAIN_LV); 396ef2ee5d0SMichal Meloun return (AS3722_MODE_OPEN_DRAIN); 397ef2ee5d0SMichal Meloun } 398ef2ee5d0SMichal Meloun /* Input modes. */ 399ef2ee5d0SMichal Meloun if (gpio_flags & GPIO_PIN_INPUT) { 400ef2ee5d0SMichal Meloun /* Accept pull up or pull down. */ 401ef2ee5d0SMichal Meloun if (flags & AS3722_CFG_BIAS_PULL_UP || 402ef2ee5d0SMichal Meloun gpio_flags & GPIO_PIN_PULLUP) 403ef2ee5d0SMichal Meloun return (AS3722_MODE_INPUT_PULL_UP_LV); 404ef2ee5d0SMichal Meloun 405ef2ee5d0SMichal Meloun if (flags & AS3722_CFG_BIAS_PULL_DOWN || 406ef2ee5d0SMichal Meloun gpio_flags & GPIO_PIN_PULLDOWN) 407ef2ee5d0SMichal Meloun return (AS3722_MODE_INPUT_PULL_DOWN); 408ef2ee5d0SMichal Meloun return (AS3722_MODE_INPUT); 409ef2ee5d0SMichal Meloun } 410ef2ee5d0SMichal Meloun /* 411ef2ee5d0SMichal Meloun * Output modes. 412ef2ee5d0SMichal Meloun * Pull down is used as indicator of low voltage output. 413ef2ee5d0SMichal Meloun */ 414ef2ee5d0SMichal Meloun if (flags & AS3722_CFG_BIAS_PULL_DOWN || 415ef2ee5d0SMichal Meloun gpio_flags & GPIO_PIN_PULLDOWN) 416ef2ee5d0SMichal Meloun return (AS3722_MODE_PUSH_PULL_LV); 417ef2ee5d0SMichal Meloun return (AS3722_MODE_PUSH_PULL); 418ef2ee5d0SMichal Meloun } 419ef2ee5d0SMichal Meloun 420ef2ee5d0SMichal Meloun int 421ef2ee5d0SMichal Meloun as3722_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) 422ef2ee5d0SMichal Meloun { 423ef2ee5d0SMichal Meloun struct as3722_softc *sc; 424ef2ee5d0SMichal Meloun uint8_t ctrl, mode, iosf; 425ef2ee5d0SMichal Meloun int rv; 426ef2ee5d0SMichal Meloun 427ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 428ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins) 429ef2ee5d0SMichal Meloun return (EINVAL); 430ef2ee5d0SMichal Meloun 431ef2ee5d0SMichal Meloun GPIO_LOCK(sc); 432ef2ee5d0SMichal Meloun ctrl = sc->gpio_pins[pin]->pin_ctrl_reg; 433ef2ee5d0SMichal Meloun iosf = (ctrl >> AS3722_GPIO_IOSF_SHIFT) & AS3722_GPIO_IOSF_MASK; 434ef2ee5d0SMichal Meloun /* Is pin in GPIO mode ? */ 435ef2ee5d0SMichal Meloun if (iosf != AS3722_IOSF_GPIO) { 436ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc); 437ef2ee5d0SMichal Meloun return (ENXIO); 438ef2ee5d0SMichal Meloun } 439ef2ee5d0SMichal Meloun mode = as3722_gpio_get_mode(sc, pin, flags); 440ef2ee5d0SMichal Meloun ctrl &= ~(AS3722_GPIO_MODE_MASK << AS3722_GPIO_MODE_SHIFT); 441*dca7f696SJohn Baldwin ctrl |= mode << AS3722_GPIO_MODE_SHIFT; 442ef2ee5d0SMichal Meloun rv = 0; 443ef2ee5d0SMichal Meloun if (ctrl != sc->gpio_pins[pin]->pin_ctrl_reg) { 444ef2ee5d0SMichal Meloun rv = WR1(sc, AS3722_GPIO0_CONTROL + pin, ctrl); 445ef2ee5d0SMichal Meloun sc->gpio_pins[pin]->pin_ctrl_reg = ctrl; 446ef2ee5d0SMichal Meloun } 447ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc); 448ef2ee5d0SMichal Meloun return (rv); 449ef2ee5d0SMichal Meloun } 450ef2ee5d0SMichal Meloun 451ef2ee5d0SMichal Meloun int 452ef2ee5d0SMichal Meloun as3722_gpio_pin_set(device_t dev, uint32_t pin, uint32_t val) 453ef2ee5d0SMichal Meloun { 454ef2ee5d0SMichal Meloun struct as3722_softc *sc; 455ef2ee5d0SMichal Meloun uint8_t tmp; 456ef2ee5d0SMichal Meloun int rv; 457ef2ee5d0SMichal Meloun 458ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 459ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins) 460ef2ee5d0SMichal Meloun return (EINVAL); 461ef2ee5d0SMichal Meloun 462ef2ee5d0SMichal Meloun tmp = (val != 0) ? 1 : 0; 463ef2ee5d0SMichal Meloun if (sc->gpio_pins[pin]->pin_ctrl_reg & AS3722_GPIO_INVERT) 464ef2ee5d0SMichal Meloun tmp ^= 1; 465ef2ee5d0SMichal Meloun 466ef2ee5d0SMichal Meloun GPIO_LOCK(sc); 467ef2ee5d0SMichal Meloun rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), (tmp << pin)); 468ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc); 469ef2ee5d0SMichal Meloun return (rv); 470ef2ee5d0SMichal Meloun } 471ef2ee5d0SMichal Meloun 472ef2ee5d0SMichal Meloun int 473ef2ee5d0SMichal Meloun as3722_gpio_pin_get(device_t dev, uint32_t pin, uint32_t *val) 474ef2ee5d0SMichal Meloun { 475ef2ee5d0SMichal Meloun struct as3722_softc *sc; 476ef2ee5d0SMichal Meloun uint8_t tmp, mode, ctrl; 477ef2ee5d0SMichal Meloun int rv; 478ef2ee5d0SMichal Meloun 479ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 480ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins) 481ef2ee5d0SMichal Meloun return (EINVAL); 482ef2ee5d0SMichal Meloun 483ef2ee5d0SMichal Meloun GPIO_LOCK(sc); 484ef2ee5d0SMichal Meloun ctrl = sc->gpio_pins[pin]->pin_ctrl_reg; 485ef2ee5d0SMichal Meloun mode = (ctrl >> AS3722_GPIO_MODE_SHIFT) & AS3722_GPIO_MODE_MASK; 486ef2ee5d0SMichal Meloun if ((mode == AS3722_MODE_PUSH_PULL) || 487ef2ee5d0SMichal Meloun (mode == AS3722_MODE_PUSH_PULL_LV)) 488ef2ee5d0SMichal Meloun rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp); 489ef2ee5d0SMichal Meloun else 490ef2ee5d0SMichal Meloun rv = RD1(sc, AS3722_GPIO_SIGNAL_IN, &tmp); 491ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc); 492ef2ee5d0SMichal Meloun if (rv != 0) 493ef2ee5d0SMichal Meloun return (rv); 494ef2ee5d0SMichal Meloun 495ef2ee5d0SMichal Meloun *val = tmp & (1 << pin) ? 1 : 0; 496ef2ee5d0SMichal Meloun if (ctrl & AS3722_GPIO_INVERT) 497ef2ee5d0SMichal Meloun *val ^= 1; 498ef2ee5d0SMichal Meloun return (0); 499ef2ee5d0SMichal Meloun } 500ef2ee5d0SMichal Meloun 501ef2ee5d0SMichal Meloun int 502ef2ee5d0SMichal Meloun as3722_gpio_pin_toggle(device_t dev, uint32_t pin) 503ef2ee5d0SMichal Meloun { 504ef2ee5d0SMichal Meloun struct as3722_softc *sc; 505ef2ee5d0SMichal Meloun uint8_t tmp; 506ef2ee5d0SMichal Meloun int rv; 507ef2ee5d0SMichal Meloun 508ef2ee5d0SMichal Meloun sc = device_get_softc(dev); 509ef2ee5d0SMichal Meloun if (pin >= sc->gpio_npins) 510ef2ee5d0SMichal Meloun return (EINVAL); 511ef2ee5d0SMichal Meloun 512ef2ee5d0SMichal Meloun GPIO_LOCK(sc); 513ef2ee5d0SMichal Meloun rv = RD1(sc, AS3722_GPIO_SIGNAL_OUT, &tmp); 514ef2ee5d0SMichal Meloun if (rv != 0) { 515ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc); 516ef2ee5d0SMichal Meloun return (rv); 517ef2ee5d0SMichal Meloun } 518ef2ee5d0SMichal Meloun tmp ^= (1 <<pin); 519ef2ee5d0SMichal Meloun rv = RM1(sc, AS3722_GPIO_SIGNAL_OUT, (1 << pin), tmp); 520ef2ee5d0SMichal Meloun GPIO_UNLOCK(sc); 521ef2ee5d0SMichal Meloun return (0); 522ef2ee5d0SMichal Meloun } 523ef2ee5d0SMichal Meloun 524ef2ee5d0SMichal Meloun int 525ef2ee5d0SMichal Meloun as3722_gpio_map_gpios(device_t dev, phandle_t pdev, phandle_t gparent, 526ef2ee5d0SMichal Meloun int gcells, pcell_t *gpios, uint32_t *pin, uint32_t *flags) 527ef2ee5d0SMichal Meloun { 528ef2ee5d0SMichal Meloun 529ef2ee5d0SMichal Meloun if (gcells != 2) 530ef2ee5d0SMichal Meloun return (ERANGE); 531ef2ee5d0SMichal Meloun *pin = gpios[0]; 532ef2ee5d0SMichal Meloun *flags= gpios[1]; 533ef2ee5d0SMichal Meloun return (0); 534ef2ee5d0SMichal Meloun } 535ef2ee5d0SMichal Meloun 536ef2ee5d0SMichal Meloun int 537ef2ee5d0SMichal Meloun as3722_gpio_attach(struct as3722_softc *sc, phandle_t node) 538ef2ee5d0SMichal Meloun { 539ef2ee5d0SMichal Meloun struct as3722_gpio_pin *pin; 540ef2ee5d0SMichal Meloun int i, rv; 541ef2ee5d0SMichal Meloun 542ef2ee5d0SMichal Meloun sx_init(&sc->gpio_lock, "AS3722 GPIO lock"); 543ef2ee5d0SMichal Meloun sc->gpio_npins = NGPIO; 544ef2ee5d0SMichal Meloun sc->gpio_pins = malloc(sizeof(struct as3722_gpio_pin *) * 545ef2ee5d0SMichal Meloun sc->gpio_npins, M_AS3722_GPIO, M_WAITOK | M_ZERO); 546ef2ee5d0SMichal Meloun 547ef2ee5d0SMichal Meloun sc->gpio_busdev = gpiobus_attach_bus(sc->dev); 548ef2ee5d0SMichal Meloun if (sc->gpio_busdev == NULL) 549ef2ee5d0SMichal Meloun return (ENXIO); 550ef2ee5d0SMichal Meloun for (i = 0; i < sc->gpio_npins; i++) { 551ef2ee5d0SMichal Meloun sc->gpio_pins[i] = malloc(sizeof(struct as3722_gpio_pin), 552ef2ee5d0SMichal Meloun M_AS3722_GPIO, M_WAITOK | M_ZERO); 553ef2ee5d0SMichal Meloun pin = sc->gpio_pins[i]; 554ef2ee5d0SMichal Meloun sprintf(pin->pin_name, "gpio%d", i); 555ef2ee5d0SMichal Meloun pin->pin_caps = GPIO_PIN_INPUT | GPIO_PIN_OUTPUT | 556ef2ee5d0SMichal Meloun GPIO_PIN_OPENDRAIN | GPIO_PIN_PUSHPULL | GPIO_PIN_TRISTATE | 557ef2ee5d0SMichal Meloun GPIO_PIN_PULLUP | GPIO_PIN_PULLDOWN | GPIO_PIN_INVIN | 558ef2ee5d0SMichal Meloun GPIO_PIN_INVOUT; 559ef2ee5d0SMichal Meloun rv = RD1(sc, AS3722_GPIO0_CONTROL + i, &pin->pin_ctrl_reg); 560ef2ee5d0SMichal Meloun if (rv != 0) { 561ef2ee5d0SMichal Meloun device_printf(sc->dev, 562ef2ee5d0SMichal Meloun "Cannot read configuration for pin %s\n", 563ef2ee5d0SMichal Meloun sc->gpio_pins[i]->pin_name); 564ef2ee5d0SMichal Meloun } 565ef2ee5d0SMichal Meloun } 566ef2ee5d0SMichal Meloun return (0); 567ef2ee5d0SMichal Meloun } 568