Re: [PATCH] [WAR] hwmon: (ina3221) Apply software WAR to offset shunt voltage

From: Guenter Roeck
Date: Thu Nov 17 2022 - 09:47:19 EST


On Thu, Nov 17, 2022 at 04:32:26PM +0800, Ninad Malwade wrote:
> This is used as a software WAR to offset shunt voltage reading

What is a software WAR ? Or a WAR in the first place ?
What is the relevance of the "[WAR]" tag in the subject ?

None of the definitions at https://acronyms.thefreedictionary.com/WAR
seem to apply. I am sure it means something for you, and it seems to be
important enough to use the term repeatedly, but please do not assume
that others know what it means.

> from INA3221 to increase its accuracy. This patch implements a
> previous downstream feature by reading the offset information
> from DT and apply it to current readings.
>

Where is the devicetree documentation ?

> Signed-off-by: Ninad Malwade <nmalwade@xxxxxxxxxx>
> ---
> drivers/hwmon/ina3221.c | 141 ++++++++++++++++++++++++++++++++++++++--
> 1 file changed, 137 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/hwmon/ina3221.c b/drivers/hwmon/ina3221.c
> index e06186986444..726c8b99b8cd 100644
> --- a/drivers/hwmon/ina3221.c
> +++ b/drivers/hwmon/ina3221.c
> @@ -94,13 +94,39 @@ enum ina3221_channels {
> INA3221_NUM_CHANNELS
> };
>
> +/**
> + * struct shuntv_offset_range - [WAR] shunt voltage offset sub-range

Still no explanation what WAR actually stands for.

> + * @start: range start (uV)
> + * @end: range end (uV)
> + * @offset: offset for the current sub-range
> + */
> +struct shuntv_offset_range {
> + s32 start;
> + s32 end;
> + s32 offset;
> +};
> +
> +/**
> + * struct shuntv_offset - [WAR] shunt voltage offset information
> + * @offset: general offset
> + * @range: pointer to a sub-range of shunt voltage offset (uV)
> + * @num_range: number of sub-ranges of shunt voltage offset
> + */
> +struct shuntv_offset {
> + s32 offset;
> + struct shuntv_offset_range *range;
> + s32 num_range;
> +};
> +
> /**
> * struct ina3221_input - channel input source specific information
> + * @shuntv_offset: [WAR] shunt voltage offset information
> * @label: label of channel input source
> * @shunt_resistor: shunt resistor value of channel input source
> * @disconnected: connection status of channel input source
> */
> struct ina3221_input {
> + struct shuntv_offset *shuntv_offset;
> const char *label;
> int shunt_resistor;
> bool disconnected;
> @@ -329,7 +355,7 @@ static int ina3221_read_curr(struct device *dev, u32 attr,
> struct ina3221_data *ina = dev_get_drvdata(dev);
> struct ina3221_input *input = ina->inputs;
> u8 reg = ina3221_curr_reg[attr][channel];
> - int resistance_uo, voltage_nv;
> + int resistance_uo, voltage_uv;
> int regval, ret;
>
> if (channel > INA3221_CHANNEL3)
> @@ -362,10 +388,34 @@ static int ina3221_read_curr(struct device *dev, u32 attr,
> if (ret)
> return ret;
>
> - /* Scale of shunt voltage: LSB is 40uV (40000nV) */
> - voltage_nv = regval * 40000;
> + /* Scale of shunt voltage: LSB is 40uV */
> + voltage_uv = regval * 40;
> +
> + /* Apply software WAR to offset shunt voltage for accuracy */
> + if (input->shuntv_offset) {
> + struct shuntv_offset_range *range =
> + input->shuntv_offset->range;
> + int num_range = input->shuntv_offset->num_range;
> + int offset = input->shuntv_offset->offset;
> +
> + while (num_range--) {
> + if (voltage_uv >= range->start &&
> + voltage_uv <= range->end) {
> + /* Use range offset instead */
> + offset = range->offset;
> + break;
> + }
> + range++;
> + }
> +
> + if (voltage_uv < 0)
> + voltage_uv += offset;
> + else
> + voltage_uv -= offset;
> + }
> +
> /* Return current in mA */
> - *val = DIV_ROUND_CLOSEST(voltage_nv, resistance_uo);
> + *val = DIV_ROUND_CLOSEST(voltage_uv * 1000, resistance_uo);
> return 0;
> case hwmon_curr_crit_alarm:
> case hwmon_curr_max_alarm:
> @@ -758,6 +808,84 @@ static const struct regmap_config ina3221_regmap_config = {
> .volatile_table = &ina3221_volatile_table,
> };
>
> +static struct shuntv_offset *
> +ina3221_probe_shuntv_offset_from_dt(struct device *dev,
> + struct device_node *child)
> +{
> + struct device_node *np, *range_np;
> + struct shuntv_offset *shuntv_offset;
> + struct shuntv_offset_range *range;
> + s32 start, end, offset;
> + const __be32 *prop;
> + int ret, num_range;
> +
> + prop = of_get_property(child, "shunt-volt-offset-uv", NULL);
> + /* Silently return for devices with no need of an offset WAR */
> + if (!prop)
> + return NULL;
> +
> + np = of_find_node_by_phandle(be32_to_cpup(prop));
> + if (!np) {
> + dev_err(dev, "corrupted phandle for shunt-volt-offset-uv\n");
> + return ERR_PTR(-ENODEV);
> + }
> +
> + ret = of_property_read_s32(np, "offset", &offset);
> + if (ret) {
> + dev_err(dev, "failed to read general shuntv offset\n");
> + return ERR_PTR(-ENODEV);
> + }
> +
> + shuntv_offset = devm_kzalloc(dev, sizeof(*shuntv_offset), GFP_KERNEL);
> + if (!shuntv_offset)
> + return ERR_PTR(-ENOMEM);
> +
> + shuntv_offset->offset = offset;
> +
> + num_range = of_get_child_count(np);
> +
> + /* Return upon no sub-range found */
> + if (!num_range)
> + return shuntv_offset;
> +
> + range = devm_kzalloc(dev, sizeof(*range) * num_range, GFP_KERNEL);
> + if (!range)
> + return ERR_PTR(-ENOMEM);
> +
> + shuntv_offset->range = range;
> + shuntv_offset->num_range = num_range;
> +
> + for_each_child_of_node(np, range_np) {
> + ret = of_property_read_s32(range_np, "start", &start);
> + if (ret) {
> + dev_warn(dev, "missing start in range node\n");
> + range++;
> + continue;
> + }
> +
> + ret = of_property_read_s32(range_np, "end", &end);
> + if (ret) {
> + dev_warn(dev, "missing end in range node\n");
> + range++;
> + continue;
> + }
> +
> + ret = of_property_read_s32(range_np, "offset", &offset);
> + if (ret) {
> + dev_warn(dev, "missing offset in range node\n");
> + range++;
> + continue;
> + }

Not sure if that would be an acceptable devicetree binding. The bindings
will need to be submitted, reviewed, and accepted by a devicetree maintainer.

> +
> + range->start = start;
> + range->end = end;
> + range->offset = offset;
> + range++;
> + }
> +
> + return shuntv_offset;
> +}
> +
> static int ina3221_probe_child_from_dt(struct device *dev,
> struct device_node *child,
> struct ina3221_data *ina)
> @@ -796,6 +924,11 @@ static int ina3221_probe_child_from_dt(struct device *dev,
> input->shunt_resistor = val;
> }
>
> + /* Apply software WAR to offset shunt voltage for accuracy */
> + input->shuntv_offset = ina3221_probe_shuntv_offset_from_dt(dev, child);
> + if (IS_ERR(input->shuntv_offset))
> + return PTR_ERR(input->shuntv_offset);
> +
> return 0;
> }
>
> --
> 2.17.1
>