172f1cf04SJared McNeill /*- 272f1cf04SJared McNeill * Copyright (c) 2016 Jared McNeill <jmcneill@invisible.ca> 372f1cf04SJared McNeill * All rights reserved. 472f1cf04SJared McNeill * 572f1cf04SJared McNeill * Redistribution and use in source and binary forms, with or without 672f1cf04SJared McNeill * modification, are permitted provided that the following conditions 772f1cf04SJared McNeill * are met: 872f1cf04SJared McNeill * 1. Redistributions of source code must retain the above copyright 972f1cf04SJared McNeill * notice, this list of conditions and the following disclaimer. 1072f1cf04SJared McNeill * 2. Redistributions in binary form must reproduce the above copyright 1172f1cf04SJared McNeill * notice, this list of conditions and the following disclaimer in the 1272f1cf04SJared McNeill * documentation and/or other materials provided with the distribution. 1372f1cf04SJared McNeill * 1472f1cf04SJared McNeill * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 1572f1cf04SJared McNeill * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 1672f1cf04SJared McNeill * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 1772f1cf04SJared McNeill * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 1872f1cf04SJared McNeill * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 1972f1cf04SJared McNeill * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 2072f1cf04SJared McNeill * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 2172f1cf04SJared McNeill * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 2272f1cf04SJared McNeill * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2372f1cf04SJared McNeill * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2472f1cf04SJared McNeill * SUCH DAMAGE. 2572f1cf04SJared McNeill * 2672f1cf04SJared McNeill * $FreeBSD$ 2772f1cf04SJared McNeill */ 2872f1cf04SJared McNeill 2972f1cf04SJared McNeill /* 3072f1cf04SJared McNeill * GPIO controlled regulators 3172f1cf04SJared McNeill */ 3272f1cf04SJared McNeill 3372f1cf04SJared McNeill #include <sys/cdefs.h> 3472f1cf04SJared McNeill __FBSDID("$FreeBSD$"); 3572f1cf04SJared McNeill 3672f1cf04SJared McNeill #include <sys/param.h> 3772f1cf04SJared McNeill #include <sys/systm.h> 3872f1cf04SJared McNeill #include <sys/bus.h> 3972f1cf04SJared McNeill #include <sys/rman.h> 4072f1cf04SJared McNeill #include <sys/kernel.h> 4172f1cf04SJared McNeill #include <sys/module.h> 4272f1cf04SJared McNeill #include <sys/gpio.h> 4372f1cf04SJared McNeill 4472f1cf04SJared McNeill #include <dev/ofw/ofw_bus.h> 4572f1cf04SJared McNeill #include <dev/ofw/ofw_bus_subr.h> 4672f1cf04SJared McNeill 4772f1cf04SJared McNeill #include <dev/gpio/gpiobusvar.h> 4872f1cf04SJared McNeill 4972f1cf04SJared McNeill #include <dev/extres/regulator/regulator.h> 5072f1cf04SJared McNeill 5172f1cf04SJared McNeill #include "regdev_if.h" 5272f1cf04SJared McNeill 5372f1cf04SJared McNeill struct gpioregulator_state { 5472f1cf04SJared McNeill int val; 5572f1cf04SJared McNeill uint32_t mask; 5672f1cf04SJared McNeill }; 5772f1cf04SJared McNeill 5872f1cf04SJared McNeill struct gpioregulator_init_def { 5972f1cf04SJared McNeill struct regnode_init_def reg_init_def; 6072f1cf04SJared McNeill struct gpiobus_pin *enable_pin; 6172f1cf04SJared McNeill int enable_pin_valid; 6272f1cf04SJared McNeill int startup_delay_us; 6372f1cf04SJared McNeill int nstates; 6472f1cf04SJared McNeill struct gpioregulator_state *states; 6572f1cf04SJared McNeill int npins; 6672f1cf04SJared McNeill struct gpiobus_pin **pins; 6772f1cf04SJared McNeill }; 6872f1cf04SJared McNeill 6972f1cf04SJared McNeill struct gpioregulator_reg_sc { 7072f1cf04SJared McNeill struct regnode *regnode; 7172f1cf04SJared McNeill device_t base_dev; 7272f1cf04SJared McNeill struct regnode_std_param *param; 7372f1cf04SJared McNeill struct gpioregulator_init_def *def; 7472f1cf04SJared McNeill }; 7572f1cf04SJared McNeill 7672f1cf04SJared McNeill struct gpioregulator_softc { 7772f1cf04SJared McNeill device_t dev; 7872f1cf04SJared McNeill struct gpioregulator_reg_sc *reg_sc; 7972f1cf04SJared McNeill struct gpioregulator_init_def init_def; 8072f1cf04SJared McNeill }; 8172f1cf04SJared McNeill 8272f1cf04SJared McNeill static int 8372f1cf04SJared McNeill gpioregulator_regnode_init(struct regnode *regnode) 8472f1cf04SJared McNeill { 8572f1cf04SJared McNeill struct gpioregulator_reg_sc *sc; 8672f1cf04SJared McNeill int error, n; 8772f1cf04SJared McNeill 8872f1cf04SJared McNeill sc = regnode_get_softc(regnode); 8972f1cf04SJared McNeill 9072f1cf04SJared McNeill if (sc->def->enable_pin_valid == 1) { 9172f1cf04SJared McNeill error = gpio_pin_setflags(sc->def->enable_pin, GPIO_PIN_OUTPUT); 9272f1cf04SJared McNeill if (error != 0) 9372f1cf04SJared McNeill return (error); 9472f1cf04SJared McNeill } 9572f1cf04SJared McNeill 9672f1cf04SJared McNeill for (n = 0; n < sc->def->npins; n++) { 9772f1cf04SJared McNeill error = gpio_pin_setflags(sc->def->pins[n], GPIO_PIN_OUTPUT); 9872f1cf04SJared McNeill if (error != 0) 9972f1cf04SJared McNeill return (error); 10072f1cf04SJared McNeill } 10172f1cf04SJared McNeill 10272f1cf04SJared McNeill return (0); 10372f1cf04SJared McNeill } 10472f1cf04SJared McNeill 10572f1cf04SJared McNeill static int 10672f1cf04SJared McNeill gpioregulator_regnode_enable(struct regnode *regnode, bool enable, int *udelay) 10772f1cf04SJared McNeill { 10872f1cf04SJared McNeill struct gpioregulator_reg_sc *sc; 10972f1cf04SJared McNeill bool active; 11072f1cf04SJared McNeill int error; 11172f1cf04SJared McNeill 11272f1cf04SJared McNeill sc = regnode_get_softc(regnode); 11372f1cf04SJared McNeill 11472f1cf04SJared McNeill if (sc->def->enable_pin_valid == 1) { 11572f1cf04SJared McNeill active = enable; 11672f1cf04SJared McNeill if (!sc->param->enable_active_high) 11772f1cf04SJared McNeill active = !active; 11872f1cf04SJared McNeill error = gpio_pin_set_active(sc->def->enable_pin, active); 11972f1cf04SJared McNeill if (error != 0) 12072f1cf04SJared McNeill return (error); 12172f1cf04SJared McNeill } 12272f1cf04SJared McNeill 12372f1cf04SJared McNeill *udelay = sc->def->startup_delay_us; 12472f1cf04SJared McNeill 12572f1cf04SJared McNeill return (0); 12672f1cf04SJared McNeill } 12772f1cf04SJared McNeill 12872f1cf04SJared McNeill static int 12972f1cf04SJared McNeill gpioregulator_regnode_set_voltage(struct regnode *regnode, int min_uvolt, 13072f1cf04SJared McNeill int max_uvolt, int *udelay) 13172f1cf04SJared McNeill { 13272f1cf04SJared McNeill struct gpioregulator_reg_sc *sc; 13372f1cf04SJared McNeill const struct gpioregulator_state *state; 13472f1cf04SJared McNeill int error, n; 13572f1cf04SJared McNeill 13672f1cf04SJared McNeill sc = regnode_get_softc(regnode); 13772f1cf04SJared McNeill state = NULL; 13872f1cf04SJared McNeill 13972f1cf04SJared McNeill for (n = 0; n < sc->def->nstates; n++) { 14072f1cf04SJared McNeill if (sc->def->states[n].val >= min_uvolt && 14172f1cf04SJared McNeill sc->def->states[n].val <= max_uvolt) { 14272f1cf04SJared McNeill state = &sc->def->states[n]; 14372f1cf04SJared McNeill break; 14472f1cf04SJared McNeill } 14572f1cf04SJared McNeill } 14672f1cf04SJared McNeill if (state == NULL) 14772f1cf04SJared McNeill return (EINVAL); 14872f1cf04SJared McNeill 14972f1cf04SJared McNeill for (n = 0; n < sc->def->npins; n++) { 15072f1cf04SJared McNeill error = gpio_pin_set_active(sc->def->pins[n], 15172f1cf04SJared McNeill (state->mask >> n) & 1); 15272f1cf04SJared McNeill if (error != 0) 15372f1cf04SJared McNeill return (error); 15472f1cf04SJared McNeill } 15572f1cf04SJared McNeill 15672f1cf04SJared McNeill *udelay = sc->def->startup_delay_us; 15772f1cf04SJared McNeill 15872f1cf04SJared McNeill return (0); 15972f1cf04SJared McNeill } 16072f1cf04SJared McNeill 16172f1cf04SJared McNeill static int 16272f1cf04SJared McNeill gpioregulator_regnode_get_voltage(struct regnode *regnode, int *uvolt) 16372f1cf04SJared McNeill { 16472f1cf04SJared McNeill struct gpioregulator_reg_sc *sc; 16572f1cf04SJared McNeill uint32_t mask; 16672f1cf04SJared McNeill int error, n; 16772f1cf04SJared McNeill bool active; 16872f1cf04SJared McNeill 16972f1cf04SJared McNeill sc = regnode_get_softc(regnode); 17072f1cf04SJared McNeill mask = 0; 17172f1cf04SJared McNeill 17272f1cf04SJared McNeill for (n = 0; n < sc->def->npins; n++) { 17372f1cf04SJared McNeill error = gpio_pin_is_active(sc->def->pins[n], &active); 17472f1cf04SJared McNeill if (error != 0) 17572f1cf04SJared McNeill return (error); 17672f1cf04SJared McNeill mask |= (active << n); 17772f1cf04SJared McNeill } 17872f1cf04SJared McNeill 17972f1cf04SJared McNeill for (n = 0; n < sc->def->nstates; n++) { 18072f1cf04SJared McNeill if (sc->def->states[n].mask == mask) { 18172f1cf04SJared McNeill *uvolt = sc->def->states[n].val; 18272f1cf04SJared McNeill return (0); 18372f1cf04SJared McNeill } 18472f1cf04SJared McNeill } 18572f1cf04SJared McNeill 18672f1cf04SJared McNeill return (EIO); 18772f1cf04SJared McNeill } 18872f1cf04SJared McNeill 18972f1cf04SJared McNeill static regnode_method_t gpioregulator_regnode_methods[] = { 19072f1cf04SJared McNeill /* Regulator interface */ 19172f1cf04SJared McNeill REGNODEMETHOD(regnode_init, gpioregulator_regnode_init), 19272f1cf04SJared McNeill REGNODEMETHOD(regnode_enable, gpioregulator_regnode_enable), 19372f1cf04SJared McNeill REGNODEMETHOD(regnode_set_voltage, gpioregulator_regnode_set_voltage), 19472f1cf04SJared McNeill REGNODEMETHOD(regnode_get_voltage, gpioregulator_regnode_get_voltage), 19572f1cf04SJared McNeill REGNODEMETHOD_END 19672f1cf04SJared McNeill }; 19772f1cf04SJared McNeill DEFINE_CLASS_1(gpioregulator_regnode, gpioregulator_regnode_class, 19872f1cf04SJared McNeill gpioregulator_regnode_methods, sizeof(struct gpioregulator_reg_sc), 19972f1cf04SJared McNeill regnode_class); 20072f1cf04SJared McNeill 20172f1cf04SJared McNeill static int 20272f1cf04SJared McNeill gpioregulator_parse_fdt(struct gpioregulator_softc *sc) 20372f1cf04SJared McNeill { 20472f1cf04SJared McNeill uint32_t *pstates, mask; 20572f1cf04SJared McNeill phandle_t node; 20672f1cf04SJared McNeill ssize_t len; 20772f1cf04SJared McNeill int error, n; 20872f1cf04SJared McNeill 20972f1cf04SJared McNeill node = ofw_bus_get_node(sc->dev); 21072f1cf04SJared McNeill pstates = NULL; 21172f1cf04SJared McNeill mask = 0; 21272f1cf04SJared McNeill 21372f1cf04SJared McNeill error = regulator_parse_ofw_stdparam(sc->dev, node, 21472f1cf04SJared McNeill &sc->init_def.reg_init_def); 21572f1cf04SJared McNeill if (error != 0) 21672f1cf04SJared McNeill return (error); 21772f1cf04SJared McNeill 21872f1cf04SJared McNeill /* "states" property (required) */ 219f7604b1bSOleksandr Tymoshenko len = OF_getencprop_alloc_multi(node, "states", sizeof(*pstates), 22072f1cf04SJared McNeill (void **)&pstates); 22172f1cf04SJared McNeill if (len < 2) { 22272f1cf04SJared McNeill device_printf(sc->dev, "invalid 'states' property\n"); 22372f1cf04SJared McNeill error = EINVAL; 22472f1cf04SJared McNeill goto done; 22572f1cf04SJared McNeill } 22672f1cf04SJared McNeill sc->init_def.nstates = len / 2; 22772f1cf04SJared McNeill sc->init_def.states = malloc(sc->init_def.nstates * 22872f1cf04SJared McNeill sizeof(*sc->init_def.states), M_DEVBUF, M_WAITOK); 22972f1cf04SJared McNeill for (n = 0; n < sc->init_def.nstates; n++) { 23072f1cf04SJared McNeill sc->init_def.states[n].val = pstates[n * 2 + 0]; 23172f1cf04SJared McNeill sc->init_def.states[n].mask = pstates[n * 2 + 1]; 23272f1cf04SJared McNeill mask |= sc->init_def.states[n].mask; 23372f1cf04SJared McNeill } 23472f1cf04SJared McNeill 23572f1cf04SJared McNeill /* "startup-delay-us" property (optional) */ 23672f1cf04SJared McNeill len = OF_getencprop(node, "startup-delay-us", 23772f1cf04SJared McNeill &sc->init_def.startup_delay_us, 23872f1cf04SJared McNeill sizeof(sc->init_def.startup_delay_us)); 23972f1cf04SJared McNeill if (len <= 0) 24072f1cf04SJared McNeill sc->init_def.startup_delay_us = 0; 24172f1cf04SJared McNeill 24272f1cf04SJared McNeill /* "enable-gpio" property (optional) */ 24372f1cf04SJared McNeill error = gpio_pin_get_by_ofw_property(sc->dev, node, "enable-gpio", 24472f1cf04SJared McNeill &sc->init_def.enable_pin); 24572f1cf04SJared McNeill if (error == 0) 24672f1cf04SJared McNeill sc->init_def.enable_pin_valid = 1; 24772f1cf04SJared McNeill 24872f1cf04SJared McNeill /* "gpios" property */ 24972f1cf04SJared McNeill sc->init_def.npins = 32 - __builtin_clz(mask); 25072f1cf04SJared McNeill sc->init_def.pins = malloc(sc->init_def.npins * 251*e5b6bcc7SLuiz Otavio O Souza sizeof(sc->init_def.pins), M_DEVBUF, M_WAITOK | M_ZERO); 25272f1cf04SJared McNeill for (n = 0; n < sc->init_def.npins; n++) { 25372f1cf04SJared McNeill error = gpio_pin_get_by_ofw_idx(sc->dev, node, n, 25472f1cf04SJared McNeill &sc->init_def.pins[n]); 25572f1cf04SJared McNeill if (error != 0) { 25672f1cf04SJared McNeill device_printf(sc->dev, "cannot get pin %d\n", n); 25772f1cf04SJared McNeill goto done; 25872f1cf04SJared McNeill } 25972f1cf04SJared McNeill } 26072f1cf04SJared McNeill 26172f1cf04SJared McNeill done: 26272f1cf04SJared McNeill if (error != 0) { 26372f1cf04SJared McNeill for (n = 0; n < sc->init_def.npins; n++) { 26472f1cf04SJared McNeill if (sc->init_def.pins[n] != NULL) 26572f1cf04SJared McNeill gpio_pin_release(sc->init_def.pins[n]); 26672f1cf04SJared McNeill } 26772f1cf04SJared McNeill 26872f1cf04SJared McNeill free(sc->init_def.states, M_DEVBUF); 26972f1cf04SJared McNeill free(sc->init_def.pins, M_DEVBUF); 27072f1cf04SJared McNeill 27172f1cf04SJared McNeill } 27272f1cf04SJared McNeill OF_prop_free(pstates); 27372f1cf04SJared McNeill 27472f1cf04SJared McNeill return (error); 27572f1cf04SJared McNeill } 27672f1cf04SJared McNeill 27772f1cf04SJared McNeill static int 27872f1cf04SJared McNeill gpioregulator_probe(device_t dev) 27972f1cf04SJared McNeill { 28072f1cf04SJared McNeill 28172f1cf04SJared McNeill if (!ofw_bus_is_compatible(dev, "regulator-gpio")) 28272f1cf04SJared McNeill return (ENXIO); 28372f1cf04SJared McNeill 28472f1cf04SJared McNeill device_set_desc(dev, "GPIO controlled regulator"); 28572f1cf04SJared McNeill return (BUS_PROBE_GENERIC); 28672f1cf04SJared McNeill } 28772f1cf04SJared McNeill 28872f1cf04SJared McNeill static int 28972f1cf04SJared McNeill gpioregulator_attach(device_t dev) 29072f1cf04SJared McNeill { 29172f1cf04SJared McNeill struct gpioregulator_softc *sc; 29272f1cf04SJared McNeill struct regnode *regnode; 29372f1cf04SJared McNeill phandle_t node; 29472f1cf04SJared McNeill int error; 29572f1cf04SJared McNeill 29672f1cf04SJared McNeill sc = device_get_softc(dev); 29772f1cf04SJared McNeill sc->dev = dev; 29872f1cf04SJared McNeill node = ofw_bus_get_node(dev); 29972f1cf04SJared McNeill 30072f1cf04SJared McNeill error = gpioregulator_parse_fdt(sc); 30172f1cf04SJared McNeill if (error != 0) { 30272f1cf04SJared McNeill device_printf(dev, "cannot parse parameters\n"); 30372f1cf04SJared McNeill return (ENXIO); 30472f1cf04SJared McNeill } 30572f1cf04SJared McNeill sc->init_def.reg_init_def.id = 1; 30672f1cf04SJared McNeill sc->init_def.reg_init_def.ofw_node = node; 30772f1cf04SJared McNeill 30872f1cf04SJared McNeill regnode = regnode_create(dev, &gpioregulator_regnode_class, 30972f1cf04SJared McNeill &sc->init_def.reg_init_def); 31072f1cf04SJared McNeill if (regnode == NULL) { 31172f1cf04SJared McNeill device_printf(dev, "cannot create regulator\n"); 31272f1cf04SJared McNeill return (ENXIO); 31372f1cf04SJared McNeill } 31472f1cf04SJared McNeill 31572f1cf04SJared McNeill sc->reg_sc = regnode_get_softc(regnode); 31672f1cf04SJared McNeill sc->reg_sc->regnode = regnode; 31772f1cf04SJared McNeill sc->reg_sc->base_dev = dev; 31872f1cf04SJared McNeill sc->reg_sc->param = regnode_get_stdparam(regnode); 31972f1cf04SJared McNeill sc->reg_sc->def = &sc->init_def; 32072f1cf04SJared McNeill 32172f1cf04SJared McNeill regnode_register(regnode); 32272f1cf04SJared McNeill 32372f1cf04SJared McNeill return (0); 32472f1cf04SJared McNeill } 32572f1cf04SJared McNeill 32672f1cf04SJared McNeill 32772f1cf04SJared McNeill static device_method_t gpioregulator_methods[] = { 32872f1cf04SJared McNeill /* Device interface */ 32972f1cf04SJared McNeill DEVMETHOD(device_probe, gpioregulator_probe), 33072f1cf04SJared McNeill DEVMETHOD(device_attach, gpioregulator_attach), 33172f1cf04SJared McNeill 33272f1cf04SJared McNeill /* Regdev interface */ 33372f1cf04SJared McNeill DEVMETHOD(regdev_map, regdev_default_ofw_map), 33472f1cf04SJared McNeill 33572f1cf04SJared McNeill DEVMETHOD_END 33672f1cf04SJared McNeill }; 33772f1cf04SJared McNeill 33872f1cf04SJared McNeill static driver_t gpioregulator_driver = { 33972f1cf04SJared McNeill "gpioregulator", 34072f1cf04SJared McNeill gpioregulator_methods, 34172f1cf04SJared McNeill sizeof(struct gpioregulator_softc), 34272f1cf04SJared McNeill }; 34372f1cf04SJared McNeill 34472f1cf04SJared McNeill static devclass_t gpioregulator_devclass; 34572f1cf04SJared McNeill 34672f1cf04SJared McNeill EARLY_DRIVER_MODULE(gpioregulator, simplebus, gpioregulator_driver, 34772f1cf04SJared McNeill gpioregulator_devclass, 0, 0, BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LAST); 34872f1cf04SJared McNeill MODULE_VERSION(gpioregulator, 1); 349