iio: meter: ina3221: support to offset shunt volt
Rajkumar Kasirajan [Mon, 23 Nov 2015 10:29:50 +0000 (18:29 +0800)]
added support to offset shunt voltage reading to hangle INA
channel inaccuracy on high voltage rails.

example:
    p2180_shuntv_offset: shuntv-offset {
        offset = <40>;
        conditional_offset@0 {
            shunt_volt_start = <40>;
            shunt_volt_end = <120>;
            offset = <33>;
        };
    conditional_offset@1 {
            shunt_volt_start = <200>;
            shunt_volt_end = <400>;
            offset = <80>;
        };
};

channel@0 {
reg = <0x0>;
ti,rail-name = "VDD_IN";
ti,shunt-resistor-mohm = <20>;
ti,current-critical-limit-ma = <2105>;
shunt-volt-offset-uv = <&p2180_shuntv_offset>;
};

which means,
 if shunt voltage reading is  from 40 to 120, offset -33 will be applied
 if shunt voltage reading is  from 200 to 400, offset -80 will be applied
 for all other readings offset -40 will be applied.

Bug 1677375

Change-Id: I67c12a6b105011a0a8ca2aae2c5764df6e21ce8b
Signed-off-by: Rajkumar Kasirajan <rkasirajan@nvidia.com>
Reviewed-on: http://git-master/r/836475
(cherry picked from commit e1613a0534cc372160090e9e60b52349ffee9ade)
Reviewed-on: http://git-master/r/923383
Reviewed-by: Automatic_Commit_Validation_User
Reviewed-by: Ninad Malwade <nmalwade@nvidia.com>
Reviewed-by: Bharat Nihalani <bnihalani@nvidia.com>
GVS: Gerrit_Virtual_Submit

Documentation/devicetree/bindings/iio/meter/ina3221.txt
drivers/staging/iio/meter/ina3221.c

index 8d49102..44593e2 100644 (file)
@@ -22,6 +22,15 @@ Optional subnode properties:
 - ti,current-warning-limit-ma: Cureent warning limit for the given channel.
 - ti,current-critical-limit-ma: Current critical limit for the given channel.
 - ti,shunt-resistor-mohm: Shunt register in milli ohm.
+- shunt-volt-offset-uv: reference to shunt-volt-offset node
+
+shunt-volt-offset node properties:
+- offset: value to be subtracted from shunt voltage reading
+
+conditional_offset subnode properties:
+       - shunt_volt_start: conditional offset start voltage
+       - shunt_volt_end: conditional offset end voltage
+       - offset: value to be subtracted from <shunt_volt_start to shunt_volt_end> range
 
 IIO channel properties:
 It follows same mechanism for iio properties detailed on ../iio-bindings.txt
@@ -55,6 +64,15 @@ The INA3221_CHAN_INDEX defined as:
 #define INA3221_CHAN_INDEX(chan, type, add) (chan * 5 + INA3221_##type + INA3221_##add)
 
 Example:
