Index: head/sys/arm/nvidia/as3722_regulators.c =================================================================== --- head/sys/arm/nvidia/as3722_regulators.c (revision 307557) +++ head/sys/arm/nvidia/as3722_regulators.c (revision 307558) @@ -1,811 +1,717 @@ /*- * Copyright 2016 Michal Meloun * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include "as3722.h" MALLOC_DEFINE(M_AS3722_REG, "AS3722 regulator", "AS3722 power regulator"); #define DIV_ROUND_UP(n,d) howmany(n, d) enum as3722_reg_id { AS3722_REG_ID_SD0, AS3722_REG_ID_SD1, AS3722_REG_ID_SD2, AS3722_REG_ID_SD3, AS3722_REG_ID_SD4, AS3722_REG_ID_SD5, AS3722_REG_ID_SD6, AS3722_REG_ID_LDO0, AS3722_REG_ID_LDO1, AS3722_REG_ID_LDO2, AS3722_REG_ID_LDO3, AS3722_REG_ID_LDO4, AS3722_REG_ID_LDO5, AS3722_REG_ID_LDO6, AS3722_REG_ID_LDO7, AS3722_REG_ID_LDO9, AS3722_REG_ID_LDO10, AS3722_REG_ID_LDO11, }; -struct regulator_range { - u_int min_uvolt; - u_int step_uvolt; - u_int min_sel; - u_int max_sel; -}; - /* Regulator HW definition. */ struct reg_def { intptr_t id; /* ID */ char *name; /* Regulator name */ char *supply_name; /* Source property name */ uint8_t volt_reg; uint8_t volt_vsel_mask; uint8_t enable_reg; uint8_t enable_mask; uint8_t ext_enable_reg; uint8_t ext_enable_mask; struct regulator_range *ranges; int nranges; }; struct as3722_reg_sc { struct regnode *regnode; struct as3722_softc *base_sc; struct reg_def *def; phandle_t xref; struct regnode_std_param *param; int ext_control; int enable_tracking; int enable_usec; }; -#define RANGE_INIT(_min_sel, _max_sel, _min_uvolt, _step_uvolt) \ -{ \ - .min_sel = _min_sel, \ - .max_sel = _max_sel, \ - .min_uvolt = _min_uvolt, \ - .step_uvolt = _step_uvolt, \ -} - static struct regulator_range as3722_sd016_ranges[] = { - RANGE_INIT(0x00, 0x00, 0, 0), - RANGE_INIT(0x01, 0x5A, 610000, 10000), + REG_RANGE_INIT(0x00, 0x00, 0, 0), + REG_RANGE_INIT(0x01, 0x5A, 610000, 10000), }; static struct regulator_range as3722_sd0_lv_ranges[] = { - RANGE_INIT(0x00, 0x00, 0, 0), - RANGE_INIT(0x01, 0x6E, 410000, 10000), + REG_RANGE_INIT(0x00, 0x00, 0, 0), + REG_RANGE_INIT(0x01, 0x6E, 410000, 10000), }; static struct regulator_range as3722_sd_ranges[] = { - RANGE_INIT(0x00, 0x00, 0, 0), - RANGE_INIT(0x01, 0x40, 612500, 12500), - RANGE_INIT(0x41, 0x70, 1425000, 25000), - RANGE_INIT(0x71, 0x7F, 2650000, 50000), + REG_RANGE_INIT(0x00, 0x00, 0, 0), + REG_RANGE_INIT(0x01, 0x40, 612500, 12500), + REG_RANGE_INIT(0x41, 0x70, 1425000, 25000), + REG_RANGE_INIT(0x71, 0x7F, 2650000, 50000), }; static struct regulator_range as3722_ldo3_ranges[] = { - RANGE_INIT(0x00, 0x00, 0, 0), - RANGE_INIT(0x01, 0x2D, 620000, 20000), + REG_RANGE_INIT(0x00, 0x00, 0, 0), + REG_RANGE_INIT(0x01, 0x2D, 620000, 20000), }; static struct regulator_range as3722_ldo_ranges[] = { - RANGE_INIT(0x00, 0x00, 0, 0), - RANGE_INIT(0x01, 0x24, 825000, 25000), - RANGE_INIT(0x40, 0x7F, 1725000, 25000), + REG_RANGE_INIT(0x00, 0x00, 0, 0), + REG_RANGE_INIT(0x01, 0x24, 825000, 25000), + REG_RANGE_INIT(0x40, 0x7F, 1725000, 25000), }; static struct reg_def as3722s_def[] = { { .id = AS3722_REG_ID_SD0, .name = "sd0", .volt_reg = AS3722_SD0_VOLTAGE, .volt_vsel_mask = AS3722_SD_VSEL_MASK, .enable_reg = AS3722_SD_CONTROL, .enable_mask = AS3722_SDN_CTRL(0), .ext_enable_reg = AS3722_ENABLE_CTRL1, .ext_enable_mask = AS3722_SD0_EXT_ENABLE_MASK, .ranges = as3722_sd016_ranges, .nranges = nitems(as3722_sd016_ranges), }, { .id = AS3722_REG_ID_SD1, .name = "sd1", .volt_reg = AS3722_SD1_VOLTAGE, .volt_vsel_mask = AS3722_SD_VSEL_MASK, .enable_reg = AS3722_SD_CONTROL, .enable_mask = AS3722_SDN_CTRL(1), .ext_enable_reg = AS3722_ENABLE_CTRL1, .ext_enable_mask = AS3722_SD1_EXT_ENABLE_MASK, .ranges = as3722_sd_ranges, .nranges = nitems(as3722_sd_ranges), }, { .id = AS3722_REG_ID_SD2, .name = "sd2", .supply_name = "vsup-sd2", .volt_reg = AS3722_SD2_VOLTAGE, .volt_vsel_mask = AS3722_SD_VSEL_MASK, .enable_reg = AS3722_SD_CONTROL, .enable_mask = AS3722_SDN_CTRL(2), .ext_enable_reg = AS3722_ENABLE_CTRL1, .ext_enable_mask = AS3722_SD2_EXT_ENABLE_MASK, .ranges = as3722_sd_ranges, .nranges = nitems(as3722_sd_ranges), }, { .id = AS3722_REG_ID_SD3, .name = "sd3", .supply_name = "vsup-sd3", .volt_reg = AS3722_SD3_VOLTAGE, .volt_vsel_mask = AS3722_SD_VSEL_MASK, .enable_reg = AS3722_SD_CONTROL, .enable_mask = AS3722_SDN_CTRL(3), .ext_enable_reg = AS3722_ENABLE_CTRL1, .ext_enable_mask = AS3722_SD3_EXT_ENABLE_MASK, .ranges = as3722_sd_ranges, .nranges = nitems(as3722_sd_ranges), }, { .id = AS3722_REG_ID_SD4, .name = "sd4", .supply_name = "vsup-sd4", .volt_reg = AS3722_SD4_VOLTAGE, .volt_vsel_mask = AS3722_SD_VSEL_MASK, .enable_reg = AS3722_SD_CONTROL, .enable_mask = AS3722_SDN_CTRL(4), .ext_enable_reg = AS3722_ENABLE_CTRL2, .ext_enable_mask = AS3722_SD4_EXT_ENABLE_MASK, .ranges = as3722_sd_ranges, .nranges = nitems(as3722_sd_ranges), }, { .id = AS3722_REG_ID_SD5, .name = "sd5", .supply_name = "vsup-sd5", .volt_reg = AS3722_SD5_VOLTAGE, .volt_vsel_mask = AS3722_SD_VSEL_MASK, .enable_reg = AS3722_SD_CONTROL, .enable_mask = AS3722_SDN_CTRL(5), .ext_enable_reg = AS3722_ENABLE_CTRL2, .ext_enable_mask = AS3722_SD5_EXT_ENABLE_MASK, .ranges = as3722_sd_ranges, .nranges = nitems(as3722_sd_ranges), }, { .id = AS3722_REG_ID_SD6, .name = "sd6", .volt_reg = AS3722_SD6_VOLTAGE, .volt_vsel_mask = AS3722_SD_VSEL_MASK, .enable_reg = AS3722_SD_CONTROL, .enable_mask = AS3722_SDN_CTRL(6), .ext_enable_reg = AS3722_ENABLE_CTRL2, .ext_enable_mask = AS3722_SD6_EXT_ENABLE_MASK, .ranges = as3722_sd016_ranges, .nranges = nitems(as3722_sd016_ranges), }, { .id = AS3722_REG_ID_LDO0, .name = "ldo0", .supply_name = "vin-ldo0", .volt_reg = AS3722_LDO0_VOLTAGE, .volt_vsel_mask = AS3722_LDO0_VSEL_MASK, .enable_reg = AS3722_LDO_CONTROL0, .enable_mask = AS3722_LDO0_CTRL, .ext_enable_reg = AS3722_ENABLE_CTRL3, .ext_enable_mask = AS3722_LDO0_EXT_ENABLE_MASK, .ranges = as3722_ldo_ranges, .nranges = nitems(as3722_ldo_ranges), }, { .id = AS3722_REG_ID_LDO1, .name = "ldo1", .supply_name = "vin-ldo1-6", .volt_reg = AS3722_LDO1_VOLTAGE, .volt_vsel_mask = AS3722_LDO_VSEL_MASK, .enable_reg = AS3722_LDO_CONTROL0, .enable_mask = AS3722_LDO1_CTRL, .ext_enable_reg = AS3722_ENABLE_CTRL3, .ext_enable_mask = AS3722_LDO1_EXT_ENABLE_MASK, .ranges = as3722_ldo_ranges, .nranges = nitems(as3722_ldo_ranges), }, { .id = AS3722_REG_ID_LDO2, .name = "ldo2", .supply_name = "vin-ldo2-5-7", .volt_reg = AS3722_LDO2_VOLTAGE, .volt_vsel_mask = AS3722_LDO_VSEL_MASK, .enable_reg = AS3722_LDO_CONTROL0, .enable_mask = AS3722_LDO2_CTRL, .ext_enable_reg = AS3722_ENABLE_CTRL3, .ext_enable_mask = AS3722_LDO2_EXT_ENABLE_MASK, .ranges = as3722_ldo_ranges, .nranges = nitems(as3722_ldo_ranges), }, { .id = AS3722_REG_ID_LDO3, .name = "ldo3", .supply_name = "vin-ldo3-4", .volt_reg = AS3722_LDO3_VOLTAGE, .volt_vsel_mask = AS3722_LDO3_VSEL_MASK, .enable_reg = AS3722_LDO_CONTROL0, .enable_mask = AS3722_LDO3_CTRL, .ext_enable_reg = AS3722_ENABLE_CTRL3, .ext_enable_mask = AS3722_LDO3_EXT_ENABLE_MASK, .ranges = as3722_ldo3_ranges, .nranges = nitems(as3722_ldo3_ranges), }, { .id = AS3722_REG_ID_LDO4, .name = "ldo4", .supply_name = "vin-ldo3-4", .volt_reg = AS3722_LDO4_VOLTAGE, .volt_vsel_mask = AS3722_LDO_VSEL_MASK, .enable_reg = AS3722_LDO_CONTROL0, .enable_mask = AS3722_LDO4_CTRL, .ext_enable_reg = AS3722_ENABLE_CTRL4, .ext_enable_mask = AS3722_LDO4_EXT_ENABLE_MASK, .ranges = as3722_ldo_ranges, .nranges = nitems(as3722_ldo_ranges), }, { .id = AS3722_REG_ID_LDO5, .name = "ldo5", .supply_name = "vin-ldo2-5-7", .volt_reg = AS3722_LDO5_VOLTAGE, .volt_vsel_mask = AS3722_LDO_VSEL_MASK, .enable_reg = AS3722_LDO_CONTROL0, .enable_mask = AS3722_LDO5_CTRL, .ext_enable_reg = AS3722_ENABLE_CTRL4, .ext_enable_mask = AS3722_LDO5_EXT_ENABLE_MASK, .ranges = as3722_ldo_ranges, .nranges = nitems(as3722_ldo_ranges), }, { .id = AS3722_REG_ID_LDO6, .name = "ldo6", .supply_name = "vin-ldo1-6", .volt_reg = AS3722_LDO6_VOLTAGE, .volt_vsel_mask = AS3722_LDO_VSEL_MASK, .enable_reg = AS3722_LDO_CONTROL0, .enable_mask = AS3722_LDO6_CTRL, .ext_enable_reg = AS3722_ENABLE_CTRL4, .ext_enable_mask = AS3722_LDO6_EXT_ENABLE_MASK, .ranges = as3722_ldo_ranges, .nranges = nitems(as3722_ldo_ranges), }, { .id = AS3722_REG_ID_LDO7, .name = "ldo7", .supply_name = "vin-ldo2-5-7", .volt_reg = AS3722_LDO7_VOLTAGE, .volt_vsel_mask = AS3722_LDO_VSEL_MASK, .enable_reg = AS3722_LDO_CONTROL0, .enable_mask = AS3722_LDO7_CTRL, .ext_enable_reg = AS3722_ENABLE_CTRL4, .ext_enable_mask = AS3722_LDO7_EXT_ENABLE_MASK, .ranges = as3722_ldo_ranges, .nranges = nitems(as3722_ldo_ranges), }, { .id = AS3722_REG_ID_LDO9, .name = "ldo9", .supply_name = "vin-ldo9-10", .volt_reg = AS3722_LDO9_VOLTAGE, .volt_vsel_mask = AS3722_LDO_VSEL_MASK, .enable_reg = AS3722_LDO_CONTROL1, .enable_mask = AS3722_LDO9_CTRL, .ext_enable_reg = AS3722_ENABLE_CTRL5, .ext_enable_mask = AS3722_LDO9_EXT_ENABLE_MASK, .ranges = as3722_ldo_ranges, .nranges = nitems(as3722_ldo_ranges), }, { .id = AS3722_REG_ID_LDO10, .name = "ldo10", .supply_name = "vin-ldo9-10", .volt_reg = AS3722_LDO10_VOLTAGE, .volt_vsel_mask = AS3722_LDO_VSEL_MASK, .enable_reg = AS3722_LDO_CONTROL1, .enable_mask = AS3722_LDO10_CTRL, .ext_enable_reg = AS3722_ENABLE_CTRL5, .ext_enable_mask = AS3722_LDO10_EXT_ENABLE_MASK, .ranges = as3722_ldo_ranges, .nranges = nitems(as3722_ldo_ranges), }, { .id = AS3722_REG_ID_LDO11, .name = "ldo11", .supply_name = "vin-ldo11", .volt_reg = AS3722_LDO11_VOLTAGE, .volt_vsel_mask = AS3722_LDO_VSEL_MASK, .enable_reg = AS3722_LDO_CONTROL1, .enable_mask = AS3722_LDO11_CTRL, .ext_enable_reg = AS3722_ENABLE_CTRL5, .ext_enable_mask = AS3722_LDO11_EXT_ENABLE_MASK, .ranges = as3722_ldo_ranges, .nranges = nitems(as3722_ldo_ranges), }, }; struct as3722_regnode_init_def { struct regnode_init_def reg_init_def; int ext_control; int enable_tracking; }; static int as3722_regnode_init(struct regnode *regnode); static int as3722_regnode_enable(struct regnode *regnode, bool enable, int *udelay); static int as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay); static int as3722_regnode_get_volt(struct regnode *regnode, int *uvolt); static regnode_method_t as3722_regnode_methods[] = { /* Regulator interface */ REGNODEMETHOD(regnode_init, as3722_regnode_init), REGNODEMETHOD(regnode_enable, as3722_regnode_enable), REGNODEMETHOD(regnode_set_voltage, as3722_regnode_set_volt), REGNODEMETHOD(regnode_get_voltage, as3722_regnode_get_volt), REGNODEMETHOD_END }; DEFINE_CLASS_1(as3722_regnode, as3722_regnode_class, as3722_regnode_methods, sizeof(struct as3722_reg_sc), regnode_class); static int -regulator_range_sel_to_volt(struct as3722_reg_sc *sc, uint8_t sel, int *volt) -{ - struct regulator_range *range; - struct reg_def *def; - int i; - - def = sc->def; - if (def->nranges == 0) - panic("Voltage regulator have zero ranges\n"); - - for (i = 0; i < def->nranges ; i++) { - range = def->ranges + i; - - if (!(sel >= range->min_sel && - sel <= range->max_sel)) - continue; - - sel -= range->min_sel; - - *volt = range->min_uvolt + sel * range->step_uvolt; - return (0); - } - - return (ERANGE); -} - -static int -regulator_range_volt_to_sel(struct as3722_reg_sc *sc, int min_uvolt, - int max_uvolt, uint8_t *out_sel) -{ - struct regulator_range *range; - struct reg_def *def; - uint8_t sel; - int uvolt; - int rv, i; - - def = sc->def; - if (def->nranges == 0) - panic("Voltage regulator have zero ranges\n"); - - for (i = 0; i < def->nranges; i++) { - range = def->ranges + i; - uvolt = range->min_uvolt + - (range->max_sel - range->min_sel) * range->step_uvolt; - - if ((min_uvolt > uvolt) || - (max_uvolt < range->min_uvolt)) - continue; - - if (min_uvolt <= range->min_uvolt) - min_uvolt = range->min_uvolt; - - /* If step is zero then range is fixed voltage range. */ - if (range->step_uvolt == 0) - sel = 0; - else - sel = DIV_ROUND_UP(min_uvolt - range->min_uvolt, - range->step_uvolt); - - - sel += range->min_sel; - - break; - } - - if (i >= def->nranges) - return (ERANGE); - - /* Verify new settings. */ - rv = regulator_range_sel_to_volt(sc, sel, &uvolt); - if (rv != 0) - return (rv); - if ((uvolt < min_uvolt) || (uvolt > max_uvolt)) - return (ERANGE); - - *out_sel = sel; - return (0); -} - - -static int as3722_read_sel(struct as3722_reg_sc *sc, uint8_t *sel) { int rv; rv = RD1(sc->base_sc, sc->def->volt_reg, sel); if (rv != 0) return (rv); *sel &= sc->def->volt_vsel_mask; *sel >>= ffs(sc->def->volt_vsel_mask) - 1; return (0); } static int as3722_write_sel(struct as3722_reg_sc *sc, uint8_t sel) { int rv; sel <<= ffs(sc->def->volt_vsel_mask) - 1; sel &= sc->def->volt_vsel_mask; rv = RM1(sc->base_sc, sc->def->volt_reg, sc->def->volt_vsel_mask, sel); if (rv != 0) return (rv); return (rv); } static bool as3722_sd0_is_low_voltage(struct as3722_reg_sc *sc) { uint8_t val; int rv; rv = RD1(sc->base_sc, AS3722_FUSE7, &val); if (rv != 0) return (rv); return (val & AS3722_FUSE7_SD0_LOW_VOLTAGE ? true : false); } static int as3722_reg_extreg_setup(struct as3722_reg_sc *sc, int ext_pwr_ctrl) { uint8_t val; int rv; val = ext_pwr_ctrl << (ffs(sc->def->ext_enable_mask) - 1); rv = RM1(sc->base_sc, sc->def->ext_enable_reg, sc->def->ext_enable_mask, val); return (rv); } static int as3722_reg_enable(struct as3722_reg_sc *sc) { int rv; rv = RM1(sc->base_sc, sc->def->enable_reg, sc->def->enable_mask, sc->def->enable_mask); return (rv); } static int as3722_reg_disable(struct as3722_reg_sc *sc) { int rv; rv = RM1(sc->base_sc, sc->def->enable_reg, sc->def->enable_mask, 0); return (rv); } static int as3722_regnode_init(struct regnode *regnode) { struct as3722_reg_sc *sc; int rv; sc = regnode_get_softc(regnode); sc->enable_usec = 500; if (sc->def->id == AS3722_REG_ID_SD0) { if (as3722_sd0_is_low_voltage(sc)) { sc->def->ranges = as3722_sd0_lv_ranges; sc->def->nranges = nitems(as3722_sd0_lv_ranges); } sc->enable_usec = 600; } else if (sc->def->id == AS3722_REG_ID_LDO3) { if (sc->enable_tracking) { rv = RM1(sc->base_sc, sc->def->volt_reg, AS3722_LDO3_MODE_MASK, AS3722_LDO3_MODE_PMOS_TRACKING); if (rv < 0) { device_printf(sc->base_sc->dev, "LDO3 tracking failed: %d\n", rv); return (rv); } } } if (sc->ext_control) { rv = as3722_reg_enable(sc); if (rv < 0) { device_printf(sc->base_sc->dev, "Failed to enable %s regulator: %d\n", sc->def->name, rv); return (rv); } rv = as3722_reg_extreg_setup(sc, sc->ext_control); if (rv < 0) { device_printf(sc->base_sc->dev, "%s ext control failed: %d", sc->def->name, rv); return (rv); } } return (0); } static void as3722_fdt_parse(struct as3722_softc *sc, phandle_t node, struct reg_def *def, struct as3722_regnode_init_def *init_def) { int rv; phandle_t parent, supply_node; char prop_name[64]; /* Maximum OFW property name length. */ rv = regulator_parse_ofw_stdparam(sc->dev, node, &init_def->reg_init_def); rv = OF_getencprop(node, "ams,ext-control", &init_def->ext_control, sizeof(init_def->ext_control)); if (rv <= 0) init_def->ext_control = 0; if (init_def->ext_control > 3) { device_printf(sc->dev, "Invalid value for ams,ext-control property: %d\n", init_def->ext_control); init_def->ext_control = 0; } if (OF_hasprop(node, "ams,enable-tracking")) init_def->enable_tracking = 1; /* Get parent supply. */ if (def->supply_name == NULL) return; parent = OF_parent(node); snprintf(prop_name, sizeof(prop_name), "%s-supply", def->supply_name); rv = OF_getencprop(parent, prop_name, &supply_node, sizeof(supply_node)); if (rv <= 0) return; supply_node = OF_node_from_xref(supply_node); rv = OF_getprop_alloc(supply_node, "regulator-name", 1, (void **)&init_def->reg_init_def.parent_name); if (rv <= 0) init_def->reg_init_def.parent_name = NULL; } static struct as3722_reg_sc * as3722_attach(struct as3722_softc *sc, phandle_t node, struct reg_def *def) { struct as3722_reg_sc *reg_sc; struct as3722_regnode_init_def init_def; struct regnode *regnode; bzero(&init_def, sizeof(init_def)); as3722_fdt_parse(sc, node, def, &init_def); init_def.reg_init_def.id = def->id; init_def.reg_init_def.ofw_node = node; regnode = regnode_create(sc->dev, &as3722_regnode_class, &init_def.reg_init_def); if (regnode == NULL) { device_printf(sc->dev, "Cannot create regulator.\n"); return (NULL); } reg_sc = regnode_get_softc(regnode); /* Init regulator softc. */ reg_sc->regnode = regnode; reg_sc->base_sc = sc; reg_sc->def = def; reg_sc->xref = OF_xref_from_node(node); reg_sc->param = regnode_get_stdparam(regnode); reg_sc->ext_control = init_def.ext_control; reg_sc->enable_tracking = init_def.enable_tracking; regnode_register(regnode); if (bootverbose) { int volt, rv; regnode_topo_slock(); rv = regnode_get_voltage(regnode, &volt); if (rv == ENODEV) { device_printf(sc->dev, " Regulator %s: parent doesn't exist yet.\n", regnode_get_name(regnode)); } else if (rv != 0) { device_printf(sc->dev, " Regulator %s: voltage: INVALID!!!\n", regnode_get_name(regnode)); } else { device_printf(sc->dev, " Regulator %s: voltage: %d uV\n", regnode_get_name(regnode), volt); } regnode_topo_unlock(); } return (reg_sc); } int as3722_regulator_attach(struct as3722_softc *sc, phandle_t node) { struct as3722_reg_sc *reg; phandle_t child, rnode; int i; rnode = ofw_bus_find_child(node, "regulators"); if (rnode <= 0) { device_printf(sc->dev, " Cannot find regulators subnode\n"); return (ENXIO); } sc->nregs = nitems(as3722s_def); sc->regs = malloc(sizeof(struct as3722_reg_sc *) * sc->nregs, M_AS3722_REG, M_WAITOK | M_ZERO); /* Attach all known regulators if exist in DT. */ for (i = 0; i < sc->nregs; i++) { child = ofw_bus_find_child(rnode, as3722s_def[i].name); if (child == 0) { if (bootverbose) device_printf(sc->dev, "Regulator %s missing in DT\n", as3722s_def[i].name); continue; } reg = as3722_attach(sc, child, as3722s_def + i); if (reg == NULL) { device_printf(sc->dev, "Cannot attach regulator: %s\n", as3722s_def[i].name); return (ENXIO); } sc->regs[i] = reg; } return (0); } int as3722_regulator_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, int *num) { struct as3722_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 int as3722_regnode_enable(struct regnode *regnode, bool val, int *udelay) { struct as3722_reg_sc *sc; int rv; sc = regnode_get_softc(regnode); if (val) rv = as3722_reg_enable(sc); else rv = as3722_reg_disable(sc); *udelay = sc->enable_usec; return (rv); } static int as3722_regnode_set_volt(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay) { struct as3722_reg_sc *sc; uint8_t sel; int rv; sc = regnode_get_softc(regnode); *udelay = 0; - rv = regulator_range_volt_to_sel(sc, min_uvolt, max_uvolt, &sel); + rv = regulator_range_volt_to_sel8(sc->def->ranges, sc->def->nranges, + min_uvolt, max_uvolt, &sel); if (rv != 0) return (rv); rv = as3722_write_sel(sc, sel); return (rv); } static int as3722_regnode_get_volt(struct regnode *regnode, int *uvolt) { struct as3722_reg_sc *sc; uint8_t sel; int rv; sc = regnode_get_softc(regnode); rv = as3722_read_sel(sc, &sel); if (rv != 0) return (rv); /* LDO6 have bypass. */ if (sc->def->id == AS3722_REG_ID_LDO6 && sel == AS3722_LDO6_SEL_BYPASS) return (ENOENT); - rv = regulator_range_sel_to_volt(sc, sel, uvolt); + rv = regulator_range_sel8_to_volt(sc->def->ranges, sc->def->nranges, + sel, uvolt); return (rv); } Index: head/sys/dev/extres/regulator/regulator.c =================================================================== --- head/sys/dev/extres/regulator/regulator.c (revision 307557) +++ head/sys/dev/extres/regulator/regulator.c (revision 307558) @@ -1,986 +1,1072 @@ /*- * Copyright 2016 Michal Meloun * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_platform.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef FDT #include #include #include #endif #include #include "regdev_if.h" MALLOC_DEFINE(M_REGULATOR, "regulator", "Regulator framework"); +#define DIV_ROUND_UP(n,d) howmany(n, d) + /* Forward declarations. */ struct regulator; struct regnode; typedef TAILQ_HEAD(regnode_list, regnode) regnode_list_t; typedef TAILQ_HEAD(regulator_list, regulator) regulator_list_t; /* Default regulator methods. */ static int regnode_method_enable(struct regnode *regnode, bool enable, int *udelay); static int regnode_method_status(struct regnode *regnode, int *status); static int regnode_method_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay); static int regnode_method_get_voltage(struct regnode *regnode, int *uvolt); /* * Regulator controller methods. */ static regnode_method_t regnode_methods[] = { REGNODEMETHOD(regnode_enable, regnode_method_enable), REGNODEMETHOD(regnode_status, regnode_method_status), REGNODEMETHOD(regnode_set_voltage, regnode_method_set_voltage), REGNODEMETHOD(regnode_get_voltage, regnode_method_get_voltage), REGNODEMETHOD_END }; DEFINE_CLASS_0(regnode, regnode_class, regnode_methods, 0); /* * Regulator node - basic element for modelling SOC and bard power supply * chains. Its contains producer data. */ struct regnode { KOBJ_FIELDS; TAILQ_ENTRY(regnode) reglist_link; /* Global list entry */ regulator_list_t consumers_list; /* Consumers list */ /* Cache for already resolved names */ struct regnode *parent; /* Resolved parent */ /* Details of this device. */ const char *name; /* Globally unique name */ const char *parent_name; /* Parent name */ device_t pdev; /* Producer device_t */ void *softc; /* Producer softc */ intptr_t id; /* Per producer unique id */ #ifdef FDT phandle_t ofw_node; /* OFW node of regulator */ #endif int flags; /* REGULATOR_FLAGS_ */ struct sx lock; /* Lock for this regulator */ int ref_cnt; /* Reference counter */ int enable_cnt; /* Enabled counter */ struct regnode_std_param std_param; /* Standard parameters */ }; /* * Per consumer data, information about how a consumer is using a regulator * node. * A pointer to this structure is used as a handle in the consumer interface. */ struct regulator { device_t cdev; /* Consumer device */ struct regnode *regnode; TAILQ_ENTRY(regulator) link; /* Consumers list entry */ int enable_cnt; int min_uvolt; /* Requested uvolt range */ int max_uvolt; }; /* * Regulator names must be system wide unique. */ static regnode_list_t regnode_list = TAILQ_HEAD_INITIALIZER(regnode_list); static struct sx regnode_topo_lock; SX_SYSINIT(regulator_topology, ®node_topo_lock, "Regulator topology lock"); #define REG_TOPO_SLOCK() sx_slock(®node_topo_lock) #define REG_TOPO_XLOCK() sx_xlock(®node_topo_lock) #define REG_TOPO_UNLOCK() sx_unlock(®node_topo_lock) #define REG_TOPO_ASSERT() sx_assert(®node_topo_lock, SA_LOCKED) #define REG_TOPO_XASSERT() sx_assert(®node_topo_lock, SA_XLOCKED) #define REGNODE_SLOCK(_sc) sx_slock(&((_sc)->lock)) #define REGNODE_XLOCK(_sc) sx_xlock(&((_sc)->lock)) #define REGNODE_UNLOCK(_sc) sx_unlock(&((_sc)->lock)) /* ---------------------------------------------------------------------------- * * Default regulator methods for base class. * */ static int regnode_method_enable(struct regnode *regnode, bool enable, int *udelay) { if (!enable) return (ENXIO); *udelay = 0; return (0); } static int regnode_method_status(struct regnode *regnode, int *status) { *status = REGULATOR_STATUS_ENABLED; return (0); } static int regnode_method_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt, int *udelay) { if ((min_uvolt > regnode->std_param.max_uvolt) || (max_uvolt < regnode->std_param.min_uvolt)) return (ERANGE); *udelay = 0; return (0); } static int regnode_method_get_voltage(struct regnode *regnode, int *uvolt) { return (regnode->std_param.min_uvolt + (regnode->std_param.max_uvolt - regnode->std_param.min_uvolt) / 2); } /* ---------------------------------------------------------------------------- * * Internal functions. * */ static struct regnode * regnode_find_by_name(const char *name) { struct regnode *entry; REG_TOPO_ASSERT(); TAILQ_FOREACH(entry, ®node_list, reglist_link) { if (strcmp(entry->name, name) == 0) return (entry); } return (NULL); } static struct regnode * regnode_find_by_id(device_t dev, intptr_t id) { struct regnode *entry; REG_TOPO_ASSERT(); TAILQ_FOREACH(entry, ®node_list, reglist_link) { if ((entry->pdev == dev) && (entry->id == id)) return (entry); } return (NULL); } /* * Create and initialize regulator object, but do not register it. */ struct regnode * regnode_create(device_t pdev, regnode_class_t regnode_class, struct regnode_init_def *def) { struct regnode *regnode; KASSERT(def->name != NULL, ("regulator name is NULL")); KASSERT(def->name[0] != '\0', ("regulator name is empty")); REG_TOPO_SLOCK(); if (regnode_find_by_name(def->name) != NULL) panic("Duplicated regulator registration: %s\n", def->name); REG_TOPO_UNLOCK(); /* Create object and initialize it. */ regnode = malloc(sizeof(struct regnode), M_REGULATOR, M_WAITOK | M_ZERO); kobj_init((kobj_t)regnode, (kobj_class_t)regnode_class); sx_init(®node->lock, "Regulator node lock"); /* Allocate softc if required. */ if (regnode_class->size > 0) { regnode->softc = malloc(regnode_class->size, M_REGULATOR, M_WAITOK | M_ZERO); } /* Copy all strings unless they're flagged as static. */ if (def->flags & REGULATOR_FLAGS_STATIC) { regnode->name = def->name; regnode->parent_name = def->parent_name; } else { regnode->name = strdup(def->name, M_REGULATOR); if (def->parent_name != NULL) regnode->parent_name = strdup(def->parent_name, M_REGULATOR); } /* Rest of init. */ TAILQ_INIT(®node->consumers_list); regnode->id = def->id; regnode->pdev = pdev; regnode->flags = def->flags; regnode->parent = NULL; regnode->std_param = def->std_param; #ifdef FDT regnode->ofw_node = def->ofw_node; #endif return (regnode); } /* Register regulator object. */ struct regnode * regnode_register(struct regnode *regnode) { int rv; #ifdef FDT if (regnode->ofw_node <= 0) regnode->ofw_node = ofw_bus_get_node(regnode->pdev); if (regnode->ofw_node <= 0) return (NULL); #endif rv = REGNODE_INIT(regnode); if (rv != 0) { printf("REGNODE_INIT failed: %d\n", rv); return (NULL); } REG_TOPO_XLOCK(); TAILQ_INSERT_TAIL(®node_list, regnode, reglist_link); REG_TOPO_UNLOCK(); #ifdef FDT OF_device_register_xref(OF_xref_from_node(regnode->ofw_node), regnode->pdev); #endif return (regnode); } static int regnode_resolve_parent(struct regnode *regnode) { /* All ready resolved or no parent? */ if ((regnode->parent != NULL) || (regnode->parent_name == NULL)) return (0); regnode->parent = regnode_find_by_name(regnode->parent_name); if (regnode->parent == NULL) return (ENODEV); return (0); } static void regnode_delay(int usec) { int ticks; if (usec == 0) return; ticks = (usec * hz + 999999) / 1000000; if (cold || ticks < 2) DELAY(usec); else pause("REGULATOR", ticks); } /* -------------------------------------------------------------------------- * * Regulator providers interface * */ const char * regnode_get_name(struct regnode *regnode) { return (regnode->name); } const char * regnode_get_parent_name(struct regnode *regnode) { return (regnode->parent_name); } int regnode_get_flags(struct regnode *regnode) { return (regnode->flags); } void * regnode_get_softc(struct regnode *regnode) { return (regnode->softc); } device_t regnode_get_device(struct regnode *regnode) { return (regnode->pdev); } struct regnode_std_param *regnode_get_stdparam(struct regnode *regnode) { return (®node->std_param); } void regnode_topo_unlock(void) { REG_TOPO_UNLOCK(); } void regnode_topo_xlock(void) { REG_TOPO_XLOCK(); } void regnode_topo_slock(void) { REG_TOPO_SLOCK(); } /* -------------------------------------------------------------------------- * * Real consumers executive * */ struct regnode * regnode_get_parent(struct regnode *regnode) { int rv; REG_TOPO_ASSERT(); rv = regnode_resolve_parent(regnode); if (rv != 0) return (NULL); return (regnode->parent); } /* * Enable regulator. */ int regnode_enable(struct regnode *regnode) { int udelay; int rv; REG_TOPO_ASSERT(); /* Enable regulator for each node in chain, starting from source. */ rv = regnode_resolve_parent(regnode); if (rv != 0) return (rv); if (regnode->parent != NULL) { rv = regnode_enable(regnode->parent); if (rv != 0) return (rv); } /* Handle this node. */ REGNODE_XLOCK(regnode); if (regnode->enable_cnt == 0) { rv = REGNODE_ENABLE(regnode, true, &udelay); if (rv != 0) { REGNODE_UNLOCK(regnode); return (rv); } regnode_delay(udelay); } regnode->enable_cnt++; REGNODE_UNLOCK(regnode); return (0); } /* * Disable regulator. */ int regnode_disable(struct regnode *regnode) { int udelay; int rv; REG_TOPO_ASSERT(); rv = 0; REGNODE_XLOCK(regnode); /* Disable regulator for each node in chain, starting from consumer. */ if ((regnode->enable_cnt == 1) && ((regnode->flags & REGULATOR_FLAGS_NOT_DISABLE) == 0)) { rv = REGNODE_ENABLE(regnode, false, &udelay); if (rv != 0) { REGNODE_UNLOCK(regnode); return (rv); } regnode_delay(udelay); } regnode->enable_cnt--; REGNODE_UNLOCK(regnode); rv = regnode_resolve_parent(regnode); if (rv != 0) return (rv); if (regnode->parent != NULL) rv = regnode_disable(regnode->parent); return (rv); } /* * Stop regulator. */ int regnode_stop(struct regnode *regnode, int depth) { int udelay; int rv; REG_TOPO_ASSERT(); rv = 0; REGNODE_XLOCK(regnode); /* The first node must not be enabled. */ if ((regnode->enable_cnt != 0) && (depth == 0)) { REGNODE_UNLOCK(regnode); return (EBUSY); } /* Disable regulator for each node in chain, starting from consumer */ if ((regnode->enable_cnt == 0) && ((regnode->flags & REGULATOR_FLAGS_NOT_DISABLE) == 0)) { rv = REGNODE_ENABLE(regnode, false, &udelay); if (rv != 0) { REGNODE_UNLOCK(regnode); return (rv); } regnode_delay(udelay); } REGNODE_UNLOCK(regnode); rv = regnode_resolve_parent(regnode); if (rv != 0) return (rv); if (regnode->parent != NULL) rv = regnode_stop(regnode->parent, depth + 1); return (rv); } /* * Get regulator status. (REGULATOR_STATUS_*) */ int regnode_status(struct regnode *regnode, int *status) { int rv; REG_TOPO_ASSERT(); REGNODE_XLOCK(regnode); rv = REGNODE_STATUS(regnode, status); REGNODE_UNLOCK(regnode); return (rv); } /* * Get actual regulator voltage. */ int regnode_get_voltage(struct regnode *regnode, int *uvolt) { int rv; REG_TOPO_ASSERT(); REGNODE_XLOCK(regnode); rv = REGNODE_GET_VOLTAGE(regnode, uvolt); REGNODE_UNLOCK(regnode); /* Pass call into parent, if regulator is in bypass mode. */ if (rv == ENOENT) { rv = regnode_resolve_parent(regnode); if (rv != 0) return (rv); if (regnode->parent != NULL) rv = regnode_get_voltage(regnode->parent, uvolt); } return (rv); } /* * Set regulator voltage. */ int regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt) { int udelay; int rv; REG_TOPO_ASSERT(); REGNODE_XLOCK(regnode); rv = REGNODE_SET_VOLTAGE(regnode, min_uvolt, max_uvolt, &udelay); if (rv == 0) regnode_delay(udelay); REGNODE_UNLOCK(regnode); return (rv); } /* * Consumer variant of regnode_set_voltage(). */ static int regnode_set_voltage_checked(struct regnode *regnode, struct regulator *reg, int min_uvolt, int max_uvolt) { int udelay; int all_max_uvolt; int all_min_uvolt; struct regulator *tmp; int rv; REG_TOPO_ASSERT(); REGNODE_XLOCK(regnode); /* Return error if requested range is outside of regulator range. */ if ((min_uvolt > regnode->std_param.max_uvolt) || (max_uvolt < regnode->std_param.min_uvolt)) { REGNODE_UNLOCK(regnode); return (ERANGE); } /* Get actual voltage range for all consumers. */ all_min_uvolt = regnode->std_param.min_uvolt; all_max_uvolt = regnode->std_param.max_uvolt; TAILQ_FOREACH(tmp, ®node->consumers_list, link) { /* Don't take requestor in account. */ if (tmp == reg) continue; if (all_min_uvolt < tmp->min_uvolt) all_min_uvolt = tmp->min_uvolt; if (all_max_uvolt > tmp->max_uvolt) all_max_uvolt = tmp->max_uvolt; } /* Test if request fits to actual contract. */ if ((min_uvolt > all_max_uvolt) || (max_uvolt < all_min_uvolt)) { REGNODE_UNLOCK(regnode); return (ERANGE); } /* Adjust new range.*/ if (min_uvolt < all_min_uvolt) min_uvolt = all_min_uvolt; if (max_uvolt > all_max_uvolt) max_uvolt = all_max_uvolt; rv = REGNODE_SET_VOLTAGE(regnode, min_uvolt, max_uvolt, &udelay); regnode_delay(udelay); REGNODE_UNLOCK(regnode); return (rv); } #ifdef FDT phandle_t regnode_get_ofw_node(struct regnode *regnode) { return (regnode->ofw_node); } #endif /* -------------------------------------------------------------------------- * * Regulator consumers interface. * */ /* Helper function for regulator_get*() */ static regulator_t regulator_create(struct regnode *regnode, device_t cdev) { struct regulator *reg; REG_TOPO_ASSERT(); reg = malloc(sizeof(struct regulator), M_REGULATOR, M_WAITOK | M_ZERO); reg->cdev = cdev; reg->regnode = regnode; reg->enable_cnt = 0; REGNODE_XLOCK(regnode); regnode->ref_cnt++; TAILQ_INSERT_TAIL(®node->consumers_list, reg, link); reg ->min_uvolt = regnode->std_param.min_uvolt; reg ->max_uvolt = regnode->std_param.max_uvolt; REGNODE_UNLOCK(regnode); return (reg); } int regulator_enable(regulator_t reg) { int rv; struct regnode *regnode; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); REG_TOPO_SLOCK(); rv = regnode_enable(regnode); if (rv == 0) reg->enable_cnt++; REG_TOPO_UNLOCK(); return (rv); } int regulator_disable(regulator_t reg) { int rv; struct regnode *regnode; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); KASSERT(reg->enable_cnt > 0, ("Attempt to disable already disabled regulator: %s\n", regnode->name)); REG_TOPO_SLOCK(); rv = regnode_disable(regnode); if (rv == 0) reg->enable_cnt--; REG_TOPO_UNLOCK(); return (rv); } int regulator_stop(regulator_t reg) { int rv; struct regnode *regnode; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); KASSERT(reg->enable_cnt == 0, ("Attempt to stop already enabled regulator: %s\n", regnode->name)); REG_TOPO_SLOCK(); rv = regnode_stop(regnode, 0); REG_TOPO_UNLOCK(); return (rv); } int regulator_status(regulator_t reg, int *status) { int rv; struct regnode *regnode; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); REG_TOPO_SLOCK(); rv = regnode_status(regnode, status); REG_TOPO_UNLOCK(); return (rv); } int regulator_get_voltage(regulator_t reg, int *uvolt) { int rv; struct regnode *regnode; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); REG_TOPO_SLOCK(); rv = regnode_get_voltage(regnode, uvolt); REG_TOPO_UNLOCK(); return (rv); } int regulator_set_voltage(regulator_t reg, int min_uvolt, int max_uvolt) { struct regnode *regnode; int rv; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); REG_TOPO_SLOCK(); rv = regnode_set_voltage_checked(regnode, reg, min_uvolt, max_uvolt); if (rv == 0) { reg->min_uvolt = min_uvolt; reg->max_uvolt = max_uvolt; } REG_TOPO_UNLOCK(); return (rv); } const char * regulator_get_name(regulator_t reg) { struct regnode *regnode; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); return (regnode->name); } int regulator_get_by_name(device_t cdev, const char *name, regulator_t *reg) { struct regnode *regnode; REG_TOPO_SLOCK(); regnode = regnode_find_by_name(name); if (regnode == NULL) { REG_TOPO_UNLOCK(); return (ENODEV); } *reg = regulator_create(regnode, cdev); REG_TOPO_UNLOCK(); return (0); } int regulator_get_by_id(device_t cdev, device_t pdev, intptr_t id, regulator_t *reg) { struct regnode *regnode; REG_TOPO_SLOCK(); regnode = regnode_find_by_id(pdev, id); if (regnode == NULL) { REG_TOPO_UNLOCK(); return (ENODEV); } *reg = regulator_create(regnode, cdev); REG_TOPO_UNLOCK(); return (0); } int regulator_release(regulator_t reg) { struct regnode *regnode; regnode = reg->regnode; KASSERT(regnode->ref_cnt > 0, ("Attempt to access unreferenced regulator: %s\n", regnode->name)); REG_TOPO_SLOCK(); while (reg->enable_cnt > 0) { regnode_disable(regnode); reg->enable_cnt--; } REGNODE_XLOCK(regnode); TAILQ_REMOVE(®node->consumers_list, reg, link); regnode->ref_cnt--; REGNODE_UNLOCK(regnode); REG_TOPO_UNLOCK(); free(reg, M_REGULATOR); return (0); } #ifdef FDT /* Default DT mapper. */ int regdev_default_ofw_map(device_t dev, phandle_t xref, int ncells, pcell_t *cells, intptr_t *id) { if (ncells == 0) *id = 1; else if (ncells == 1) *id = cells[0]; else return (ERANGE); return (0); } int regulator_parse_ofw_stdparam(device_t pdev, phandle_t node, struct regnode_init_def *def) { phandle_t supply_xref; struct regnode_std_param *par; int rv; par = &def->std_param; rv = OF_getprop_alloc(node, "regulator-name", 1, (void **)&def->name); if (rv <= 0) { device_printf(pdev, "%s: Missing regulator name\n", __func__); return (ENXIO); } rv = OF_getencprop(node, "regulator-min-microvolt", &par->min_uvolt, sizeof(par->min_uvolt)); if (rv <= 0) par->min_uvolt = 0; rv = OF_getencprop(node, "regulator-max-microvolt", &par->max_uvolt, sizeof(par->max_uvolt)); if (rv <= 0) par->max_uvolt = 0; rv = OF_getencprop(node, "regulator-min-microamp", &par->min_uamp, sizeof(par->min_uamp)); if (rv <= 0) par->min_uamp = 0; rv = OF_getencprop(node, "regulator-max-microamp", &par->max_uamp, sizeof(par->max_uamp)); if (rv <= 0) par->max_uamp = 0; rv = OF_getencprop(node, "regulator-ramp-delay", &par->ramp_delay, sizeof(par->ramp_delay)); if (rv <= 0) par->ramp_delay = 0; rv = OF_getencprop(node, "regulator-enable-ramp-delay", &par->enable_delay, sizeof(par->enable_delay)); if (rv <= 0) par->enable_delay = 0; if (OF_hasprop(node, "regulator-boot-on")) par->boot_on = 1; if (OF_hasprop(node, "regulator-always-on")) par->always_on = 1; if (OF_hasprop(node, "enable-active-high")) par->enable_active_high = 1; rv = OF_getencprop(node, "vin-supply", &supply_xref, sizeof(supply_xref)); if (rv >= 0) { rv = OF_getprop_alloc(supply_xref, "regulator-name", 1, (void **)&def->parent_name); if (rv <= 0) def->parent_name = NULL; } return (0); } int regulator_get_by_ofw_property(device_t cdev, phandle_t cnode, char *name, regulator_t *reg) { phandle_t *cells; device_t regdev; int ncells, rv; intptr_t id; *reg = NULL; if (cnode <= 0) cnode = ofw_bus_get_node(cdev); if (cnode <= 0) { device_printf(cdev, "%s called on not ofw based device\n", __func__); return (ENXIO); } cells = NULL; ncells = OF_getencprop_alloc(cnode, name, sizeof(*cells), (void **)&cells); if (ncells <= 0) return (ENXIO); /* Translate xref to device */ regdev = OF_device_from_xref(cells[0]); if (regdev == NULL) { OF_prop_free(cells); return (ENODEV); } /* Map regulator to number */ rv = REGDEV_MAP(regdev, cells[0], ncells - 1, cells + 1, &id); OF_prop_free(cells); if (rv != 0) return (rv); return (regulator_get_by_id(cdev, regdev, id, reg)); } #endif + +/* -------------------------------------------------------------------------- + * + * Regulator utility functions. + * + */ + +/* Convert raw selector value to real voltage */ +int +regulator_range_sel8_to_volt(struct regulator_range *ranges, int nranges, + uint8_t sel, int *volt) +{ + struct regulator_range *range; + int i; + + if (nranges == 0) + panic("Voltage regulator have zero ranges\n"); + + for (i = 0; i < nranges ; i++) { + range = ranges + i; + + if (!(sel >= range->min_sel && + sel <= range->max_sel)) + continue; + + sel -= range->min_sel; + + *volt = range->min_uvolt + sel * range->step_uvolt; + return (0); + } + + return (ERANGE); +} + +int +regulator_range_volt_to_sel8(struct regulator_range *ranges, int nranges, + int min_uvolt, int max_uvolt, uint8_t *out_sel) +{ + struct regulator_range *range; + uint8_t sel; + int uvolt; + int rv, i; + + if (nranges == 0) + panic("Voltage regulator have zero ranges\n"); + + for (i = 0; i < nranges; i++) { + range = ranges + i; + uvolt = range->min_uvolt + + (range->max_sel - range->min_sel) * range->step_uvolt; + + if ((min_uvolt > uvolt) || + (max_uvolt < range->min_uvolt)) + continue; + + if (min_uvolt <= range->min_uvolt) + min_uvolt = range->min_uvolt; + + /* if step == 0 -> fixed voltage range. */ + if (range->step_uvolt == 0) + sel = 0; + else + sel = DIV_ROUND_UP(min_uvolt - range->min_uvolt, + range->step_uvolt); + + + sel += range->min_sel; + + break; + } + + if (i >= nranges) + return (ERANGE); + + /* Verify new settings. */ + rv = regulator_range_sel8_to_volt(ranges, nranges, sel, &uvolt); + if (rv != 0) + return (rv); + if ((uvolt < min_uvolt) || (uvolt > max_uvolt)) + return (ERANGE); + + *out_sel = sel; + return (0); +} Index: head/sys/dev/extres/regulator/regulator.h =================================================================== --- head/sys/dev/extres/regulator/regulator.h (revision 307557) +++ head/sys/dev/extres/regulator/regulator.h (revision 307558) @@ -1,128 +1,147 @@ /*- * Copyright 2016 Michal Meloun * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _DEV_EXTRES_REGULATOR_H_ #define _DEV_EXTRES_REGULATOR_H_ #include "opt_platform.h" #include #ifdef FDT #include #endif #include "regnode_if.h" #define REGULATOR_FLAGS_STATIC 0x00000001 /* Static strings */ #define REGULATOR_FLAGS_NOT_DISABLE 0x00000002 /* Cannot be disabled */ #define REGULATOR_STATUS_ENABLED 0x00000001 #define REGULATOR_STATUS_OVERCURRENT 0x00000002 typedef struct regulator *regulator_t; /* Standard regulator parameters. */ struct regnode_std_param { int min_uvolt; /* In uV */ int max_uvolt; /* In uV */ int min_uamp; /* In uA */ int max_uamp; /* In uA */ int ramp_delay; /* In uV/usec */ int enable_delay; /* In usec */ bool boot_on; /* Is enabled on boot */ bool always_on; /* Must be enabled */ int enable_active_high; }; /* Initialization parameters. */ struct regnode_init_def { char *name; /* Regulator name */ char *parent_name; /* Name of parent regulator */ struct regnode_std_param std_param; /* Standard parameters */ intptr_t id; /* Regulator ID */ int flags; /* Flags */ #ifdef FDT phandle_t ofw_node; /* OFW node of regulator */ #endif +}; +struct regulator_range { + int min_uvolt; + int step_uvolt; + uint8_t min_sel; + uint8_t max_sel; }; +#define REG_RANGE_INIT(_min_sel, _max_sel, _min_uvolt, _step_uvolt) { \ + .min_sel = _min_sel, \ + .max_sel = _max_sel, \ + .min_uvolt = _min_uvolt, \ + .step_uvolt = _step_uvolt, \ +} + /* * Shorthands for constructing method tables. */ #define REGNODEMETHOD KOBJMETHOD #define REGNODEMETHOD_END KOBJMETHOD_END #define regnode_method_t kobj_method_t #define regnode_class_t kobj_class_t DECLARE_CLASS(regnode_class); /* Providers interface. */ struct regnode *regnode_create(device_t pdev, regnode_class_t regnode_class, struct regnode_init_def *def); struct regnode *regnode_register(struct regnode *regnode); const char *regnode_get_name(struct regnode *regnode); const char *regnode_get_parent_name(struct regnode *regnode); struct regnode *regnode_get_parent(struct regnode *regnode); int regnode_get_flags(struct regnode *regnode); void *regnode_get_softc(struct regnode *regnode); device_t regnode_get_device(struct regnode *regnode); struct regnode_std_param *regnode_get_stdparam(struct regnode *regnode); void regnode_topo_unlock(void); void regnode_topo_xlock(void); void regnode_topo_slock(void); int regnode_enable(struct regnode *regnode); int regnode_disable(struct regnode *regnode); int regnode_stop(struct regnode *regnode, int depth); int regnode_status(struct regnode *regnode, int *status); int regnode_get_voltage(struct regnode *regnode, int *uvolt); int regnode_set_voltage(struct regnode *regnode, int min_uvolt, int max_uvolt); #ifdef FDT phandle_t regnode_get_ofw_node(struct regnode *regnode); #endif /* Consumers interface. */ int regulator_get_by_name(device_t cdev, const char *name, regulator_t *regulator); int regulator_get_by_id(device_t cdev, device_t pdev, intptr_t id, regulator_t *regulator); int regulator_release(regulator_t regulator); const char *regulator_get_name(regulator_t regulator); int regulator_enable(regulator_t reg); int regulator_disable(regulator_t reg); int regulator_stop(regulator_t reg); int regulator_status(regulator_t reg, int *status); int regulator_get_voltage(regulator_t reg, int *uvolt); int regulator_set_voltage(regulator_t reg, int min_uvolt, int max_uvolt); #ifdef FDT int regulator_get_by_ofw_property(device_t dev, phandle_t node, char *name, regulator_t *reg); int regulator_parse_ofw_stdparam(device_t dev, phandle_t node, struct regnode_init_def *def); #endif + +/* Utility functions */ +int regulator_range_volt_to_sel8(struct regulator_range *ranges, int nranges, + int min_uvolt, int max_uvolt, uint8_t *out_sel); +int regulator_range_sel8_to_volt(struct regulator_range *ranges, int nranges, + uint8_t sel, int *volt); #endif /* _DEV_EXTRES_REGULATOR_H_ */