/* * BD718(37/47/50) have two "enable control modes". ON/OFF can either be * controlled by software - or by PMIC internal HW state machine. Whether * regulator should be under SW or HW control can be defined from device-tree. * Let's provide separate ops for regulators to use depending on the "enable * control mode".
*/ #define BD718XX_HWOPNAME(swopname) swopname##_hwcontrol
/* These functions are used when regulators are under HW state machine control. * We assume PMIC is in RUN state because SW running and able to query the * status. Most of the regulators have fixed ON or OFF state at RUN/IDLE so for * them we just return a constant. BD71837 BUCK3 and BUCK4 are exceptions as * they support configuring the ON/OFF state for RUN. * * Note for next hacker - these PMICs have a register where the HW state can be * read. If assuming RUN appears to be false in your use-case - you can * implement state reading (although that is not going to be atomic) before * returning the enable state.
*/ staticint always_enabled_by_hwstate(struct regulator_dev *rdev)
{ return 1;
}
if (*mask) { /* * We had fault detection disabled for the duration of the * voltage change. * * According to HW colleagues the maximum time it takes is * 1000us. I assume that on systems with light load this * might be less - and we could probably use DT to give * system specific delay value if performance matters. * * Well, knowing we use I2C here and can add scheduling delays * I don't think it is worth the hassle and I just add fixed * 1ms sleep here (and allow scheduling). If this turns out to * be a problem we can change it to delay and make the delay * time configurable.
*/
msleep(1);
ret = regmap_clear_bits(rdev->regmap, BD718XX_REG_MVRFLTMASK2,
*mask); if (ret)
dev_err(&rdev->dev, "Failed to re-enable voltage monitoring (%d)\n",
ret);
}
}
*mask = 0; if (rdev->desc->ops->is_enabled(rdev)) { int now, new;
now = rdev->desc->ops->get_voltage_sel(rdev); if (now < 0) return now;
now = rdev->desc->ops->list_voltage(rdev, now); if (now < 0) return now;
new = rdev->desc->ops->list_voltage(rdev, sel); if (new < 0) returnnew;
/* * If we increase LDO voltage when LDO is enabled we need to * disable the power-good detection until voltage has reached * the new level.
*/ if (new > now) { int tmp; int prot_bit; int ldo_offset = rdev->desc->id - BD718XX_LDO1;
prot_bit = BD718XX_LDO1_VRMON80 << ldo_offset;
ret = regmap_read(rdev->regmap, BD718XX_REG_MVRFLTMASK2,
&tmp); if (ret) {
dev_err(&rdev->dev, "Failed to read voltage monitoring state\n"); return ret;
}
if (!(tmp & prot_bit)) { /* We disable protection if it was enabled... */
ret = regmap_set_bits(rdev->regmap,
BD718XX_REG_MVRFLTMASK2,
prot_bit); /* ...and we also want to re-enable it */
*mask = prot_bit;
} if (ret) {
dev_err(&rdev->dev, "Failed to stop voltage monitoring\n"); return ret;
}
}
}
return 0;
}
staticint bd718xx_set_voltage_sel_restricted(struct regulator_dev *rdev, unsignedint sel)
{ int ret; int mask;
ret = voltage_change_prepare(rdev, sel, &mask); if (ret) return ret;
ret = regulator_set_voltage_sel_regmap(rdev, sel);
voltage_change_done(rdev, sel, &mask);
return ret;
}
staticint bd718xx_set_voltage_sel_pickable_restricted( struct regulator_dev *rdev, unsignedint sel)
{ int ret; int mask;
ret = voltage_change_prepare(rdev, sel, &mask); if (ret) return ret;
ret = regulator_set_voltage_sel_pickable_regmap(rdev, sel);
voltage_change_done(rdev, sel, &mask);
/* * BD71837 BUCK5 * 0.7V to 1.35V (range 0) * and * 0.675 to 1.325 (range 1)
*/ staticconststruct linear_range bd71837_buck5_volts[] = { /* Ranges when VOLT_SEL bit is 0 */
REGULATOR_LINEAR_RANGE(700000, 0x00, 0x03, 100000),
REGULATOR_LINEAR_RANGE(1050000, 0x04, 0x05, 50000),
REGULATOR_LINEAR_RANGE(1200000, 0x06, 0x07, 150000), /* Ranges when VOLT_SEL bit is 1 */
REGULATOR_LINEAR_RANGE(675000, 0x0, 0x3, 100000),
REGULATOR_LINEAR_RANGE(1025000, 0x4, 0x5, 50000),
REGULATOR_LINEAR_RANGE(1175000, 0x6, 0x7, 150000),
};
/* * Range selector for first 3 linear ranges is 0x0 * and 0x1 for last 3 ranges.
*/ staticconstunsignedint bd71837_buck5_volt_range_sel[] = {
0x0, 0x0, 0x0, 0x1, 0x1, 0x1
};
staticint bd718x7_xvp_sanity_check(struct regulator_dev *rdev, int lim_uV, int severity)
{ /* * BD71837/47/50 ... (ICs supported by this driver) do not provide * warnings, only protection
*/ if (severity != REGULATOR_SEVERITY_PROT) {
dev_err(&rdev->dev, "Unsupported Under Voltage protection level\n"); return -EINVAL;
}
/* * And protection limit is not changeable. It can only be enabled * or disabled
*/ if (lim_uV) return -EINVAL;
return 0;
}
staticint bd718x7_set_ldo_uvp(struct regulator_dev *rdev, int lim_uV, int severity, bool enable)
{ int ldo_offset = rdev->desc->id - BD718XX_LDO1; int prot_bit, ret;
ret = bd718x7_xvp_sanity_check(rdev, lim_uV, severity); if (ret) return ret;
prot_bit = BD718XX_LDO1_VRMON80 << ldo_offset;
if (enable) return regmap_clear_bits(rdev->regmap, BD718XX_REG_MVRFLTMASK2,
prot_bit);
staticint bd718x7_get_buck_prot_reg(int id, int *reg)
{
if (id > BD718XX_BUCK8) {
WARN_ON(id > BD718XX_BUCK8); return -EINVAL;
}
if (id > BD718XX_BUCK4)
*reg = BD718XX_REG_MVRFLTMASK0; else
*reg = BD718XX_REG_MVRFLTMASK1;
return 0;
}
staticint bd718x7_get_buck_ovp_info(int id, int *reg, int *bit)
{ int ret;
ret = bd718x7_get_buck_prot_reg(id, reg); if (ret) return ret;
*bit = BIT((id % 4) * 2 + 1);
return 0;
}
staticint bd718x7_get_buck_uvp_info(int id, int *reg, int *bit)
{ int ret;
ret = bd718x7_get_buck_prot_reg(id, reg); if (ret) return ret;
*bit = BIT((id % 4) * 2);
return 0;
}
staticint bd718x7_set_buck_uvp(struct regulator_dev *rdev, int lim_uV, int severity, bool enable)
{ int bit, reg, ret;
ret = bd718x7_xvp_sanity_check(rdev, lim_uV, severity); if (ret) return ret;
ret = bd718x7_get_buck_uvp_info(rdev->desc->id, ®, &bit); if (ret) return ret;
if (enable) return regmap_clear_bits(rdev->regmap, reg, bit);
return regmap_set_bits(rdev->regmap, reg, bit);
}
staticint bd718x7_set_buck_ovp(struct regulator_dev *rdev, int lim_uV, int severity, bool enable)
{ int bit, reg, ret;
ret = bd718x7_xvp_sanity_check(rdev, lim_uV, severity); if (ret) return ret;
ret = bd718x7_get_buck_ovp_info(rdev->desc->id, ®, &bit); if (ret) return ret;
if (enable) return regmap_clear_bits(rdev->regmap, reg, bit);
return regmap_set_bits(rdev->regmap, reg, bit);
}
/* * OPS common for BD71847 and BD71850
*/
BD718XX_OPS(bd718xx_pickable_range_ldo_ops,
regulator_list_voltage_pickable_linear_range, NULL,
bd718xx_set_voltage_sel_pickable_restricted,
regulator_get_voltage_sel_pickable_regmap, NULL, NULL,
bd718x7_set_ldo_uvp, NULL);
/* BD71847 and BD71850 LDO 5 is by default OFF at RUN state */ staticconststruct regulator_ops bd718xx_ldo5_ops_hwstate = {
.is_enabled = never_enabled_by_hwstate,
.list_voltage = regulator_list_voltage_pickable_linear_range,
.set_voltage_sel = bd718xx_set_voltage_sel_pickable_restricted,
.get_voltage_sel = regulator_get_voltage_sel_pickable_regmap,
.set_under_voltage_protection = bd718x7_set_ldo_uvp,
};
BD718XX_OPS(bd71837_buck_regulator_nolinear_ops, regulator_list_voltage_table,
regulator_map_voltage_ascend, rohm_regulator_set_voltage_sel_restricted,
regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
NULL, bd718x7_set_buck_uvp, bd718x7_set_buck_ovp); /* * BD71837 bucks 3 and 4 support defining their enable/disable state also * when buck enable state is under HW state machine control. In that case the * bit [2] in CTRL register is used to indicate if regulator should be ON.
*/ staticconststruct regulator_ops bd71837_buck34_ops_hwctrl = {
.is_enabled = bd71837_get_buck34_enable_hwctrl,
.list_voltage = regulator_list_voltage_linear_range,
.set_voltage_sel = regulator_set_voltage_sel_regmap,
.get_voltage_sel = regulator_get_voltage_sel_regmap,
.set_voltage_time_sel = regulator_set_voltage_time_sel,
.set_ramp_delay = regulator_set_ramp_delay_regmap,
.set_under_voltage_protection = bd718x7_set_buck_uvp,
.set_over_voltage_protection = bd718x7_set_buck_ovp,
};
/* * OPS for all of the ICs - BD718(37/47/50)
*/
BD718XX_OPS(bd718xx_dvs_buck_regulator_ops, regulator_list_voltage_linear_range,
NULL, regulator_set_voltage_sel_regmap,
regulator_get_voltage_sel_regmap, regulator_set_voltage_time_sel,
regulator_set_ramp_delay_regmap, bd718x7_set_buck_uvp,
bd718x7_set_buck_ovp);
/* * There is a HW quirk in BD71837. The shutdown sequence timings for * bucks/LDOs which are controlled via register interface are changed. * At PMIC poweroff the voltage for BUCK6/7 is cut immediately at the * beginning of shut-down sequence. As bucks 6 and 7 are parent * supplies for LDO5 and LDO6 - this causes LDO5/6 voltage * monitoring to errorneously detect under voltage and force PMIC to * emergency state instead of poweroff. In order to avoid this we * disable voltage monitoring for LDO5 and LDO6
*/ staticconststruct reg_init bd71837_ldo5_inits[] = {
{
.reg = BD718XX_REG_MVRFLTMASK2,
.mask = BD718XX_LDO5_VRMON80,
.val = BD718XX_LDO5_VRMON80,
},
};
/* * Setups where regulator (especially the buck8) output voltage is scaled * by adding external connection where some other regulator output is connected * to feedback-pin (over suitable resistors) is getting popular amongst users * of BD71837. (This allows for example scaling down the buck8 voltages to suit * lover GPU voltages for projects where buck8 is (ab)used to supply power * for GPU. Additionally some setups do allow DVS for buck8 but as this do * produce voltage spikes the HW must be evaluated to be able to survive this * - hence I keep the DVS disabled for non DVS bucks by default. I don't want * to help you burn your proto board) * * So we allow describing this external connection from DT and scale the * voltages accordingly. This is what the connection should look like: * * |------------| * | buck 8 |-------+----->Vout * | | | * |------------| | * | FB pin | * | | * +-------+--R2---+ * | * R1 * | * V FB-pull-up * * Here the buck output is sifted according to formula: * * Vout_o = Vo - (Vpu - Vo)*R2/R1 * Linear_step = step_orig*(R1+R2)/R1 * * where: * Vout_o is adjusted voltage output at vsel reg value 0 * Vo is original voltage output at vsel reg value 0 * Vpu is the pull-up voltage V FB-pull-up in the picture * R1 and R2 are resistor values. * * As a real world example for buck8 and a specific GPU: * VLDO = 1.6V (used as FB-pull-up) * R1 = 1000ohms * R2 = 150ohms * VSEL 0x0 => 0.8V – (VLDO – 0.8) * R2 / R1 = 0.68V * Linear Step = 10mV * (R1 + R2) / R1 = 11.5mV
*/ staticint setup_feedback_loop(struct device *dev, struct device_node *np, struct bd718xx_regulator_data *reg_data, unsignedint num_reg_data, int fb_uv)
{ int i, r1, r2, ret;
/* * We do adjust the values in the global desc based on DT settings. * This may not be best approach as it can cause problems if more than * one PMIC is controlled from same processor. I don't see such use-case * for BD718x7 now - so we spare some bits. * * If this will point out to be a problem - then we can allocate new * bd718xx_regulator_data array at probe and just use the global * array as a template where we copy initial values. Then we can * use allocated descs for regultor registration and do IC specific * modifications to this copy while leaving other PMICs untouched. But * that means allocating new array for each PMIC - and currently I see * no need for that.
*/
for (i = 0; i < num_reg_data; i++) { struct regulator_desc *desc = ®_data[i].desc; int j;
if (!of_node_name_eq(np, desc->of_match)) continue;
/* The feedback loop connection does not make sense for LDOs */ if (desc->id >= BD718XX_LDO1) return -EINVAL;
ret = of_property_read_u32(np, "rohm,feedback-pull-up-r1-ohms",
&r1); if (ret) return ret;
if (!r1) return -EINVAL;
ret = of_property_read_u32(np, "rohm,feedback-pull-up-r2-ohms",
&r2); if (ret) return ret;
if (desc->n_linear_ranges && desc->linear_ranges) { struct linear_range *new;
new = devm_kzalloc(dev, desc->n_linear_ranges * sizeof(struct linear_range),
GFP_KERNEL); if (!new) return -ENOMEM;
for (j = 0; j < desc->n_linear_ranges; j++) { int min = desc->linear_ranges[j].min; int step = desc->linear_ranges[j].step;
dev_dbg(dev, "%s: old range min %d, step %d\n",
desc->name, desc->linear_ranges[j].min,
desc->linear_ranges[j].step);
dev_dbg(dev, "new range min %d, step %d\n", min,
step);
}
desc->linear_ranges = new;
}
dev_dbg(dev, "regulator '%s' has FB pull-up configured\n",
desc->name);
return 0;
}
return -ENODEV;
}
staticint get_special_regulators(struct device *dev, struct bd718xx_regulator_data *reg_data, unsignedint num_reg_data, int *info)
{ int ret; int uv;
*info = 0;
struct device_node *nproot __free(device_node) = of_get_child_by_name(dev->of_node, "regulators"); if (!nproot) {
dev_err(dev, "failed to find regulators node\n"); return -ENODEV;
}
for_each_child_of_node_scoped(nproot, np) { if (of_property_read_bool(np, "rohm,no-regulator-enable-control"))
mark_hw_controlled(dev, np, reg_data, num_reg_data,
info);
ret = of_property_read_u32(np, "rohm,fb-pull-up-microvolt",
&uv); if (ret) { if (ret == -EINVAL) continue; else return ret;
}
ret = setup_feedback_loop(dev, np, reg_data, num_reg_data, uv); if (ret) return ret;
}
/* * Change the next stage from poweroff to be READY instead of SNVS * for all reset types because OTP loading at READY will clear SEL * bit allowing HW defaults for power rails to be used
*/ if (!use_snvs) {
err = regmap_update_bits(regmap, BD718XX_REG_TRANS_COND1,
BD718XX_ON_REQ_POWEROFF_MASK |
BD718XX_SWRESET_POWEROFF_MASK |
BD718XX_WDOG_POWEROFF_MASK |
BD718XX_KEY_L_POWEROFF_MASK,
BD718XX_POWOFF_TO_RDY); if (err) return dev_err_probe(&pdev->dev, err, "Failed to change reset target\n");
dev_dbg(&pdev->dev, "Changed all resets from SVNS to READY\n");
}
config.dev = pdev->dev.parent;
config.regmap = regmap; /* * There are cases when we want to leave the enable-control for * the HW state machine and use this driver only for voltage control. * One special case is when we use PMIC_STBY_REQ line from SoC to PMIC * in order to set the system to SUSPEND state. * * If regulator is taken under SW control the regulator state will not * be affected by PMIC state machine - Eg. regulator is likely to stay * on even in SUSPEND
*/
err = get_special_regulators(pdev->dev.parent, reg_data, num_reg_data,
&omit_enable); if (err) return err;
if (no_enable_control)
desc->ops = hwops[i]; else
desc->ops = swops[i];
rdev = devm_regulator_register(&pdev->dev, desc, &config); if (IS_ERR(rdev)) return dev_err_probe(&pdev->dev, PTR_ERR(rdev), "failed to register %s regulator\n",
desc->name);
/* * Regulator register gets the regulator constraints and * applies them (set_machine_constraints). This should have * turned the control register(s) to correct values and we * can now switch the control from PMIC state machine to the * register interface * * At poweroff transition PMIC HW disables EN bit for * regulators but leaves SEL bit untouched. So if state * transition from POWEROFF is done to SNVS - then all power * rails controlled by SW (having SEL bit set) stay disabled * as EN is cleared. This will result boot failure if any * crucial systems are powered by these rails. We don't * enable SW control for crucial regulators if snvs state is * used
*/ if (!no_enable_control && (!use_snvs ||
!rdev->constraints->always_on ||
!rdev->constraints->boot_on)) {
err = regmap_update_bits(regmap, r->init.reg,
r->init.mask, r->init.val); if (err) return dev_err_probe(&pdev->dev, err, "Failed to take control for (%s)\n",
desc->name);
} for (j = 0; j < r->additional_init_amnt; j++) {
err = regmap_update_bits(regmap,
r->additional_inits[j].reg,
r->additional_inits[j].mask,
r->additional_inits[j].val); if (err) return dev_err_probe(&pdev->dev, err, "Buck (%s) initialization failed\n",
desc->name);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.