PMBus 1.3 allows for relative encoding of voltages in certain output voltage related commands. These commands are encoded relative to VOUT_COMMAND. Support this when encoding/decoding commands. Signed-off-by: Lars Petter Mostad <lars.petter.mostad@xxxxxxxxxx> --- drivers/hwmon/pmbus/pmbus.c | 2 +- drivers/hwmon/pmbus/pmbus_core.c | 85 +++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 12 deletions(-) diff --git a/drivers/hwmon/pmbus/pmbus.c b/drivers/hwmon/pmbus/pmbus.c index ec40c5c59954..c9ad83d8d2ca 100644 --- a/drivers/hwmon/pmbus/pmbus.c +++ b/drivers/hwmon/pmbus/pmbus.c @@ -121,7 +121,7 @@ static int pmbus_identify(struct i2c_client *client, vout_mode = pmbus_read_byte_data(client, 0, PMBUS_VOUT_MODE); if (vout_mode >= 0 && vout_mode != 0xff) { - switch (vout_mode >> 5) { + switch ((vout_mode & 0x60) >> 5) { case 0: break; case 1: diff --git a/drivers/hwmon/pmbus/pmbus_core.c b/drivers/hwmon/pmbus/pmbus_core.c index cb4c65a7f288..4108e1684250 100644 --- a/drivers/hwmon/pmbus/pmbus_core.c +++ b/drivers/hwmon/pmbus/pmbus_core.c @@ -87,6 +87,8 @@ struct pmbus_data { int exponent[PMBUS_PAGES]; /* linear mode: exponent for output voltages */ + bool vout_mode_relative[PMBUS_PAGES]; + s64 vout_command[PMBUS_PAGES]; const struct pmbus_driver_info *info; @@ -644,6 +646,21 @@ static void pmbus_update_sensor_data(struct i2c_client *client, struct pmbus_sen sensor->phase, sensor->reg); } +static bool pmbus_reg_is_vout_command_relative(u16 reg) +{ + switch (reg) { + case PMBUS_VOUT_MARGIN_HIGH: + case PMBUS_VOUT_MARGIN_LOW: + case PMBUS_VOUT_OV_FAULT_LIMIT: + case PMBUS_VOUT_OV_WARN_LIMIT: + case PMBUS_VOUT_UV_WARN_LIMIT: + case PMBUS_VOUT_UV_FAULT_LIMIT: + return true; + default: + return false; + } +} + /* * Convert ieee754 sensor values to milli- or micro-units * depending on sensor type. @@ -842,6 +859,14 @@ static s64 pmbus_reg2data(struct pmbus_data *data, struct pmbus_sensor *sensor) val = pmbus_reg2data_linear(data, sensor); break; } + + if (sensor->class == PSC_VOLTAGE_OUT && + data->vout_mode_relative[sensor->page] && + pmbus_reg_is_vout_command_relative(sensor->reg)) { + /* At this point val is the voltage as a permillage of VOUT_COMMAND */ + val = DIV_ROUND_CLOSEST_ULL(val * data->vout_command[sensor->page], 1000); + } + return val; } @@ -1028,6 +1053,12 @@ static u16 pmbus_data2reg(struct pmbus_data *data, if (!sensor->convert) return val; + if (sensor->class == PSC_VOLTAGE_OUT && + data->vout_mode_relative[sensor->page] && + pmbus_reg_is_vout_command_relative(sensor->reg)) { + val = DIV_ROUND_CLOSEST_ULL(val * 1000, data->vout_command[sensor->page]); + } + switch (data->info->format[sensor->class]) { case direct: regval = pmbus_data2reg_direct(data, sensor, val); @@ -2500,6 +2531,20 @@ static int pmbus_init_coefficients(struct i2c_client *client, return 0; } +static void pmbus_init_vout_relative(struct pmbus_data *data, int page, + int vout_command_raw) +{ + struct pmbus_sensor s = { + .page = page, + .reg = PMBUS_VOUT_COMMAND, + .class = PSC_VOLTAGE_OUT, + .convert = true, + .data = vout_command_raw + }; + + data->vout_command[page] = pmbus_reg2data(data, &s); +} + /* * Identify chip parameters. * This function is called for all chips. @@ -2512,6 +2557,10 @@ static int pmbus_identify_common(struct i2c_client *client, if (pmbus_check_byte_register(client, page, PMBUS_VOUT_MODE)) vout_mode = _pmbus_read_byte_data(client, page, PMBUS_VOUT_MODE); + if (vout_mode >= 0 && vout_mode & 0x80) { + data->vout_mode_relative[page] = true; + vout_mode &= 0x7f; + } if (vout_mode >= 0 && vout_mode != 0xff) { /* * Not all chips support the VOUT_MODE command, @@ -2676,6 +2725,15 @@ static int pmbus_init_common(struct i2c_client *client, struct pmbus_data *data, return ret; } + for (page = 0; page < info->pages; page++) { + if (data->vout_mode_relative[page]) { + ret = _pmbus_read_word_data(client, page, 0xff, PMBUS_VOUT_COMMAND); + if (ret < 0) + return ret; + pmbus_init_vout_relative(data, page, ret); + } + } + if (client->flags & I2C_CLIENT_PEC) { /* * If I2C_CLIENT_PEC is set here, both the I2C adapter and the @@ -3070,22 +3128,27 @@ static int pmbus_regulator_set_voltage(struct regulator_dev *rdev, int min_uv, *selector = 0; - low = pmbus_regulator_get_low_margin(client, s.page); - if (low < 0) - return low; + if (!data->vout_mode_relative[s.page]) { + low = pmbus_regulator_get_low_margin(client, s.page); + if (low < 0) + return low; - high = pmbus_regulator_get_high_margin(client, s.page); - if (high < 0) - return high; + high = pmbus_regulator_get_high_margin(client, s.page); + if (high < 0) + return high; - /* Make sure we are within margins */ - if (low > val) - val = low; - if (high < val) - val = high; + /* Make sure we are within margins */ + if (low > val) + val = low; + if (high < val) + val = high; + } val = pmbus_data2reg(data, &s, val); + if (data->vout_mode_relative[s.page]) + pmbus_init_vout_relative(data, s.page, val); + return _pmbus_write_word_data(client, s.page, PMBUS_VOUT_COMMAND, (u16)val); } -- 2.44.0