1296936Smmel/*- 2296936Smmel * Copyright 2016 Michal Meloun <mmel@FreeBSD.org> 3296936Smmel * All rights reserved. 4296936Smmel * 5296936Smmel * Redistribution and use in source and binary forms, with or without 6296936Smmel * modification, are permitted provided that the following conditions 7296936Smmel * are met: 8296936Smmel * 1. Redistributions of source code must retain the above copyright 9296936Smmel * notice, this list of conditions and the following disclaimer. 10296936Smmel * 2. Redistributions in binary form must reproduce the above copyright 11296936Smmel * notice, this list of conditions and the following disclaimer in the 12296936Smmel * documentation and/or other materials provided with the distribution. 13296936Smmel * 14296936Smmel * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15296936Smmel * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16296936Smmel * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17296936Smmel * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18296936Smmel * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19296936Smmel * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20296936Smmel * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21296936Smmel * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22296936Smmel * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23296936Smmel * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24296936Smmel * SUCH DAMAGE. 25296936Smmel */ 26296936Smmel 27296936Smmel#include <sys/cdefs.h> 28296936Smmel__FBSDID("$FreeBSD: stable/11/sys/arm/nvidia/as3722_regulators.c 308328 2016-11-05 04:40:58Z mmel $"); 29296936Smmel 30296936Smmel#include <sys/param.h> 31296936Smmel#include <sys/systm.h> 32296936Smmel#include <sys/bus.h> 33296936Smmel#include <sys/gpio.h> 34296936Smmel#include <sys/kernel.h> 35296936Smmel#include <sys/module.h> 36296936Smmel#include <sys/malloc.h> 37296936Smmel#include <sys/rman.h> 38296936Smmel#include <sys/sx.h> 39296936Smmel 40296936Smmel#include <machine/bus.h> 41296936Smmel 42296936Smmel#include <dev/extres/regulator/regulator.h> 43296936Smmel#include <dev/gpio/gpiobusvar.h> 44296936Smmel 45296936Smmel#include <gnu/dts/include/dt-bindings/mfd/as3722.h> 46296936Smmel 47296936Smmel#include "as3722.h" 48296936Smmel 49296936SmmelMALLOC_DEFINE(M_AS3722_REG, "AS3722 regulator", "AS3722 power regulator"); 50296936Smmel 51298643Spfg#define DIV_ROUND_UP(n,d) howmany(n, d) 52296936Smmel 53296936Smmelenum as3722_reg_id { 54296936Smmel AS3722_REG_ID_SD0, 55296936Smmel AS3722_REG_ID_SD1, 56296936Smmel AS3722_REG_ID_SD2, 57296936Smmel AS3722_REG_ID_SD3, 58296936Smmel AS3722_REG_ID_SD4, 59296936Smmel AS3722_REG_ID_SD5, 60296936Smmel AS3722_REG_ID_SD6, 61296936Smmel AS3722_REG_ID_LDO0, 62296936Smmel AS3722_REG_ID_LDO1, 63296936Smmel AS3722_REG_ID_LDO2, 64296936Smmel AS3722_REG_ID_LDO3, 65296936Smmel AS3722_REG_ID_LDO4, 66296936Smmel AS3722_REG_ID_LDO5, 67296936Smmel AS3722_REG_ID_LDO6, 68296936Smmel AS3722_REG_ID_LDO7, 69296936Smmel AS3722_REG_ID_LDO9, 70296936Smmel AS3722_REG_ID_LDO10, 71296936Smmel AS3722_REG_ID_LDO11, 72296936Smmel}; 73296936Smmel 74296936Smmel 75296936Smmel/* Regulator HW definition. */ 76296936Smmelstruct reg_def { 77296936Smmel intptr_t id; /* ID */ 78296936Smmel char *name; /* Regulator name */ 79296936Smmel char *supply_name; /* Source property name */ 80296936Smmel uint8_t volt_reg; 81296936Smmel uint8_t volt_vsel_mask; 82296936Smmel uint8_t enable_reg; 83296936Smmel uint8_t enable_mask; 84296936Smmel uint8_t ext_enable_reg; 85296936Smmel uint8_t ext_enable_mask; 86296936Smmel struct regulator_range *ranges; 87296936Smmel int nranges; 88296936Smmel}; 89296936Smmel 90296936Smmelstruct as3722_reg_sc { 91296936Smmel struct regnode *regnode; 92296936Smmel struct as3722_softc *base_sc; 93296936Smmel struct reg_def *def; 94296936Smmel phandle_t xref; 95296936Smmel 96296936Smmel struct regnode_std_param *param; 97296936Smmel int ext_control; 98296936Smmel int enable_tracking; 99296936Smmel 100296936Smmel int enable_usec; 101296936Smmel}; 102296936Smmel 103296936Smmelstatic struct regulator_range as3722_sd016_ranges[] = { 104308328Smmel REG_RANGE_INIT(0x00, 0x00, 0, 0), 105308328Smmel REG_RANGE_INIT(0x01, 0x5A, 610000, 10000), 106296936Smmel}; 107296936Smmel 108296936Smmelstatic struct regulator_range as3722_sd0_lv_ranges[] = { 109308328Smmel REG_RANGE_INIT(0x00, 0x00, 0, 0), 110308328Smmel REG_RANGE_INIT(0x01, 0x6E, 410000, 10000), 111296936Smmel}; 112296936Smmel 113296936Smmelstatic struct regulator_range as3722_sd_ranges[] = { 114308328Smmel REG_RANGE_INIT(0x00, 0x00, 0, 0), 115308328Smmel REG_RANGE_INIT(0x01, 0x40, 612500, 12500), 116308328Smmel REG_RANGE_INIT(0x41, 0x70, 1425000, 25000), 117308328Smmel REG_RANGE_INIT(0x71, 0x7F, 2650000, 50000), 118296936Smmel}; 119296936Smmel 120296936Smmelstatic struct regulator_range as3722_ldo3_ranges[] = { 121308328Smmel REG_RANGE_INIT(0x00, 0x00, 0, 0), 122308328Smmel REG_RANGE_INIT(0x01, 0x2D, 620000, 20000), 123296936Smmel}; 124296936Smmel 125296936Smmelstatic struct regulator_range as3722_ldo_ranges[] = { 126308328Smmel REG_RANGE_INIT(0x00, 0x00, 0, 0), 127308328Smmel REG_RANGE_INIT(0x01, 0x24, 825000, 25000), 128308328Smmel REG_RANGE_INIT(0x40, 0x7F, 1725000, 25000), 129296936Smmel}; 130296936Smmel 131296936Smmelstatic struct reg_def as3722s_def[] = { 132296936Smmel { 133296936Smmel .id = AS3722_REG_ID_SD0, 134296936Smmel .name = "sd0", 135296936Smmel .volt_reg = AS3722_SD0_VOLTAGE, 136296936Smmel .volt_vsel_mask = AS3722_SD_VSEL_MASK, 137296936Smmel .enable_reg = AS3722_SD_CONTROL, 138296936Smmel .enable_mask = AS3722_SDN_CTRL(0), 139296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL1, 140296936Smmel .ext_enable_mask = AS3722_SD0_EXT_ENABLE_MASK, 141296936Smmel .ranges = as3722_sd016_ranges, 142296936Smmel .nranges = nitems(as3722_sd016_ranges), 143296936Smmel }, 144296936Smmel { 145296936Smmel .id = AS3722_REG_ID_SD1, 146296936Smmel .name = "sd1", 147296936Smmel .volt_reg = AS3722_SD1_VOLTAGE, 148296936Smmel .volt_vsel_mask = AS3722_SD_VSEL_MASK, 149296936Smmel .enable_reg = AS3722_SD_CONTROL, 150296936Smmel .enable_mask = AS3722_SDN_CTRL(1), 151296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL1, 152296936Smmel .ext_enable_mask = AS3722_SD1_EXT_ENABLE_MASK, 153296936Smmel .ranges = as3722_sd_ranges, 154296936Smmel .nranges = nitems(as3722_sd_ranges), 155296936Smmel }, 156296936Smmel { 157296936Smmel .id = AS3722_REG_ID_SD2, 158296936Smmel .name = "sd2", 159296936Smmel .supply_name = "vsup-sd2", 160296936Smmel .volt_reg = AS3722_SD2_VOLTAGE, 161296936Smmel .volt_vsel_mask = AS3722_SD_VSEL_MASK, 162296936Smmel .enable_reg = AS3722_SD_CONTROL, 163296936Smmel .enable_mask = AS3722_SDN_CTRL(2), 164296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL1, 165296936Smmel .ext_enable_mask = AS3722_SD2_EXT_ENABLE_MASK, 166296936Smmel .ranges = as3722_sd_ranges, 167296936Smmel .nranges = nitems(as3722_sd_ranges), 168296936Smmel }, 169296936Smmel { 170296936Smmel .id = AS3722_REG_ID_SD3, 171296936Smmel .name = "sd3", 172296936Smmel .supply_name = "vsup-sd3", 173296936Smmel .volt_reg = AS3722_SD3_VOLTAGE, 174296936Smmel .volt_vsel_mask = AS3722_SD_VSEL_MASK, 175296936Smmel .enable_reg = AS3722_SD_CONTROL, 176296936Smmel .enable_mask = AS3722_SDN_CTRL(3), 177296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL1, 178296936Smmel .ext_enable_mask = AS3722_SD3_EXT_ENABLE_MASK, 179296936Smmel .ranges = as3722_sd_ranges, 180296936Smmel .nranges = nitems(as3722_sd_ranges), 181296936Smmel }, 182296936Smmel { 183296936Smmel .id = AS3722_REG_ID_SD4, 184296936Smmel .name = "sd4", 185296936Smmel .supply_name = "vsup-sd4", 186296936Smmel .volt_reg = AS3722_SD4_VOLTAGE, 187296936Smmel .volt_vsel_mask = AS3722_SD_VSEL_MASK, 188296936Smmel .enable_reg = AS3722_SD_CONTROL, 189296936Smmel .enable_mask = AS3722_SDN_CTRL(4), 190296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL2, 191296936Smmel .ext_enable_mask = AS3722_SD4_EXT_ENABLE_MASK, 192296936Smmel .ranges = as3722_sd_ranges, 193296936Smmel .nranges = nitems(as3722_sd_ranges), 194296936Smmel }, 195296936Smmel { 196296936Smmel .id = AS3722_REG_ID_SD5, 197296936Smmel .name = "sd5", 198296936Smmel .supply_name = "vsup-sd5", 199296936Smmel .volt_reg = AS3722_SD5_VOLTAGE, 200296936Smmel .volt_vsel_mask = AS3722_SD_VSEL_MASK, 201296936Smmel .enable_reg = AS3722_SD_CONTROL, 202296936Smmel .enable_mask = AS3722_SDN_CTRL(5), 203296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL2, 204296936Smmel .ext_enable_mask = AS3722_SD5_EXT_ENABLE_MASK, 205296936Smmel .ranges = as3722_sd_ranges, 206296936Smmel .nranges = nitems(as3722_sd_ranges), 207296936Smmel }, 208296936Smmel { 209296936Smmel .id = AS3722_REG_ID_SD6, 210296936Smmel .name = "sd6", 211296936Smmel .volt_reg = AS3722_SD6_VOLTAGE, 212296936Smmel .volt_vsel_mask = AS3722_SD_VSEL_MASK, 213296936Smmel .enable_reg = AS3722_SD_CONTROL, 214296936Smmel .enable_mask = AS3722_SDN_CTRL(6), 215296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL2, 216296936Smmel .ext_enable_mask = AS3722_SD6_EXT_ENABLE_MASK, 217296936Smmel .ranges = as3722_sd016_ranges, 218296936Smmel .nranges = nitems(as3722_sd016_ranges), 219296936Smmel }, 220296936Smmel { 221296936Smmel .id = AS3722_REG_ID_LDO0, 222296936Smmel .name = "ldo0", 223296936Smmel .supply_name = "vin-ldo0", 224296936Smmel .volt_reg = AS3722_LDO0_VOLTAGE, 225296936Smmel .volt_vsel_mask = AS3722_LDO0_VSEL_MASK, 226296936Smmel .enable_reg = AS3722_LDO_CONTROL0, 227296936Smmel .enable_mask = AS3722_LDO0_CTRL, 228296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL3, 229296936Smmel .ext_enable_mask = AS3722_LDO0_EXT_ENABLE_MASK, 230296936Smmel .ranges = as3722_ldo_ranges, 231296936Smmel .nranges = nitems(as3722_ldo_ranges), 232296936Smmel }, 233296936Smmel { 234296936Smmel .id = AS3722_REG_ID_LDO1, 235296936Smmel .name = "ldo1", 236296936Smmel .supply_name = "vin-ldo1-6", 237296936Smmel .volt_reg = AS3722_LDO1_VOLTAGE, 238296936Smmel .volt_vsel_mask = AS3722_LDO_VSEL_MASK, 239296936Smmel .enable_reg = AS3722_LDO_CONTROL0, 240296936Smmel .enable_mask = AS3722_LDO1_CTRL, 241296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL3, 242296936Smmel .ext_enable_mask = AS3722_LDO1_EXT_ENABLE_MASK, 243296936Smmel .ranges = as3722_ldo_ranges, 244296936Smmel .nranges = nitems(as3722_ldo_ranges), 245296936Smmel }, 246296936Smmel { 247296936Smmel .id = AS3722_REG_ID_LDO2, 248296936Smmel .name = "ldo2", 249296936Smmel .supply_name = "vin-ldo2-5-7", 250296936Smmel .volt_reg = AS3722_LDO2_VOLTAGE, 251296936Smmel .volt_vsel_mask = AS3722_LDO_VSEL_MASK, 252296936Smmel .enable_reg = AS3722_LDO_CONTROL0, 253296936Smmel .enable_mask = AS3722_LDO2_CTRL, 254296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL3, 255296936Smmel .ext_enable_mask = AS3722_LDO2_EXT_ENABLE_MASK, 256296936Smmel .ranges = as3722_ldo_ranges, 257296936Smmel .nranges = nitems(as3722_ldo_ranges), 258296936Smmel }, 259296936Smmel { 260296936Smmel .id = AS3722_REG_ID_LDO3, 261296936Smmel .name = "ldo3", 262296936Smmel .supply_name = "vin-ldo3-4", 263296936Smmel .volt_reg = AS3722_LDO3_VOLTAGE, 264296936Smmel .volt_vsel_mask = AS3722_LDO3_VSEL_MASK, 265296936Smmel .enable_reg = AS3722_LDO_CONTROL0, 266296936Smmel .enable_mask = AS3722_LDO3_CTRL, 267296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL3, 268296936Smmel .ext_enable_mask = AS3722_LDO3_EXT_ENABLE_MASK, 269296936Smmel .ranges = as3722_ldo3_ranges, 270296936Smmel .nranges = nitems(as3722_ldo3_ranges), 271296936Smmel }, 272296936Smmel { 273296936Smmel .id = AS3722_REG_ID_LDO4, 274296936Smmel .name = "ldo4", 275296936Smmel .supply_name = "vin-ldo3-4", 276296936Smmel .volt_reg = AS3722_LDO4_VOLTAGE, 277296936Smmel .volt_vsel_mask = AS3722_LDO_VSEL_MASK, 278296936Smmel .enable_reg = AS3722_LDO_CONTROL0, 279296936Smmel .enable_mask = AS3722_LDO4_CTRL, 280296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL4, 281296936Smmel .ext_enable_mask = AS3722_LDO4_EXT_ENABLE_MASK, 282296936Smmel .ranges = as3722_ldo_ranges, 283296936Smmel .nranges = nitems(as3722_ldo_ranges), 284296936Smmel }, 285296936Smmel { 286296936Smmel .id = AS3722_REG_ID_LDO5, 287296936Smmel .name = "ldo5", 288296936Smmel .supply_name = "vin-ldo2-5-7", 289296936Smmel .volt_reg = AS3722_LDO5_VOLTAGE, 290296936Smmel .volt_vsel_mask = AS3722_LDO_VSEL_MASK, 291296936Smmel .enable_reg = AS3722_LDO_CONTROL0, 292296936Smmel .enable_mask = AS3722_LDO5_CTRL, 293296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL4, 294296936Smmel .ext_enable_mask = AS3722_LDO5_EXT_ENABLE_MASK, 295296936Smmel .ranges = as3722_ldo_ranges, 296296936Smmel .nranges = nitems(as3722_ldo_ranges), 297296936Smmel }, 298296936Smmel { 299296936Smmel .id = AS3722_REG_ID_LDO6, 300296936Smmel .name = "ldo6", 301296936Smmel .supply_name = "vin-ldo1-6", 302296936Smmel .volt_reg = AS3722_LDO6_VOLTAGE, 303296936Smmel .volt_vsel_mask = AS3722_LDO_VSEL_MASK, 304296936Smmel .enable_reg = AS3722_LDO_CONTROL0, 305296936Smmel .enable_mask = AS3722_LDO6_CTRL, 306296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL4, 307296936Smmel .ext_enable_mask = AS3722_LDO6_EXT_ENABLE_MASK, 308296936Smmel .ranges = as3722_ldo_ranges, 309296936Smmel .nranges = nitems(as3722_ldo_ranges), 310296936Smmel }, 311296936Smmel { 312296936Smmel .id = AS3722_REG_ID_LDO7, 313296936Smmel .name = "ldo7", 314296936Smmel .supply_name = "vin-ldo2-5-7", 315296936Smmel .volt_reg = AS3722_LDO7_VOLTAGE, 316296936Smmel .volt_vsel_mask = AS3722_LDO_VSEL_MASK, 317296936Smmel .enable_reg = AS3722_LDO_CONTROL0, 318296936Smmel .enable_mask = AS3722_LDO7_CTRL, 319296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL4, 320296936Smmel .ext_enable_mask = AS3722_LDO7_EXT_ENABLE_MASK, 321296936Smmel .ranges = as3722_ldo_ranges, 322296936Smmel .nranges = nitems(as3722_ldo_ranges), 323296936Smmel }, 324296936Smmel { 325296936Smmel .id = AS3722_REG_ID_LDO9, 326296936Smmel .name = "ldo9", 327296936Smmel .supply_name = "vin-ldo9-10", 328296936Smmel .volt_reg = AS3722_LDO9_VOLTAGE, 329296936Smmel .volt_vsel_mask = AS3722_LDO_VSEL_MASK, 330296936Smmel .enable_reg = AS3722_LDO_CONTROL1, 331296936Smmel .enable_mask = AS3722_LDO9_CTRL, 332296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL5, 333296936Smmel .ext_enable_mask = AS3722_LDO9_EXT_ENABLE_MASK, 334296936Smmel .ranges = as3722_ldo_ranges, 335296936Smmel .nranges = nitems(as3722_ldo_ranges), 336296936Smmel }, 337296936Smmel { 338296936Smmel .id = AS3722_REG_ID_LDO10, 339296936Smmel .name = "ldo10", 340296936Smmel .supply_name = "vin-ldo9-10", 341296936Smmel .volt_reg = AS3722_LDO10_VOLTAGE, 342296936Smmel .volt_vsel_mask = AS3722_LDO_VSEL_MASK, 343296936Smmel .enable_reg = AS3722_LDO_CONTROL1, 344296936Smmel .enable_mask = AS3722_LDO10_CTRL, 345296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL5, 346296936Smmel .ext_enable_mask = AS3722_LDO10_EXT_ENABLE_MASK, 347296936Smmel .ranges = as3722_ldo_ranges, 348296936Smmel .nranges = nitems(as3722_ldo_ranges), 349296936Smmel }, 350296936Smmel { 351296936Smmel .id = AS3722_REG_ID_LDO11, 352296936Smmel .name = "ldo11", 353296936Smmel .supply_name = "vin-ldo11", 354296936Smmel .volt_reg = AS3722_LDO11_VOLTAGE, 355296936Smmel .volt_vsel_mask = AS3722_LDO_VSEL_MASK, 356296936Smmel .enable_reg = AS3722_LDO_CONTROL1, 357296936Smmel .enable_mask = AS3722_LDO11_CTRL, 358296936Smmel .ext_enable_reg = AS3722_ENABLE_CTRL5, 359296936Smmel .ext_enable_mask = AS3722_LDO11_EXT_ENABLE_MASK, 360296936Smmel .ranges = as3722_ldo_ranges, 361296936Smmel .nranges = nitems(as3722_ldo_ranges), 362296936Smmel }, 363296936Smmel}; 364296936Smmel 365296936Smmel 366296936Smmelstruct as3722_regnode_init_def { 367296936Smmel struct regnode_init_def reg_init_def; 368296936Smmel int ext_control; 369296936Smmel int enable_tracking; 370296936Smmel}; 371296936Smmel 372296936Smmelstatic int as3722_regnode_init(struct regnode *regnode); 373296936Smmelstatic int as3722_regnode_enable(struct regnode *regnode, bool enable, 374296936Smmel int *udelay); 375296936Smmelstatic int as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt, 376296936Smmel int max_uvolt, int *udelay); 377296936Smmelstatic int as3722_regnode_get_volt(struct regnode *regnode, int *uvolt); 378296936Smmelstatic regnode_method_t as3722_regnode_methods[] = { 379296936Smmel /* Regulator interface */ 380296936Smmel REGNODEMETHOD(regnode_init, as3722_regnode_init), 381296936Smmel REGNODEMETHOD(regnode_enable, as3722_regnode_enable), 382296936Smmel REGNODEMETHOD(regnode_set_voltage, as3722_regnode_set_volt), 383296936Smmel REGNODEMETHOD(regnode_get_voltage, as3722_regnode_get_volt), 384296936Smmel REGNODEMETHOD_END 385296936Smmel}; 386296936SmmelDEFINE_CLASS_1(as3722_regnode, as3722_regnode_class, as3722_regnode_methods, 387296936Smmel sizeof(struct as3722_reg_sc), regnode_class); 388296936Smmel 389296936Smmelstatic int 390296936Smmelas3722_read_sel(struct as3722_reg_sc *sc, uint8_t *sel) 391296936Smmel{ 392296936Smmel int rv; 393296936Smmel 394296936Smmel rv = RD1(sc->base_sc, sc->def->volt_reg, sel); 395296936Smmel if (rv != 0) 396296936Smmel return (rv); 397296936Smmel *sel &= sc->def->volt_vsel_mask; 398296936Smmel *sel >>= ffs(sc->def->volt_vsel_mask) - 1; 399296936Smmel return (0); 400296936Smmel} 401296936Smmel 402296936Smmelstatic int 403296936Smmelas3722_write_sel(struct as3722_reg_sc *sc, uint8_t sel) 404296936Smmel{ 405296936Smmel int rv; 406296936Smmel 407296936Smmel sel <<= ffs(sc->def->volt_vsel_mask) - 1; 408296936Smmel sel &= sc->def->volt_vsel_mask; 409296936Smmel 410296936Smmel rv = RM1(sc->base_sc, sc->def->volt_reg, 411296936Smmel sc->def->volt_vsel_mask, sel); 412296936Smmel if (rv != 0) 413296936Smmel return (rv); 414296936Smmel return (rv); 415296936Smmel} 416296936Smmel 417296936Smmelstatic bool 418296936Smmelas3722_sd0_is_low_voltage(struct as3722_reg_sc *sc) 419296936Smmel{ 420296936Smmel uint8_t val; 421296936Smmel int rv; 422296936Smmel 423296936Smmel rv = RD1(sc->base_sc, AS3722_FUSE7, &val); 424296936Smmel if (rv != 0) 425296936Smmel return (rv); 426296936Smmel return (val & AS3722_FUSE7_SD0_LOW_VOLTAGE ? true : false); 427296936Smmel} 428296936Smmel 429296936Smmelstatic int 430296936Smmelas3722_reg_extreg_setup(struct as3722_reg_sc *sc, int ext_pwr_ctrl) 431296936Smmel{ 432296936Smmel uint8_t val; 433296936Smmel int rv; 434296936Smmel 435296936Smmel val = ext_pwr_ctrl << (ffs(sc->def->ext_enable_mask) - 1); 436296936Smmel rv = RM1(sc->base_sc, sc->def->ext_enable_reg, 437296936Smmel sc->def->ext_enable_mask, val); 438296936Smmel return (rv); 439296936Smmel} 440296936Smmel 441296936Smmelstatic int 442296936Smmelas3722_reg_enable(struct as3722_reg_sc *sc) 443296936Smmel{ 444296936Smmel int rv; 445296936Smmel 446296936Smmel rv = RM1(sc->base_sc, sc->def->enable_reg, 447296936Smmel sc->def->enable_mask, sc->def->enable_mask); 448296936Smmel return (rv); 449296936Smmel} 450296936Smmel 451296936Smmelstatic int 452296936Smmelas3722_reg_disable(struct as3722_reg_sc *sc) 453296936Smmel{ 454296936Smmel int rv; 455296936Smmel 456296936Smmel rv = RM1(sc->base_sc, sc->def->enable_reg, 457296936Smmel sc->def->enable_mask, 0); 458296936Smmel return (rv); 459296936Smmel} 460296936Smmel 461296936Smmelstatic int 462296936Smmelas3722_regnode_init(struct regnode *regnode) 463296936Smmel{ 464296936Smmel struct as3722_reg_sc *sc; 465296936Smmel int rv; 466296936Smmel 467296936Smmel sc = regnode_get_softc(regnode); 468296936Smmel 469296936Smmel sc->enable_usec = 500; 470296936Smmel if (sc->def->id == AS3722_REG_ID_SD0) { 471296936Smmel if (as3722_sd0_is_low_voltage(sc)) { 472296936Smmel sc->def->ranges = as3722_sd0_lv_ranges; 473296936Smmel sc->def->nranges = nitems(as3722_sd0_lv_ranges); 474296936Smmel } 475296936Smmel sc->enable_usec = 600; 476296936Smmel } else if (sc->def->id == AS3722_REG_ID_LDO3) { 477296936Smmel if (sc->enable_tracking) { 478296936Smmel rv = RM1(sc->base_sc, sc->def->volt_reg, 479296936Smmel AS3722_LDO3_MODE_MASK, 480296936Smmel AS3722_LDO3_MODE_PMOS_TRACKING); 481296936Smmel if (rv < 0) { 482296936Smmel device_printf(sc->base_sc->dev, 483296936Smmel "LDO3 tracking failed: %d\n", rv); 484296936Smmel return (rv); 485296936Smmel } 486296936Smmel } 487296936Smmel } 488296936Smmel 489296936Smmel if (sc->ext_control) { 490296936Smmel 491296936Smmel rv = as3722_reg_enable(sc); 492296936Smmel if (rv < 0) { 493296936Smmel device_printf(sc->base_sc->dev, 494296936Smmel "Failed to enable %s regulator: %d\n", 495296936Smmel sc->def->name, rv); 496296936Smmel return (rv); 497296936Smmel } 498296936Smmel rv = as3722_reg_extreg_setup(sc, sc->ext_control); 499296936Smmel if (rv < 0) { 500296936Smmel device_printf(sc->base_sc->dev, 501296936Smmel "%s ext control failed: %d", sc->def->name, rv); 502296936Smmel return (rv); 503296936Smmel } 504296936Smmel } 505296936Smmel return (0); 506296936Smmel} 507296936Smmel 508296936Smmelstatic void 509296936Smmelas3722_fdt_parse(struct as3722_softc *sc, phandle_t node, struct reg_def *def, 510296936Smmelstruct as3722_regnode_init_def *init_def) 511296936Smmel{ 512296936Smmel int rv; 513296936Smmel phandle_t parent, supply_node; 514296936Smmel char prop_name[64]; /* Maximum OFW property name length. */ 515296936Smmel 516296936Smmel rv = regulator_parse_ofw_stdparam(sc->dev, node, 517296936Smmel &init_def->reg_init_def); 518296936Smmel 519296936Smmel rv = OF_getencprop(node, "ams,ext-control", &init_def->ext_control, 520296936Smmel sizeof(init_def->ext_control)); 521296936Smmel if (rv <= 0) 522296936Smmel init_def->ext_control = 0; 523296936Smmel if (init_def->ext_control > 3) { 524296936Smmel device_printf(sc->dev, 525296936Smmel "Invalid value for ams,ext-control property: %d\n", 526296936Smmel init_def->ext_control); 527296936Smmel init_def->ext_control = 0; 528296936Smmel } 529296936Smmel if (OF_hasprop(node, "ams,enable-tracking")) 530296936Smmel init_def->enable_tracking = 1; 531296936Smmel 532296936Smmel 533296936Smmel /* Get parent supply. */ 534296936Smmel if (def->supply_name == NULL) 535296936Smmel return; 536296936Smmel 537296936Smmel parent = OF_parent(node); 538296936Smmel snprintf(prop_name, sizeof(prop_name), "%s-supply", 539296936Smmel def->supply_name); 540296936Smmel rv = OF_getencprop(parent, prop_name, &supply_node, 541296936Smmel sizeof(supply_node)); 542296936Smmel if (rv <= 0) 543296936Smmel return; 544296936Smmel supply_node = OF_node_from_xref(supply_node); 545296936Smmel rv = OF_getprop_alloc(supply_node, "regulator-name", 1, 546296936Smmel (void **)&init_def->reg_init_def.parent_name); 547296936Smmel if (rv <= 0) 548296936Smmel init_def->reg_init_def.parent_name = NULL; 549296936Smmel} 550296936Smmel 551296936Smmelstatic struct as3722_reg_sc * 552296936Smmelas3722_attach(struct as3722_softc *sc, phandle_t node, struct reg_def *def) 553296936Smmel{ 554296936Smmel struct as3722_reg_sc *reg_sc; 555296936Smmel struct as3722_regnode_init_def init_def; 556296936Smmel struct regnode *regnode; 557296936Smmel 558296936Smmel bzero(&init_def, sizeof(init_def)); 559296936Smmel 560296936Smmel as3722_fdt_parse(sc, node, def, &init_def); 561296936Smmel init_def.reg_init_def.id = def->id; 562296936Smmel init_def.reg_init_def.ofw_node = node; 563296936Smmel regnode = regnode_create(sc->dev, &as3722_regnode_class, 564296936Smmel &init_def.reg_init_def); 565296936Smmel if (regnode == NULL) { 566296936Smmel device_printf(sc->dev, "Cannot create regulator.\n"); 567296936Smmel return (NULL); 568296936Smmel } 569296936Smmel reg_sc = regnode_get_softc(regnode); 570296936Smmel 571296936Smmel /* Init regulator softc. */ 572296936Smmel reg_sc->regnode = regnode; 573296936Smmel reg_sc->base_sc = sc; 574296936Smmel reg_sc->def = def; 575296936Smmel reg_sc->xref = OF_xref_from_node(node); 576296936Smmel 577296936Smmel reg_sc->param = regnode_get_stdparam(regnode); 578296936Smmel reg_sc->ext_control = init_def.ext_control; 579296936Smmel reg_sc->enable_tracking = init_def.enable_tracking; 580296936Smmel 581296936Smmel regnode_register(regnode); 582296936Smmel if (bootverbose) { 583296936Smmel int volt, rv; 584296936Smmel regnode_topo_slock(); 585296936Smmel rv = regnode_get_voltage(regnode, &volt); 586296936Smmel if (rv == ENODEV) { 587296936Smmel device_printf(sc->dev, 588296936Smmel " Regulator %s: parent doesn't exist yet.\n", 589296936Smmel regnode_get_name(regnode)); 590296936Smmel } else if (rv != 0) { 591296936Smmel device_printf(sc->dev, 592296936Smmel " Regulator %s: voltage: INVALID!!!\n", 593296936Smmel regnode_get_name(regnode)); 594296936Smmel } else { 595296936Smmel device_printf(sc->dev, 596296936Smmel " Regulator %s: voltage: %d uV\n", 597296936Smmel regnode_get_name(regnode), volt); 598296936Smmel } 599296936Smmel regnode_topo_unlock(); 600296936Smmel } 601296936Smmel 602296936Smmel return (reg_sc); 603296936Smmel} 604296936Smmel 605296936Smmelint 606296936Smmelas3722_regulator_attach(struct as3722_softc *sc, phandle_t node) 607296936Smmel{ 608296936Smmel struct as3722_reg_sc *reg; 609296936Smmel phandle_t child, rnode; 610296936Smmel int i; 611296936Smmel 612296936Smmel rnode = ofw_bus_find_child(node, "regulators"); 613296936Smmel if (rnode <= 0) { 614296936Smmel device_printf(sc->dev, " Cannot find regulators subnode\n"); 615296936Smmel return (ENXIO); 616296936Smmel } 617296936Smmel 618296936Smmel sc->nregs = nitems(as3722s_def); 619296936Smmel sc->regs = malloc(sizeof(struct as3722_reg_sc *) * sc->nregs, 620296936Smmel M_AS3722_REG, M_WAITOK | M_ZERO); 621296936Smmel 622296936Smmel 623296936Smmel /* Attach all known regulators if exist in DT. */ 624296936Smmel for (i = 0; i < sc->nregs; i++) { 625296936Smmel child = ofw_bus_find_child(rnode, as3722s_def[i].name); 626296936Smmel if (child == 0) { 627296936Smmel if (bootverbose) 628296936Smmel device_printf(sc->dev, 629296936Smmel "Regulator %s missing in DT\n", 630296936Smmel as3722s_def[i].name); 631296936Smmel continue; 632296936Smmel } 633296936Smmel reg = as3722_attach(sc, child, as3722s_def + i); 634296936Smmel if (reg == NULL) { 635296936Smmel device_printf(sc->dev, "Cannot attach regulator: %s\n", 636296936Smmel as3722s_def[i].name); 637296936Smmel return (ENXIO); 638296936Smmel } 639296936Smmel sc->regs[i] = reg; 640296936Smmel } 641296936Smmel return (0); 642296936Smmel} 643296936Smmel 644296936Smmelint 645296936Smmelas3722_regulator_map(device_t dev, phandle_t xref, int ncells, 646296936Smmel pcell_t *cells, int *num) 647296936Smmel{ 648296936Smmel struct as3722_softc *sc; 649296936Smmel int i; 650296936Smmel 651296936Smmel sc = device_get_softc(dev); 652296936Smmel for (i = 0; i < sc->nregs; i++) { 653296936Smmel if (sc->regs[i] == NULL) 654296936Smmel continue; 655296936Smmel if (sc->regs[i]->xref == xref) { 656296936Smmel *num = sc->regs[i]->def->id; 657296936Smmel return (0); 658296936Smmel } 659296936Smmel } 660296936Smmel return (ENXIO); 661296936Smmel} 662296936Smmel 663296936Smmelstatic int 664296936Smmelas3722_regnode_enable(struct regnode *regnode, bool val, int *udelay) 665296936Smmel{ 666296936Smmel struct as3722_reg_sc *sc; 667296936Smmel int rv; 668296936Smmel 669296936Smmel sc = regnode_get_softc(regnode); 670296936Smmel 671296936Smmel if (val) 672296936Smmel rv = as3722_reg_enable(sc); 673296936Smmel else 674296936Smmel rv = as3722_reg_disable(sc); 675296936Smmel *udelay = sc->enable_usec; 676296936Smmel return (rv); 677296936Smmel} 678296936Smmel 679296936Smmelstatic int 680296936Smmelas3722_regnode_set_volt(struct regnode *regnode, int min_uvolt, int max_uvolt, 681296936Smmel int *udelay) 682296936Smmel{ 683296936Smmel struct as3722_reg_sc *sc; 684296936Smmel uint8_t sel; 685296936Smmel int rv; 686296936Smmel 687296936Smmel sc = regnode_get_softc(regnode); 688296936Smmel 689296936Smmel *udelay = 0; 690308328Smmel rv = regulator_range_volt_to_sel8(sc->def->ranges, sc->def->nranges, 691308328Smmel min_uvolt, max_uvolt, &sel); 692296936Smmel if (rv != 0) 693296936Smmel return (rv); 694296936Smmel rv = as3722_write_sel(sc, sel); 695296936Smmel return (rv); 696296936Smmel 697296936Smmel} 698296936Smmel 699296936Smmelstatic int 700296936Smmelas3722_regnode_get_volt(struct regnode *regnode, int *uvolt) 701296936Smmel{ 702296936Smmel struct as3722_reg_sc *sc; 703296936Smmel uint8_t sel; 704296936Smmel int rv; 705296936Smmel 706296936Smmel sc = regnode_get_softc(regnode); 707296936Smmel rv = as3722_read_sel(sc, &sel); 708296936Smmel if (rv != 0) 709296936Smmel return (rv); 710296936Smmel 711296936Smmel /* LDO6 have bypass. */ 712296936Smmel if (sc->def->id == AS3722_REG_ID_LDO6 && sel == AS3722_LDO6_SEL_BYPASS) 713296936Smmel return (ENOENT); 714308328Smmel rv = regulator_range_sel8_to_volt(sc->def->ranges, sc->def->nranges, 715308328Smmel sel, uvolt); 716296936Smmel return (rv); 717296936Smmel} 718