+       p2180_shuntv_offset: shuntv-offset {
+               offset = <40>;
+               conditional_offset@0 {
+                       shunt_volt_start = <100>;
+                       shunt_volt_end = <1800>;
+                       offset = <80>;
+               };
+       };
+
        ina3221x@40 {
                compatible = "ti,ina3221x";
                reg = <0x40>;
@@ -70,6 +88,7 @@ Example:
                        ti,current-warning-limit-ma = <8000>;
                        ti,current-critical-limit-ma = <9000>;
                        ti,shunt-resistor-mohm = <1>;
+                       shunt-volt-offset-uv = <&p2180_shuntv_offset>;
                };
 
                channel@1 {
index 4177319..c91f12a 100644 (file)
@@ -102,11 +102,24 @@ enum mode {
 #define IS_TRIGGERED(x) (!((x) & 2))
 #define IS_CONTINUOUS(x) ((x) & 2)
 
+struct shuntv_conditional_offset {
+       s32 shuntv_start;
+       s32 shuntv_end;
+       s32 offset;
+};
+
+struct shunt_volt_offset {
+       s32 offset;
+       struct shuntv_conditional_offset *cond_offset;
+       s32 cond_offset_size;
+};
+
 struct ina3221_chan_pdata {
        const char *rail_name;
        u32 warn_conf_limits;
        u32 crit_conf_limits;
        u32 shunt_resistor;
+       struct shunt_volt_offset *shuntv_offset;
 };
 
 struct ina3221_platform_data {
@@ -159,12 +172,41 @@ static inline int busv_register_to_mv(u16 reg)
 }
 
 /* convert shunt voltage register value to current (in mA) */
-static int shuntv_register_to_ma(u16 reg, int resistance)
+static int shuntv_register_to_ma(u16 reg, int resistance,
+       struct shunt_volt_offset *shuntv_offset)
 {
        int uv, ma;
+       int offset = 0;
+       struct shuntv_conditional_offset *cond_offset;
+       int i;
 
        uv = (s16)reg;
-       uv = ((uv >> 3) * 40); /* LSB (4th bit) is 40uV */
+
+       if (uv < 0)
+               uv = ((((uv * -1) >> 3) * 40) * -1);
+       else
+               uv = ((uv >> 3) * 40); /* LSB (4th bit) is 40uV */
+
+       if (shuntv_offset != NULL) {
+               offset = shuntv_offset->offset;
+               cond_offset = shuntv_offset->cond_offset;
+
+               for (i = 0; i < shuntv_offset->cond_offset_size; i++) {
+                       if (uv >= cond_offset->shuntv_start &&
+                                       uv <= cond_offset->shuntv_end) {
+                               offset = cond_offset->offset;
+                               break;
+                       }
+
+                       cond_offset++;
+               }
+
+               /* apply shunt volt offset */
+               if (uv < 0)
+                       uv += offset;
+               else
+                       uv -= offset;
+       }
        /*
         * calculate uv/resistance with rounding knowing that C99 truncates
         * towards zero
@@ -385,7 +427,8 @@ static int ina3221_get_channel_current(struct ina3221_chip *chip,
                goto exit;
        }
        *current_ma = shuntv_register_to_ma(vsh,
-                        chip->pdata->cpdata[channel].shunt_resistor);
+                        chip->pdata->cpdata[channel].shunt_resistor,
+                               chip->pdata->cpdata[channel].shuntv_offset);
 exit:
        mutex_unlock(&chip->mutex);
        return ret;
@@ -418,7 +461,8 @@ static int ina3221_get_channel_power(struct ina3221_chip *chip,
        }
 
        current_ma = shuntv_register_to_ma(vsh,
-                       chip->pdata->cpdata[channel].shunt_resistor);
+                       chip->pdata->cpdata[channel].shunt_resistor,
+                               chip->pdata->cpdata[channel].shuntv_offset);
        voltage_mv = busv_register_to_mv(vbus);
        *power_mw = (voltage_mv * current_ma) / 1000;
 exit:
@@ -447,7 +491,8 @@ static int ina3221_get_channel_vbus_voltage_current(struct ina3221_chip *chip,
        }
 
        *current_ma = shuntv_register_to_ma(vsh,
-                       chip->pdata->cpdata[channel].shunt_resistor);
+                       chip->pdata->cpdata[channel].shunt_resistor,
+                               chip->pdata->cpdata[channel].shuntv_offset);
        *voltage_mv = busv_register_to_mv(vbus);
 exit:
        mutex_unlock(&chip->mutex);
@@ -547,7 +592,7 @@ static int ina3221_get_channel_critical(struct ina3221_chip *chip,
        }
 
        *curr_limit = shuntv_register_to_ma(be16_to_cpu(ret),
-                       cpdata->shunt_resistor);
+                       cpdata->shunt_resistor, cpdata->shuntv_offset);
        ret = 0;
 exit:
        mutex_unlock(&chip->mutex);
@@ -597,7 +642,7 @@ static int ina3221_get_channel_warning(struct ina3221_chip *chip,
 
        /* convert shunt voltage to current in mA */
        *curr_limit = shuntv_register_to_ma(be16_to_cpu(ret),
-                       cpdata->shunt_resistor);
+                       cpdata->shunt_resistor, cpdata->shuntv_offset);
        ret = 0;
 exit:
        mutex_unlock(&chip->mutex);
@@ -947,6 +992,76 @@ static const struct iio_info ina3221_info = {
        .read_raw = &ina3221_read_raw,
 };
 
+
+static struct shunt_volt_offset *ina3221_get_shuntv_offset
+       (struct i2c_client *client, struct device_node *channel_np)
+{
+
+       const __be32 *prop;
+       struct device_node *shuntv_np;
+       struct device_node *shuntv_cond_np;
+       struct shunt_volt_offset *shuntv_offset = NULL;
+       struct shuntv_conditional_offset *shuntv_cond_offset;
+       s32 shuntv_start, shuntv_end, offset;
+       int ret;
+
+       prop = of_get_property(channel_np, "shunt-volt-offset-uv", NULL);
+       if (prop != NULL) {
+               shuntv_np = of_find_node_by_phandle(be32_to_cpup(prop));
+               if (shuntv_np == NULL) {
+                       dev_err(&client->dev, "could not find shunt volt offset node\n");
+                       return NULL;
+               }
+
+       shuntv_offset = devm_kzalloc(&client->dev, sizeof(*shuntv_offset),
+                                               GFP_KERNEL);
+
+       ret = of_property_read_s32(shuntv_np, "offset", &offset);
+       if (!ret)
+               shuntv_offset->offset = offset;
+
+       shuntv_offset->cond_offset_size = of_get_child_count(shuntv_np);
+       if (shuntv_offset->cond_offset_size) {
+               shuntv_cond_offset = (struct shuntv_conditional_offset *)
+                       devm_kzalloc(&client->dev,
+                       sizeof(struct shuntv_conditional_offset)
+                       * shuntv_offset->cond_offset_size, GFP_KERNEL);
+               shuntv_offset->cond_offset = shuntv_cond_offset;
+
+               for_each_child_of_node(shuntv_np, shuntv_cond_np) {
+                       ret = of_property_read_s32(shuntv_cond_np,
+                                       "shunt_volt_start", &shuntv_start);
+                       if (ret) {
+                               dev_err(&client->dev, "property shunt_volt_start not found!\n");
+                               goto skip_node;
+                       }
+
+                       ret = of_property_read_s32(shuntv_cond_np,
+                                       "shunt_volt_end", &shuntv_end);
+                       if (ret) {
+                               dev_err(&client->dev, "property shunt_volt_end not found!\n");
+                               goto skip_node;
+                       }
+
+                       ret = of_property_read_s32(shuntv_cond_np,
+                                       "offset", &offset);
+                       if (ret) {
+                               dev_err(&client->dev, "property offset not found!\n");
+                               goto skip_node;
+                       }
+
+                       shuntv_cond_offset->shuntv_start = shuntv_start;
+                       shuntv_cond_offset->shuntv_end = shuntv_end;
+                       shuntv_cond_offset->offset = offset;
+skip_node:
+                       shuntv_cond_offset++;
+                       }
+               }
+       }
+
+       return shuntv_offset;
+}
+
 static struct ina3221_platform_data *ina3221_get_platform_data_dt(
        struct i2c_client *client)
 {
@@ -1016,6 +1131,9 @@ static struct ina3221_platform_data *ina3221_get_platform_data_dt(
                if (!ret)
                        pdata->cpdata[reg].shunt_resistor = pval;
 
+               pdata->cpdata[reg].shuntv_offset =
+                       ina3221_get_shuntv_offset(client, child);
+
                valid_channel++;
        }