From owner-svn-src-all@freebsd.org Mon Oct 24 21:31:49 2016 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 14EE8C2081A; Mon, 24 Oct 2016 21:31:49 +0000 (UTC) (envelope-from manu@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mx1.freebsd.org (Postfix) with ESMTPS id E0449F15; Mon, 24 Oct 2016 21:31:48 +0000 (UTC) (envelope-from manu@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id u9OLVm37022300; Mon, 24 Oct 2016 21:31:48 GMT (envelope-from manu@FreeBSD.org) Received: (from manu@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id u9OLVlVX022298; Mon, 24 Oct 2016 21:31:47 GMT (envelope-from manu@FreeBSD.org) Message-Id: <201610242131.u9OLVlVX022298@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: manu set sender to manu@FreeBSD.org using -f From: Emmanuel Vadot Date: Mon, 24 Oct 2016 21:31:47 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-11@freebsd.org Subject: svn commit: r307885 - stable/11/sys/arm/allwinner X-SVN-Group: stable-11 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.23 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 24 Oct 2016 21:31:49 -0000 Author: manu Date: Mon Oct 24 21:31:47 2016 New Revision: 307885 URL: https://svnweb.freebsd.org/changeset/base/307885 Log: MFC r307379: axp209: Add support for regulators Except for LDO4, all regulators are supported. Modified: stable/11/sys/arm/allwinner/axp209.c stable/11/sys/arm/allwinner/axp209reg.h Directory Properties: stable/11/ (props changed) Modified: stable/11/sys/arm/allwinner/axp209.c ============================================================================== --- stable/11/sys/arm/allwinner/axp209.c Mon Oct 24 21:16:21 2016 (r307884) +++ stable/11/sys/arm/allwinner/axp209.c Mon Oct 24 21:31:47 2016 (r307885) @@ -50,14 +50,92 @@ __FBSDID("$FreeBSD$"); #include -#include #include #include +#include + #include #include "iicbus_if.h" #include "gpio_if.h" +#include "regdev_if.h" + +MALLOC_DEFINE(M_AXP209_REG, "Axp209 regulator", "Axp209 power regulator"); + +struct axp209_regdef { + intptr_t id; + char *name; + uint8_t enable_reg; + uint8_t enable_mask; + uint8_t voltage_reg; + uint8_t voltage_mask; + uint8_t voltage_shift; + int voltage_min; + int voltage_max; + int voltage_step; + int voltage_nstep; +}; + +static struct axp209_regdef axp209_regdefs[] = { + { + .id = AXP209_REG_ID_DCDC2, + .name = "dcdc2", + .enable_reg = AXP209_POWERCTL, + .enable_mask = AXP209_POWERCTL_DCDC2, + .voltage_reg = AXP209_REG_DCDC2_VOLTAGE, + .voltage_mask = 0x3f, + .voltage_min = 700, + .voltage_max = 2275, + .voltage_step = 25, + .voltage_nstep = 64, + }, + { + .id = AXP209_REG_ID_DCDC3, + .name = "dcdc3", + .enable_reg = AXP209_POWERCTL, + .enable_mask = AXP209_POWERCTL_DCDC3, + .voltage_reg = AXP209_REG_DCDC3_VOLTAGE, + .voltage_mask = 0x7f, + .voltage_min = 700, + .voltage_max = 3500, + .voltage_step = 25, + .voltage_nstep = 128, + }, + { + .id = AXP209_REG_ID_LDO2, + .name = "ldo2", + .enable_reg = AXP209_POWERCTL, + .enable_mask = AXP209_POWERCTL_LDO2, + .voltage_reg = AXP209_REG_LDO24_VOLTAGE, + .voltage_mask = 0xf0, + .voltage_shift = 4, + .voltage_min = 1800, + .voltage_max = 3300, + .voltage_step = 100, + .voltage_nstep = 16, + }, + { + .id = AXP209_REG_ID_LDO3, + .name = "ldo3", + .enable_reg = AXP209_POWERCTL, + .enable_mask = AXP209_POWERCTL_LDO3, + .voltage_reg = AXP209_REG_LDO3_VOLTAGE, + .voltage_mask = 0x7f, + .voltage_min = 700, + .voltage_max = 2275, + .voltage_step = 25, + .voltage_nstep = 128, + }, +}; + +struct axp209_reg_sc { + struct regnode *regnode; + device_t base_dev; + struct axp209_regdef *def; + phandle_t xref; + struct regnode_std_param *param; +}; struct axp209_softc { device_t dev; @@ -67,6 +145,10 @@ struct axp209_softc { struct intr_config_hook intr_hook; device_t gpiodev; struct mtx mtx; + + /* Regulators */ + struct axp209_reg_sc **regs; + int nregs; }; /* GPIO3 is different, don't expose it for now */ @@ -125,6 +207,115 @@ axp209_write(device_t dev, uint8_t reg, } static int +axp209_regnode_init(struct regnode *regnode) +{ + return (0); +} + +static int +axp209_regnode_enable(struct regnode *regnode, bool enable, int *udelay) +{ + struct axp209_reg_sc *sc; + uint8_t val; + + sc = regnode_get_softc(regnode); + + axp209_read(sc->base_dev, sc->def->enable_reg, &val, 1); + if (enable) + val |= sc->def->enable_mask; + else + val &= ~sc->def->enable_mask; + axp209_write(sc->base_dev, sc->def->enable_reg, val); + + *udelay = 0; + + return (0); +} + +static void +axp209_regnode_reg_to_voltage(struct axp209_reg_sc *sc, uint8_t val, int *uv) +{ + if (val < sc->def->voltage_nstep) + *uv = sc->def->voltage_min + val * sc->def->voltage_step; + else + *uv = sc->def->voltage_min + + (sc->def->voltage_nstep * sc->def->voltage_step); + *uv *= 1000; +} + +static int +axp209_regnode_voltage_to_reg(struct axp209_reg_sc *sc, int min_uvolt, + int max_uvolt, uint8_t *val) +{ + uint8_t nval; + int nstep, uvolt; + + nval = 0; + uvolt = sc->def->voltage_min * 1000; + + for (nstep = 0; nstep < sc->def->voltage_nstep && uvolt < min_uvolt; + nstep++) { + ++nval; + uvolt += (sc->def->voltage_step * 1000); + } + if (uvolt > max_uvolt) + return (EINVAL); + + *val = nval; + return (0); +} + +static int +axp209_regnode_set_voltage(struct regnode *regnode, int min_uvolt, + int max_uvolt, int *udelay) +{ + struct axp209_reg_sc *sc; + uint8_t val; + + sc = regnode_get_softc(regnode); + + if (!sc->def->voltage_step) + return (ENXIO); + + if (axp209_regnode_voltage_to_reg(sc, min_uvolt, max_uvolt, &val) != 0) + return (ERANGE); + + axp209_write(sc->base_dev, sc->def->voltage_reg, val); + + *udelay = 0; + + return (0); +} + +static int +axp209_regnode_get_voltage(struct regnode *regnode, int *uvolt) +{ + struct axp209_reg_sc *sc; + uint8_t val; + + sc = regnode_get_softc(regnode); + + if (!sc->def->voltage_step) + return (ENXIO); + + axp209_read(sc->base_dev, sc->def->voltage_reg, &val, 1); + axp209_regnode_reg_to_voltage(sc, val & sc->def->voltage_mask, uvolt); + + return (0); +} + +static regnode_method_t axp209_regnode_methods[] = { + /* Regulator interface */ + REGNODEMETHOD(regnode_init, axp209_regnode_init), + REGNODEMETHOD(regnode_enable, axp209_regnode_enable), + REGNODEMETHOD(regnode_set_voltage, axp209_regnode_set_voltage), + REGNODEMETHOD(regnode_get_voltage, axp209_regnode_get_voltage), + REGNODEMETHOD_END +}; +DEFINE_CLASS_1(axp209_regnode, axp209_regnode_class, axp209_regnode_methods, + sizeof(struct axp209_reg_sc), regnode_class); + +static int axp209_sysctl(SYSCTL_HANDLER_ARGS) { device_t dev = arg1; @@ -517,6 +708,63 @@ axp209_get_node(device_t dev, device_t b return (ofw_bus_get_node(dev)); } +static struct axp209_reg_sc * +axp209_reg_attach(device_t dev, phandle_t node, + struct axp209_regdef *def) +{ + struct axp209_reg_sc *reg_sc; + struct regnode_init_def initdef; + struct regnode *regnode; + + memset(&initdef, 0, sizeof(initdef)); + if (regulator_parse_ofw_stdparam(dev, node, &initdef) != 0) { + device_printf(dev, "cannot create regulator\n"); + return (NULL); + } + if (initdef.std_param.min_uvolt == 0) + initdef.std_param.min_uvolt = def->voltage_min * 1000; + if (initdef.std_param.max_uvolt == 0) + initdef.std_param.max_uvolt = def->voltage_max * 1000; + initdef.id = def->id; + initdef.ofw_node = node; + regnode = regnode_create(dev, &axp209_regnode_class, &initdef); + if (regnode == NULL) { + device_printf(dev, "cannot create regulator\n"); + return (NULL); + } + + reg_sc = regnode_get_softc(regnode); + reg_sc->regnode = regnode; + reg_sc->base_dev = dev; + reg_sc->def = def; + reg_sc->xref = OF_xref_from_node(node); + reg_sc->param = regnode_get_stdparam(regnode); + + regnode_register(regnode); + + return (reg_sc); +} + +static int +axp209_regdev_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, + intptr_t *num) +{ + struct axp209_softc *sc; + int i; + + sc = device_get_softc(dev); + for (i = 0; i < sc->nregs; i++) { + if (sc->regs[i] == NULL) + continue; + if (sc->regs[i]->xref == xref) { + *num = sc->regs[i]->def->id; + return (0); + } + } + + return (ENXIO); +} + static void axp209_start(void *pdev) { @@ -654,6 +902,9 @@ static int axp209_attach(device_t dev) { struct axp209_softc *sc; + struct axp209_reg_sc *reg; + phandle_t rnode, child; + int i; sc = device_get_softc(dev); mtx_init(&sc->mtx, device_get_nameunit(dev), NULL, MTX_DEF); @@ -669,6 +920,29 @@ axp209_attach(device_t dev) if (config_intrhook_establish(&sc->intr_hook) != 0) return (ENOMEM); + sc->nregs = nitems(axp209_regdefs); + sc->regs = malloc(sizeof(struct axp209_reg_sc *) * sc->nregs, + M_AXP209_REG, M_WAITOK | M_ZERO); + + /* Attach known regulators that exist in the DT */ + rnode = ofw_bus_find_child(ofw_bus_get_node(dev), "regulators"); + if (rnode > 0) { + for (i = 0; i < sc->nregs; i++) { + child = ofw_bus_find_child(rnode, + axp209_regdefs[i].name); + if (child == 0) + continue; + reg = axp209_reg_attach(dev, child, &axp209_regdefs[i]); + if (reg == NULL) { + device_printf(dev, + "cannot attach regulator %s\n", + axp209_regdefs[i].name); + continue; + } + sc->regs[i] = reg; + } + } + sc->gpiodev = gpiobus_attach_bus(dev); return (0); @@ -690,6 +964,9 @@ static device_method_t axp209_methods[] DEVMETHOD(gpio_pin_toggle, axp209_gpio_pin_toggle), DEVMETHOD(gpio_map_gpios, axp209_gpio_map_gpios), + /* Regdev interface */ + DEVMETHOD(regdev_map, axp209_regdev_map), + /* OFW bus interface */ DEVMETHOD(ofw_bus_get_node, axp209_get_node), Modified: stable/11/sys/arm/allwinner/axp209reg.h ============================================================================== --- stable/11/sys/arm/allwinner/axp209reg.h Mon Oct 24 21:16:21 2016 (r307884) +++ stable/11/sys/arm/allwinner/axp209reg.h Mon Oct 24 21:31:47 2016 (r307885) @@ -146,6 +146,19 @@ #define AXP209_GPIO_DATA(x) (1 << (x + 4)) +/* Regulators registers */ +#define AXP209_POWERCTL 0x12 +#define AXP209_POWERCTL_DCDC3 (1 << 1) +#define AXP209_POWERCTL_LDO2 (1 << 2) +#define AXP209_POWERCTL_LDO4 (1 << 3) +#define AXP209_POWERCTL_DCDC2 (1 << 4) +#define AXP209_POWERCTL_LDO3 (1 << 6) + +#define AXP209_REG_DCDC2_VOLTAGE 0x23 +#define AXP209_REG_DCDC3_VOLTAGE 0x27 +#define AXP209_REG_LDO24_VOLTAGE 0x28 +#define AXP209_REG_LDO3_VOLTAGE 0x29 + enum axp209_sensor { AXP209_TEMP, AXP209_ACVOLT, @@ -157,4 +170,15 @@ enum axp209_sensor { AXP209_BATDISCHARGECURRENT, }; +enum axp209_regulators { + AXP209_REG_ID_DCDC2, + AXP209_REG_ID_DCDC3, + AXP209_REG_ID_LDO1, + AXP209_REG_ID_LDO2, + AXP209_REG_ID_LDO3, + /* LDO4 is weird, need to find a correct way to handle it */ + /* AXP209_REG_ID_LDO4, */ + AXP209_REG_ID_LDO5, +}; + #endif /* _AXP209REG_H_ */