From: "Brüns, Stefan" <Stefan.Bruens@rwth-aachen.de>
To: Maciej Purski <m.purski@samsung.com>
Cc: Jonathan Cameron <jic23@kernel.org>,
"linux-iio@vger.kernel.org" <linux-iio@vger.kernel.org>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"linux-hwmon@vger.kernel.org" <linux-hwmon@vger.kernel.org>,
Peter Meerwald-Stadler <pmeerw@pmeerw.net>,
Lars-Peter Clausen <lars@metafoo.de>,
Hartmut Knaack <knaack.h@gmx.de>,
"Jean Delvare" <jdelvare@suse.com>,
Guenter Roeck <linux@roeck-us.net>,
"Marek Szyprowski" <m.szyprowski@samsung.com>,
Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Subject: Re: [PATCH v2] iio: adc: ina2xx: Make calibration register value fixed
Date: Thu, 7 Dec 2017 16:10:40 +0000 [thread overview]
Message-ID: <3656355.n9BamPbOAV@sbruens-linux> (raw)
In-Reply-To: <1512648167-11482-1-git-send-email-m.purski@samsung.com>
On Donnerstag, 7. Dezember 2017 13:02:47 CET Maciej Purski wrote:
> Calibration register is used for calculating current register in
> hardware according to datasheet:
> current = shunt_volt * calib_register / 2048 (ina 226)
> current = shunt_volt * calib_register / 4096 (ina 219)
>
> Fix calib_register value to 2048 for ina226 and 4096 for ina 219 in
> order to avoid truncation error and provide best precision allowed
> by shunt_voltage measurement. Make current scale value follow changes
> of shunt_resistor from sysfs as calib_register value is now fixed.
>
> Power_lsb value should also follow shunt_resistor changes as stated in
> datasheet:
> power_lsb = 25 * current_lsb (ina 226)
> power_lsb = 20 * current_lsb (ina 219)
>
> This is a part of the patchset: https://lkml.org/lkml/2017/11/22/394
>
> Signed-off-by: Maciej Purski <m.purski@samsung.com>
>
> ---
> Changes in v2:
> - rebase on top of the latest next
> - remove a redundant variable - power_lsb_uW
> - fix comments
> ---
> drivers/iio/adc/ina2xx-adc.c | 58
> +++++++++++++++++++++++--------------------- 1 file changed, 31
> insertions(+), 27 deletions(-)
>
> diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
> index ddf8781..3e4972f 100644
> --- a/drivers/iio/adc/ina2xx-adc.c
> +++ b/drivers/iio/adc/ina2xx-adc.c
> @@ -124,11 +124,11 @@ enum ina2xx_ids { ina219, ina226 };
>
> struct ina2xx_config {
> u16 config_default;
> - int calibration_factor;
> + int calibration_value;
> int shunt_voltage_lsb; /* nV */
> int bus_voltage_shift; /* position of lsb */
> int bus_voltage_lsb; /* uV */
> - int power_lsb; /* uW */
> + int power_lsb_factor;
> enum ina2xx_ids chip_id;
> };
>
> @@ -138,6 +138,7 @@ struct ina2xx_chip_info {
> const struct ina2xx_config *config;
> struct mutex state_lock;
> unsigned int shunt_resistor_uohm;
> + unsigned int current_lsb_uA;
I don't think you need current_lsb_uA, see below ...
> int avg;
> int int_time_vbus; /* Bus voltage integration time uS */
> int int_time_vshunt; /* Shunt voltage integration time uS */
> @@ -149,20 +150,20 @@ struct ina2xx_chip_info {
> static const struct ina2xx_config ina2xx_config[] = {
> [ina219] = {
> .config_default = INA219_CONFIG_DEFAULT,
> - .calibration_factor = 40960000,
> + .calibration_value = 4096,
> .shunt_voltage_lsb = 10000,
> .bus_voltage_shift = INA219_BUS_VOLTAGE_SHIFT,
> .bus_voltage_lsb = 4000,
> - .power_lsb = 20000,
> + .power_lsb_factor = 20,
> .chip_id = ina219,
> },
> [ina226] = {
> .config_default = INA226_CONFIG_DEFAULT,
> - .calibration_factor = 5120000,
> + .calibration_value = 2048,
> .shunt_voltage_lsb = 2500,
> .bus_voltage_shift = 0,
> .bus_voltage_lsb = 1250,
> - .power_lsb = 25000,
> + .power_lsb_factor = 25,
> .chip_id = ina226,
> },
> };
> @@ -229,14 +230,16 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev,
>
> case INA2XX_POWER:
> /* processed (mW) = raw*lsb (uW) / 1000 */
> - *val = chip->config->power_lsb;
> + *val = chip->config->power_lsb_factor *
> + chip->current_lsb_uA;
> *val2 = 1000;
> return IIO_VAL_FRACTIONAL;
>
> case INA2XX_CURRENT:
> - /* processed (mA) = raw (mA) */
> - *val = 1;
> - return IIO_VAL_INT;
> + /* processed (mA) = raw*lsb (uA) / 1000 */
> + *val = chip->current_lsb_uA;
> + *val2 = 1000;
.. this is the only place where current_lsb_uA is used ...
> + return IIO_VAL_FRACTIONAL;
> }
>
> case IIO_CHAN_INFO_HARDWAREGAIN:
> @@ -541,28 +544,34 @@ static ssize_t ina2xx_allow_async_readout_store(struct
> device *dev, }
>
> /*
> - * Set current LSB to 1mA, shunt is in uOhms
> - * (equation 13 in datasheet). We hardcode a Current_LSB
> - * of 1.0 x10-3. The only remaining parameter is RShunt.
> - * There is no need to expose the CALIBRATION register
> - * to the user for now. But we need to reset this register
> - * if the user updates RShunt after driver init, e.g upon
> - * reading an EEPROM/Probe-type value.
> + * Calibration register is set to the best value, which eliminates
> + * truncation errors on calculating current register in hardware.
> + * According to datasheet (INA 226: eq. 3, INA219: eq. 4) the best values
> + * are 2048 for ina226 and 4096 for ina219. They are hardcoded as
> + * calibration_value.
> */
> static int ina2xx_set_calibration(struct ina2xx_chip_info *chip)
> {
> - u16 regval = DIV_ROUND_CLOSEST(chip->config->calibration_factor,
> - chip->shunt_resistor_uohm);
> -
> - return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval);
> + return regmap_write(chip->regmap, INA2XX_CALIBRATION,
> + chip->config->calibration_value);
> }
>
> +/*
> + * In order to keep calibration register value fixed, the product
> + * of current_lsb and shunt_resistor should also be fixed and equal
> + * to shunt_voltage_lsb = 1 / shunt_div multiplied by 10^9 in order
> + * to keep the scale.
> + */
> static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned int
> val) {
> - if (val <= 0 || val > chip->config->calibration_factor)
> + unsigned int dividend = DIV_ROUND_CLOSEST(1000000000,
> + chip->config->shunt_div);
> +
> + if (val <= 0 || val > dividend)
> return -EINVAL;
>
> chip->shunt_resistor_uohm = val;
> + chip->current_lsb_uA = DIV_ROUND_CLOSEST(dividend, val);
1st: shunt_div no longer exists, but shunt_voltage_lsb[nV], so this code does
not even compile ...
This function does two things - range check and calculation of the
curren_lsb_uA auxiliary variable.
Lets start with the second part:
read_raw returns the IIO_VAL_FRACTIONAL
val / val2 = current_lsb_uA / 1000 (1)
from set_shunt_resistor(...):
dividend = 1e9 / shunt_div (2)
current_lsb_uA = dividend / shunt_resistor_uohm (3)
shunt_div no longer exists, but can be substituted:
shunt_div = 1e6 / shunt_voltage_lsb (4)
substituting (2), (3) and (4) in (1), we end up with
val / val2 = 1e9 / (shunt_div * shunt_resistor_uohm * 1000)
=> val / val2 = 1e6 / ((1e6 / shunt_voltage_lsb) * shunt_resistor_uohm)
=> val / val2 = shunt_voltage_lsb / shunt_resistor_uohm
=> [mA] = [nV] / [uOhm]
The auxiliary variable is gone, as are the two DIV_ROUND_CLOSEST calls.
Part 2, range check:
lower bound is obviously 1 uOhm. The upper bound is only limited by
typeof(val2) and typeof(shunt_resistor_uohm), both are ints.
so the range check can be simplified to
if (val == 0 || val > INT_MAX)
return -EINVAL
Kind regards,
Stefan
WARNING: multiple messages have this Message-ID (diff)
From: "Brüns, Stefan" <Stefan.Bruens@rwth-aachen.de>
To: Maciej Purski <m.purski@samsung.com>
Cc: Jonathan Cameron <jic23@kernel.org>,
"linux-iio@vger.kernel.org" <linux-iio@vger.kernel.org>,
"linux-kernel@vger.kernel.org" <linux-kernel@vger.kernel.org>,
"linux-hwmon@vger.kernel.org" <linux-hwmon@vger.kernel.org>,
Peter Meerwald-Stadler <pmeerw@pmeerw.net>,
Lars-Peter Clausen <lars@metafoo.de>,
Hartmut Knaack <knaack.h@gmx.de>,
"Jean Delvare" <jdelvare@suse.com>,
Guenter Roeck <linux@roeck-us.net>,
"Marek Szyprowski" <m.szyprowski@samsung.com>,
Bartlomiej Zolnierkiewicz <b.zolnierkie@samsung.com>
Subject: Re: [PATCH v2] iio: adc: ina2xx: Make calibration register value fixed
Date: Thu, 7 Dec 2017 16:10:40 +0000 [thread overview]
Message-ID: <3656355.n9BamPbOAV@sbruens-linux> (raw)
In-Reply-To: <1512648167-11482-1-git-send-email-m.purski@samsung.com>
On Donnerstag, 7. Dezember 2017 13:02:47 CET Maciej Purski wrote:=0A=
> Calibration register is used for calculating current register in=0A=
> hardware according to datasheet:=0A=
> current =3D shunt_volt * calib_register / 2048 (ina 226)=0A=
> current =3D shunt_volt * calib_register / 4096 (ina 219)=0A=
> =0A=
> Fix calib_register value to 2048 for ina226 and 4096 for ina 219 in=0A=
> order to avoid truncation error and provide best precision allowed=0A=
> by shunt_voltage measurement. Make current scale value follow changes=0A=
> of shunt_resistor from sysfs as calib_register value is now fixed.=0A=
> =0A=
> Power_lsb value should also follow shunt_resistor changes as stated in=0A=
> datasheet:=0A=
> power_lsb =3D 25 * current_lsb (ina 226)=0A=
> power_lsb =3D 20 * current_lsb (ina 219)=0A=
> =0A=
> This is a part of the patchset: https://lkml.org/lkml/2017/11/22/394=0A=
> =0A=
> Signed-off-by: Maciej Purski <m.purski@samsung.com>=0A=
> =0A=
> ---=0A=
> Changes in v2:=0A=
> - rebase on top of the latest next=0A=
> - remove a redundant variable - power_lsb_uW=0A=
> - fix comments=0A=
> ---=0A=
> drivers/iio/adc/ina2xx-adc.c | 58=0A=
> +++++++++++++++++++++++--------------------- 1 file changed, 31=0A=
> insertions(+), 27 deletions(-)=0A=
> =0A=
> diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c=
=0A=
> index ddf8781..3e4972f 100644=0A=
> --- a/drivers/iio/adc/ina2xx-adc.c=0A=
> +++ b/drivers/iio/adc/ina2xx-adc.c=0A=
> @@ -124,11 +124,11 @@ enum ina2xx_ids { ina219, ina226 };=0A=
> =0A=
> struct ina2xx_config {=0A=
> u16 config_default;=0A=
> - int calibration_factor;=0A=
> + int calibration_value;=0A=
> int shunt_voltage_lsb; /* nV */=0A=
> int bus_voltage_shift; /* position of lsb */=0A=
> int bus_voltage_lsb; /* uV */=0A=
> - int power_lsb; /* uW */=0A=
> + int power_lsb_factor;=0A=
> enum ina2xx_ids chip_id;=0A=
> };=0A=
> =0A=
> @@ -138,6 +138,7 @@ struct ina2xx_chip_info {=0A=
> const struct ina2xx_config *config;=0A=
> struct mutex state_lock;=0A=
> unsigned int shunt_resistor_uohm;=0A=
> + unsigned int current_lsb_uA;=0A=
=0A=
I don't think you need current_lsb_uA, see below ...=0A=
=0A=
> int avg;=0A=
> int int_time_vbus; /* Bus voltage integration time uS */=0A=
> int int_time_vshunt; /* Shunt voltage integration time uS */=0A=
> @@ -149,20 +150,20 @@ struct ina2xx_chip_info {=0A=
> static const struct ina2xx_config ina2xx_config[] =3D {=0A=
> [ina219] =3D {=0A=
> .config_default =3D INA219_CONFIG_DEFAULT,=0A=
> - .calibration_factor =3D 40960000,=0A=
> + .calibration_value =3D 4096,=0A=
> .shunt_voltage_lsb =3D 10000,=0A=
> .bus_voltage_shift =3D INA219_BUS_VOLTAGE_SHIFT,=0A=
> .bus_voltage_lsb =3D 4000,=0A=
> - .power_lsb =3D 20000,=0A=
> + .power_lsb_factor =3D 20,=0A=
> .chip_id =3D ina219,=0A=
> },=0A=
> [ina226] =3D {=0A=
> .config_default =3D INA226_CONFIG_DEFAULT,=0A=
> - .calibration_factor =3D 5120000,=0A=
> + .calibration_value =3D 2048,=0A=
> .shunt_voltage_lsb =3D 2500,=0A=
> .bus_voltage_shift =3D 0,=0A=
> .bus_voltage_lsb =3D 1250,=0A=
> - .power_lsb =3D 25000,=0A=
> + .power_lsb_factor =3D 25,=0A=
> .chip_id =3D ina226,=0A=
> },=0A=
> };=0A=
> @@ -229,14 +230,16 @@ static int ina2xx_read_raw(struct iio_dev *indio_de=
v,=0A=
> =0A=
> case INA2XX_POWER:=0A=
> /* processed (mW) =3D raw*lsb (uW) / 1000 */=0A=
> - *val =3D chip->config->power_lsb;=0A=
> + *val =3D chip->config->power_lsb_factor *=0A=
> + chip->current_lsb_uA;=0A=
> *val2 =3D 1000;=0A=
> return IIO_VAL_FRACTIONAL;=0A=
> =0A=
> case INA2XX_CURRENT:=0A=
> - /* processed (mA) =3D raw (mA) */=0A=
> - *val =3D 1;=0A=
> - return IIO_VAL_INT;=0A=
> + /* processed (mA) =3D raw*lsb (uA) / 1000 */=0A=
> + *val =3D chip->current_lsb_uA;=0A=
> + *val2 =3D 1000;=0A=
=0A=
... this is the only place where current_lsb_uA is used ...=0A=
=0A=
> + return IIO_VAL_FRACTIONAL;=0A=
> }=0A=
> =0A=
> case IIO_CHAN_INFO_HARDWAREGAIN:=0A=
> @@ -541,28 +544,34 @@ static ssize_t ina2xx_allow_async_readout_store(str=
uct=0A=
> device *dev, }=0A=
> =0A=
> /*=0A=
> - * Set current LSB to 1mA, shunt is in uOhms=0A=
> - * (equation 13 in datasheet). We hardcode a Current_LSB=0A=
> - * of 1.0 x10-3. The only remaining parameter is RShunt.=0A=
> - * There is no need to expose the CALIBRATION register=0A=
> - * to the user for now. But we need to reset this register=0A=
> - * if the user updates RShunt after driver init, e.g upon=0A=
> - * reading an EEPROM/Probe-type value.=0A=
> + * Calibration register is set to the best value, which eliminates=0A=
> + * truncation errors on calculating current register in hardware.=0A=
> + * According to datasheet (INA 226: eq. 3, INA219: eq. 4) the best value=
s=0A=
> + * are 2048 for ina226 and 4096 for ina219. They are hardcoded as=0A=
> + * calibration_value.=0A=
> */=0A=
> static int ina2xx_set_calibration(struct ina2xx_chip_info *chip)=0A=
> {=0A=
> - u16 regval =3D DIV_ROUND_CLOSEST(chip->config->calibration_factor,=0A=
> - chip->shunt_resistor_uohm);=0A=
> -=0A=
> - return regmap_write(chip->regmap, INA2XX_CALIBRATION, regval);=0A=
> + return regmap_write(chip->regmap, INA2XX_CALIBRATION,=0A=
> + chip->config->calibration_value);=0A=
> }=0A=
> =0A=
> +/*=0A=
> + * In order to keep calibration register value fixed, the product=0A=
> + * of current_lsb and shunt_resistor should also be fixed and equal=0A=
> + * to shunt_voltage_lsb =3D 1 / shunt_div multiplied by 10^9 in order=0A=
> + * to keep the scale.=0A=
> + */=0A=
> static int set_shunt_resistor(struct ina2xx_chip_info *chip, unsigned in=
t=0A=
> val) {=0A=
> - if (val <=3D 0 || val > chip->config->calibration_factor)=0A=
> + unsigned int dividend =3D DIV_ROUND_CLOSEST(1000000000,=0A=
> + chip->config->shunt_div);=0A=
> +=0A=
> + if (val <=3D 0 || val > dividend)=0A=
> return -EINVAL;=0A=
> =0A=
> chip->shunt_resistor_uohm =3D val;=0A=
> + chip->current_lsb_uA =3D DIV_ROUND_CLOSEST(dividend, val);=0A=
=0A=
1st: shunt_div no longer exists, but shunt_voltage_lsb[nV], so this code do=
es =0A=
not even compile ...=0A=
=0A=
This function does two things - range check and calculation of the =0A=
curren_lsb_uA auxiliary variable.=0A=
=0A=
Lets start with the second part:=0A=
=0A=
read_raw returns the IIO_VAL_FRACTIONAL=0A=
val / val2 =3D current_lsb_uA / 1000 (1)=0A=
=0A=
=0A=
from set_shunt_resistor(...):=0A=
=0A=
dividend =3D 1e9 / shunt_div (2)=0A=
current_lsb_uA =3D dividend / shunt_resistor_uohm (3)=0A=
=0A=
shunt_div no longer exists, but can be substituted:=0A=
shunt_div =3D 1e6 / shunt_voltage_lsb (4)=0A=
=0A=
substituting (2), (3) and (4) in (1), we end up with=0A=
=0A=
val / val2 =3D 1e9 / (shunt_div * shunt_resistor_uohm * 1000)=0A=
=0A=
=3D> val / val2 =3D 1e6 / ((1e6 / shunt_voltage_lsb) * shunt_resistor_uohm=
)=0A=
=3D> val / val2 =3D shunt_voltage_lsb / shunt_resistor_uohm=0A=
=3D> [mA] =3D [nV] / [uOhm]=0A=
=0A=
The auxiliary variable is gone, as are the two DIV_ROUND_CLOSEST calls.=0A=
=0A=
=0A=
Part 2, range check:=0A=
=0A=
lower bound is obviously 1 uOhm. The upper bound is only limited by=0A=
typeof(val2) and typeof(shunt_resistor_uohm), both are ints.=0A=
=0A=
so the range check can be simplified to=0A=
=0A=
if (val =3D=3D 0 || val > INT_MAX)=0A=
return -EINVAL=0A=
=0A=
Kind regards,=0A=
=0A=
Stefan=0A=
=0A=
next prev parent reply other threads:[~2017-12-07 16:10 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
[not found] <CGME20171207120340eucas1p24b145ba9b41768c1708ad574307c00c0@eucas1p2.samsung.com>
2017-12-07 12:02 ` [PATCH v2] iio: adc: ina2xx: Make calibration register value fixed Maciej Purski
2017-12-07 16:10 ` Brüns, Stefan [this message]
2017-12-07 16:10 ` Brüns, Stefan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=3656355.n9BamPbOAV@sbruens-linux \
--to=stefan.bruens@rwth-aachen.de \
--cc=b.zolnierkie@samsung.com \
--cc=jdelvare@suse.com \
--cc=jic23@kernel.org \
--cc=knaack.h@gmx.de \
--cc=lars@metafoo.de \
--cc=linux-hwmon@vger.kernel.org \
--cc=linux-iio@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux@roeck-us.net \
--cc=m.purski@samsung.com \
--cc=m.szyprowski@samsung.com \
--cc=pmeerw@pmeerw.net \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.