Skip to content

Commit d2ad981

Browse files
M-Vaittinenbroonie
authored andcommitted
regulator: bd718x7: Support external connection to scale voltages
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. As a note - some setups do allow DVS for buck8. This do produce voltage spikes and the HW must be evaluated to be able to survive them. Thus this commit still keep the DVS disabled for non DVS bucks by default. Let's not help you burn your proto board. 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. Bring support for specifying the Vpu, R1 and R2 from device tree and scale voltages if they are given. Signed-off-by: Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com> Link: https://lore.kernel.org/r/89b2be87074f307a8823f15f34e1f662023cbf36.1604994184.git.matti.vaittinen@fi.rohmeurope.com Signed-off-by: Mark Brown <broonie@kernel.org>
1 parent b54a27d commit d2ad981

File tree

1 file changed

+157
-7
lines changed

1 file changed

+157
-7
lines changed

drivers/regulator/bd718x7-regulator.c

Lines changed: 157 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,13 +1323,142 @@ static void mark_hw_controlled(struct device *dev, struct device_node *np,
13231323
dev_warn(dev, "Bad regulator node\n");
13241324
}
13251325

1326-
static int get_hw_controlled_regulators(struct device *dev,
1327-
struct bd718xx_regulator_data *reg_data,
1328-
unsigned int num_reg_data, int *info)
1326+
/*
1327+
* Setups where regulator (especially the buck8) output voltage is scaled
1328+
* by adding external connection where some other regulator output is connected
1329+
* to feedback-pin (over suitable resistors) is getting popular amongst users
1330+
* of BD71837. (This allows for example scaling down the buck8 voltages to suit
1331+
* lover GPU voltages for projects where buck8 is (ab)used to supply power
1332+
* for GPU. Additionally some setups do allow DVS for buck8 but as this do
1333+
* produce voltage spikes the HW must be evaluated to be able to survive this
1334+
* - hence I keep the DVS disabled for non DVS bucks by default. I don't want
1335+
* to help you burn your proto board)
1336+
*
1337+
* So we allow describing this external connection from DT and scale the
1338+
* voltages accordingly. This is what the connection should look like:
1339+
*
1340+
* |------------|
1341+
* | buck 8 |-------+----->Vout
1342+
* | | |
1343+
* |------------| |
1344+
* | FB pin |
1345+
* | |
1346+
* +-------+--R2---+
1347+
* |
1348+
* R1
1349+
* |
1350+
* V FB-pull-up
1351+
*
1352+
* Here the buck output is sifted according to formula:
1353+
*
1354+
* Vout_o = Vo - (Vpu - Vo)*R2/R1
1355+
* Linear_step = step_orig*(R1+R2)/R1
1356+
*
1357+
* where:
1358+
* Vout_o is adjusted voltage output at vsel reg value 0
1359+
* Vo is original voltage output at vsel reg value 0
1360+
* Vpu is the pull-up voltage V FB-pull-up in the picture
1361+
* R1 and R2 are resistor values.
1362+
*
1363+
* As a real world example for buck8 and a specific GPU:
1364+
* VLDO = 1.6V (used as FB-pull-up)
1365+
* R1 = 1000ohms
1366+
* R2 = 150ohms
1367+
* VSEL 0x0 => 0.8V – (VLDO – 0.8) * R2 / R1 = 0.68V
1368+
* Linear Step = 10mV * (R1 + R2) / R1 = 11.5mV
1369+
*/
1370+
static int setup_feedback_loop(struct device *dev, struct device_node *np,
1371+
struct bd718xx_regulator_data *reg_data,
1372+
unsigned int num_reg_data, int fb_uv)
13291373
{
1374+
int i, r1, r2, ret;
1375+
1376+
/*
1377+
* We do adjust the values in the global desc based on DT settings.
1378+
* This may not be best approach as it can cause problems if more than
1379+
* one PMIC is controlled from same processor. I don't see such use-case
1380+
* for BD718x7 now - so we spare some bits.
1381+
*
1382+
* If this will point out to be a problem - then we can allocate new
1383+
* bd718xx_regulator_data array at probe and just use the global
1384+
* array as a template where we copy initial values. Then we can
1385+
* use allocated descs for regultor registration and do IC specific
1386+
* modifications to this copy while leaving other PMICs untouched. But
1387+
* that means allocating new array for each PMIC - and currently I see
1388+
* no need for that.
1389+
*/
1390+
1391+
for (i = 0; i < num_reg_data; i++) {
1392+
struct regulator_desc *desc = &reg_data[i].desc;
1393+
int j;
1394+
1395+
if (!of_node_name_eq(np, desc->of_match))
1396+
continue;
1397+
1398+
pr_info("Looking at node '%s'\n", desc->of_match);
1399+
1400+
/* The feedback loop connection does not make sense for LDOs */
1401+
if (desc->id >= BD718XX_LDO1)
1402+
return -EINVAL;
1403+
1404+
ret = of_property_read_u32(np, "rohm,feedback-pull-up-r1-ohms",
1405+
&r1);
1406+
if (ret)
1407+
return ret;
1408+
1409+
if (!r1)
1410+
return -EINVAL;
1411+
1412+
ret = of_property_read_u32(np, "rohm,feedback-pull-up-r2-ohms",
1413+
&r2);
1414+
if (ret)
1415+
return ret;
1416+
1417+
if (desc->n_linear_ranges && desc->linear_ranges) {
1418+
struct linear_range *new;
1419+
1420+
new = devm_kzalloc(dev, desc->n_linear_ranges *
1421+
sizeof(struct linear_range),
1422+
GFP_KERNEL);
1423+
if (!new)
1424+
return -ENOMEM;
1425+
1426+
for (j = 0; j < desc->n_linear_ranges; j++) {
1427+
int min = desc->linear_ranges[j].min;
1428+
int step = desc->linear_ranges[j].step;
1429+
1430+
min -= (fb_uv - min)*r2/r1;
1431+
step = step * (r1 + r2);
1432+
step /= r1;
1433+
1434+
new[j].min = min;
1435+
new[j].step = step;
1436+
1437+
dev_dbg(dev, "%s: old range min %d, step %d\n",
1438+
desc->name, desc->linear_ranges[j].min,
1439+
desc->linear_ranges[j].step);
1440+
dev_dbg(dev, "new range min %d, step %d\n", min,
1441+
step);
1442+
}
1443+
desc->linear_ranges = new;
1444+
}
1445+
dev_dbg(dev, "regulator '%s' has FB pull-up configured\n",
1446+
desc->name);
1447+
1448+
return 0;
1449+
}
1450+
1451+
return -ENODEV;
1452+
}
1453+
1454+
static int get_special_regulators(struct device *dev,
1455+
struct bd718xx_regulator_data *reg_data,
1456+
unsigned int num_reg_data, int *info)
1457+
{
1458+
int ret;
13301459
struct device_node *np;
13311460
struct device_node *nproot = dev->of_node;
1332-
const char *prop = "rohm,no-regulator-enable-control";
1461+
int uv;
13331462

13341463
*info = 0;
13351464

@@ -1338,13 +1467,32 @@ static int get_hw_controlled_regulators(struct device *dev,
13381467
dev_err(dev, "failed to find regulators node\n");
13391468
return -ENODEV;
13401469
}
1341-
for_each_child_of_node(nproot, np)
1342-
if (of_property_read_bool(np, prop))
1470+
for_each_child_of_node(nproot, np) {
1471+
if (of_property_read_bool(np, "rohm,no-regulator-enable-control"))
13431472
mark_hw_controlled(dev, np, reg_data, num_reg_data,
13441473
info);
1474+
ret = of_property_read_u32(np, "rohm,fb-pull-up-microvolt",
1475+
&uv);
1476+
if (ret) {
1477+
if (ret == -EINVAL)
1478+
continue;
1479+
else
1480+
goto err_out;
1481+
}
1482+
1483+
ret = setup_feedback_loop(dev, np, reg_data, num_reg_data, uv);
1484+
if (ret)
1485+
goto err_out;
1486+
}
13451487

13461488
of_node_put(nproot);
13471489
return 0;
1490+
1491+
err_out:
1492+
of_node_put(np);
1493+
of_node_put(nproot);
1494+
1495+
return ret;
13481496
}
13491497

13501498
static int bd718xx_probe(struct platform_device *pdev)
@@ -1432,8 +1580,10 @@ static int bd718xx_probe(struct platform_device *pdev)
14321580
* be affected by PMIC state machine - Eg. regulator is likely to stay
14331581
* on even in SUSPEND
14341582
*/
1435-
get_hw_controlled_regulators(pdev->dev.parent, reg_data, num_reg_data,
1583+
err = get_special_regulators(pdev->dev.parent, reg_data, num_reg_data,
14361584
&omit_enable);
1585+
if (err)
1586+
return err;
14371587

14381588
for (i = 0; i < num_reg_data; i++) {
14391589

0 commit comments

Comments
 (0)