regulator_get_value() returns READ_VOUT, the output as measured. On a regulator with a load line (droop) configured, that sits below the setpoint under load, so a caller cannot tell from it what regulator_set_value() last wrote, nor whether the setpoint already has the value it wants.
Add pmbus_regulator_read_vout_command(): let's read VOUT_COMMAND (21h) and decode it exactly like READ_VOUT, through the chip's pmbus_driver_info and VOUT_MODE, without exposing pmbus_regulator_priv. Signed-off-by: Vincent Jardin <[email protected]> --- drivers/power/regulator/pmbus_helper.c | 53 ++++++++++++++++++++------ include/pmbus.h | 14 +++++++ 2 files changed, 56 insertions(+), 11 deletions(-) diff --git a/drivers/power/regulator/pmbus_helper.c b/drivers/power/regulator/pmbus_helper.c index 1809c6cdfb5..6c76ee91c9c 100644 --- a/drivers/power/regulator/pmbus_helper.c +++ b/drivers/power/regulator/pmbus_helper.c @@ -34,12 +34,28 @@ static int pmbus_regulator_select_page(struct pmbus_regulator_priv *priv) return dm_i2c_write(priv->i2c_dev, PMBUS_PAGE, &p, 1); } +static int pmbus_regulator_vout_to_uv(struct pmbus_regulator_priv *priv, + u16 raw, u8 vout_mode) +{ + s64 uv; + + if (priv->info) + uv = pmbus_reg2data(priv->info, PSC_VOLTAGE_OUT, raw, vout_mode); + else + uv = pmbus_reg2data_linear16(raw, vout_mode); + + if (uv > INT_MAX) + uv = INT_MAX; + if (uv < INT_MIN) + uv = INT_MIN; + return (int)uv; +} + static int pmbus_regulator_get_value(struct udevice *dev) { struct pmbus_regulator_priv *priv = dev_get_priv(dev); u8 vout_mode = 0; u16 raw = 0; - s64 uv; int ret; ret = pmbus_regulator_select_page(priv); @@ -49,16 +65,7 @@ static int pmbus_regulator_get_value(struct udevice *dev) if (pmbus_read_word(priv->i2c_dev, PMBUS_READ_VOUT, &raw)) return -EIO; - if (priv->info) - uv = pmbus_reg2data(priv->info, PSC_VOLTAGE_OUT, raw, vout_mode); - else - uv = pmbus_reg2data_linear16(raw, vout_mode); - - if (uv > INT_MAX) - uv = INT_MAX; - if (uv < INT_MIN) - uv = INT_MIN; - return (int)uv; + return pmbus_regulator_vout_to_uv(priv, raw, vout_mode); } static int pmbus_regulator_set_value(struct udevice *dev, int uV) @@ -187,6 +194,30 @@ int pmbus_regulator_read_temp(struct udevice *reg_dev, int *temp_mc) return 0; } +int pmbus_regulator_read_vout_command(struct udevice *reg_dev, int *uV) +{ + struct pmbus_regulator_priv *priv; + u8 vout_mode = 0; + u16 raw = 0; + int ret; + + if (!reg_dev || !uV) + return -EINVAL; + priv = pmbus_regulator_dev_priv(reg_dev); + if (!priv || !priv->i2c_dev) + return -ENODEV; + + ret = pmbus_regulator_select_page(priv); + if (ret) + return ret; + if (pmbus_read_byte(priv->i2c_dev, PMBUS_VOUT_MODE, &vout_mode) || + pmbus_read_word(priv->i2c_dev, PMBUS_VOUT_COMMAND, &raw)) + return -EIO; + + *uV = pmbus_regulator_vout_to_uv(priv, raw, vout_mode); + return 0; +} + enum pmbus_data_format pmbus_regulator_identify_vout(struct udevice *i2c_dev, struct pmbus_driver_info *info) diff --git a/include/pmbus.h b/include/pmbus.h index bb85a61e62a..e91d97a0b10 100644 --- a/include/pmbus.h +++ b/include/pmbus.h @@ -466,6 +466,20 @@ void pmbus_print_status_word(struct udevice *chip); */ int pmbus_regulator_read_temp(struct udevice *reg_dev, int *temp_mc); +/* + * pmbus_regulator_read_vout_command() - the output voltage setpoint + * + * Read VOUT_COMMAND (21h), decoded like READ_VOUT through the chip's + * pmbus_driver_info and VOUT_MODE. regulator_get_value() returns + * READ_VOUT, the output as measured, which on a regulator with a load + * line (droop) sits below the setpoint under load; this returns the + * setpoint itself, which is what regulator_set_value() writes. + * + * Returns 0 on success, or a negative errno (-ENODEV if reg is not a + * probed pmbus regulator, -EIO on bus error). + */ +int pmbus_regulator_read_vout_command(struct udevice *reg_dev, int *uV); + /* * Look up the pmbus_driver_info of a probed UCLASS_REGULATOR device at * (bus_seq, addr) that is driven by a pmbus_helper based chip driver -- 2.43.0
