@@ -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:
@@ -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);
}
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@appear.net> --- drivers/hwmon/pmbus/pmbus.c | 2 +- drivers/hwmon/pmbus/pmbus_core.c | 85 +++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 12 deletions(-)