From: Rajkumar Kasirajan Date: Mon, 23 Nov 2015 10:29:50 +0000 (+0800) Subject: iio: meter: ina3221: support to offset shunt volt X-Git-Tag: tegra-l4t-r23.2~49 X-Git-Url: https://rtime.felk.cvut.cz/gitweb/sojka/nv-tegra/linux-3.10.git/commitdiff_plain/e1613a0534cc372160090e9e60b52349ffee9ade iio: meter: ina3221: support to offset shunt volt 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 Reviewed-on: http://git-master/r/836475 Reviewed-by: Automatic_Commit_Validation_User GVS: Gerrit_Virtual_Submit Reviewed-by: Ninad Malwade Reviewed-by: Shreshtha Sahu Reviewed-by: Laxman Dewangan --- diff --git a/Documentation/devicetree/bindings/iio/meter/ina3221.txt b/Documentation/devicetree/bindings/iio/meter/ina3221.txt index 8d4910280c8..44593e21eaf 100644 --- a/Documentation/devicetree/bindings/iio/meter/ina3221.txt +++ b/Documentation/devicetree/bindings/iio/meter/ina3221.txt @@ -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 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 { diff --git a/drivers/staging/iio/meter/ina3221.c b/drivers/staging/iio/meter/ina3221.c index 4177319abda..c91f12a3656 100644 --- a/drivers/staging/iio/meter/ina3221.c +++ b/drivers/staging/iio/meter/ina3221.c @@ -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++; }