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