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