From: Guenter Roeck <linux@roeck-us.net>
To: linux-hwmon@vger.kernel.org
Cc: Christian Kahr <christian.kahr@sie.at>,
devicetree@vger.kernel.org,
Krzysztof Kozlowski <krzk+dt@kernel.org>,
Chris Packham <chris.packham@alliedtelesis.co.nz>,
Guenter Roeck <linux@roeck-us.net>
Subject: [PATCH 05/17] hwmon: (ina238) Pre-calculate current, power, and energy LSB
Date: Fri, 5 Sep 2025 13:41:47 -0700 [thread overview]
Message-ID: <20250905204159.2618403-6-linux@roeck-us.net> (raw)
In-Reply-To: <20250905204159.2618403-1-linux@roeck-us.net>
Current, power, and energy LSB do not change during runtime, so we can
pre-calculate the respective values. The power LSB can be derived from
the current LSB using the equation in the datasheets. Similar, the
energy LSB can be derived from the power LSB.
Also add support for chips with built-in shunt resistor by providing
a chip specific configuration parameter for the current LSB. The
relationship of current -> power -> energy LSB values in those chips
is the same as in chips with external shunt resistor, so configuration
parameters for power and energy LSB are not needed.
Use ROUND_CLOSEST functions instead of divide operations to reduce
rounding errors.
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
---
drivers/hwmon/ina238.c | 47 ++++++++++++++++++++++++------------------
1 file changed, 27 insertions(+), 20 deletions(-)
diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c
index e386a0f83fbb..316a7dc720f5 100644
--- a/drivers/hwmon/ina238.c
+++ b/drivers/hwmon/ina238.c
@@ -118,9 +118,10 @@ struct ina238_config {
bool has_power_highest; /* chip detection power peak */
bool has_energy; /* chip detection energy */
u8 temp_resolution; /* temperature register resolution in bit */
- u32 power_calculate_factor; /* fixed parameters for power calculate */
+ u32 power_calculate_factor; /* fixed parameter for power calculation, from datasheet */
u16 config_default; /* Power-on default state */
int bus_voltage_lsb; /* use for temperature calculate, uV/lsb */
+ int current_lsb; /* current LSB, in uA */
};
struct ina238_data {
@@ -130,6 +131,9 @@ struct ina238_data {
struct regmap *regmap;
u32 rshunt;
int gain;
+ int current_lsb; /* current LSB, in uA */
+ int power_lsb; /* power LSB, in uW */
+ int energy_lsb; /* energy LSB, in uJ */
};
static const struct ina238_config ina238_config[] = {
@@ -422,9 +426,8 @@ static int ina238_read_current(struct device *dev, u32 attr, long *val)
regval = (s16)regval;
}
- /* Signed register, fixed 1mA current lsb. result in mA */
- *val = div_s64((s64)regval * INA238_FIXED_SHUNT * data->gain,
- data->rshunt * 4);
+ /* Signed register. Result in mA */
+ *val = DIV_S64_ROUND_CLOSEST((s64)regval * data->current_lsb, 1000);
/* Account for 4 bit offset */
if (data->config->has_20bit_voltage_current)
@@ -450,9 +453,7 @@ static int ina238_read_power(struct device *dev, u32 attr, long *val)
if (err)
return err;
- /* Fixed 1mA lsb, scaled by 1000000 to have result in uW */
- power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT * data->gain *
- data->config->power_calculate_factor, 4 * 100 * data->rshunt);
+ power = (long long)regval * data->power_lsb;
/* Clamp value to maximum value of long */
*val = clamp_val(power, 0, LONG_MAX);
break;
@@ -461,9 +462,7 @@ static int ina238_read_power(struct device *dev, u32 attr, long *val)
if (err)
return err;
- /* Fixed 1mA lsb, scaled by 1000000 to have result in uW */
- power = div_u64(regval * 1000ULL * INA238_FIXED_SHUNT * data->gain *
- data->config->power_calculate_factor, 4 * 100 * data->rshunt);
+ power = (long long)regval * data->power_lsb;
/* Clamp value to maximum value of long */
*val = clamp_val(power, 0, LONG_MAX);
break;
@@ -476,8 +475,7 @@ static int ina238_read_power(struct device *dev, u32 attr, long *val)
* Truncated 24-bit compare register, lower 8-bits are
* truncated. Same conversion to/from uW as POWER register.
*/
- power = div_u64((regval << 8) * 1000ULL * INA238_FIXED_SHUNT * data->gain *
- data->config->power_calculate_factor, 4 * 100 * data->rshunt);
+ power = ((long long)regval << 8) * data->power_lsb;
/* Clamp value to maximum value of long */
*val = clamp_val(power, 0, LONG_MAX);
break;
@@ -498,7 +496,6 @@ static int ina238_read_power(struct device *dev, u32 attr, long *val)
static int ina238_write_power_max(struct device *dev, long val)
{
struct ina238_data *data = dev_get_drvdata(dev);
- long regval;
/*
* Unsigned postive values. Compared against the 24-bit power register,
@@ -506,12 +503,11 @@ static int ina238_write_power_max(struct device *dev, long val)
* register.
* The first clamp_val() is to establish a baseline to avoid overflows.
*/
- regval = clamp_val(val, 0, LONG_MAX / 2);
- regval = div_u64(regval * 4 * 100 * data->rshunt, data->config->power_calculate_factor *
- 1000ULL * INA238_FIXED_SHUNT * data->gain);
- regval = clamp_val(regval >> 8, 0, U16_MAX);
+ val = clamp_val(val, 0, LONG_MAX / 2);
+ val = DIV_ROUND_CLOSEST(val, data->power_lsb);
+ val = clamp_val(val >> 8, 0, U16_MAX);
- return regmap_write(data->regmap, INA238_POWER_LIMIT, regval);
+ return regmap_write(data->regmap, INA238_POWER_LIMIT, val);
}
static int ina238_temp_from_reg(s16 regval, u8 resolution)
@@ -584,8 +580,7 @@ static ssize_t energy1_input_show(struct device *dev,
return ret;
/* result in uJ */
- energy = div_u64(regval * INA238_FIXED_SHUNT * data->gain * 16 * 10 *
- data->config->power_calculate_factor, 4 * data->rshunt);
+ energy = regval * data->energy_lsb;
return sysfs_emit(buf, "%llu\n", energy);
}
@@ -817,6 +812,18 @@ static int ina238_probe(struct i2c_client *client)
return -ENODEV;
}
+ if (data->config->current_lsb)
+ data->current_lsb = data->config->current_lsb;
+ else
+ data->current_lsb = DIV_U64_ROUND_CLOSEST(250ULL * INA238_FIXED_SHUNT * data->gain,
+ data->rshunt);
+
+ data->power_lsb = DIV_ROUND_CLOSEST(data->current_lsb *
+ data->config->power_calculate_factor,
+ 100);
+
+ data->energy_lsb = data->power_lsb * 16;
+
hwmon_dev = devm_hwmon_device_register_with_info(dev, client->name, data,
&ina238_chip_info,
data->config->has_energy ?
--
2.45.2
next prev parent reply other threads:[~2025-09-05 20:42 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-05 20:41 [PATCH 00/17] hwmon: (ina238) Various improvements and added chip support Guenter Roeck
2025-09-05 20:41 ` [PATCH 01/17] hwmon: (ina238) Drop platform data support Guenter Roeck
2025-09-05 20:41 ` [PATCH 02/17] hwmon: (ina238) Update documentation and Kconfig entry Guenter Roeck
2025-09-05 20:41 ` [PATCH 03/17] hwmon: (ina238) Drop pointless power attribute check on attribute writes Guenter Roeck
2025-09-05 20:41 ` [PATCH 04/17] hwmon: (ina238) Rework and simplify temperature calculations Guenter Roeck
2025-09-05 20:41 ` Guenter Roeck [this message]
2025-09-05 20:41 ` [PATCH 06/17] hwmon: (ina238) Simplify voltage register accesses Guenter Roeck
2025-09-05 20:41 ` [PATCH 07/17] hwmon: (ina238) Improve current dynamic range Guenter Roeck
2025-09-05 20:41 ` [PATCH 08/17] hwmon: (ina238) Stop using the shunt voltage register Guenter Roeck
2025-09-05 20:41 ` [PATCH 09/17] hwmon: (ina238) Add support for current limits Guenter Roeck
2025-09-05 20:41 ` [PATCH 10/17] hwmon: (ina238) Order chip information alphabetically Guenter Roeck
2025-09-05 20:41 ` [PATCH 11/17] hwmon: Introduce 64-bit energy attribute support Guenter Roeck
2025-09-05 20:41 ` [PATCH 12/17] hwmon: (ina238) Use the energy64 attribute type to report the energy Guenter Roeck
2025-09-05 20:41 ` [PATCH 13/17] hwmon: (ina238) Support active-high alert polarity Guenter Roeck
2025-09-05 20:41 ` [PATCH 14/17] hwmon: (ina238) Only configure calibration and shunt registers if needed Guenter Roeck
2025-09-05 20:41 ` [PATCH 15/17] hwmon: (ina238) Add support for INA780 Guenter Roeck
2025-09-05 20:41 ` [PATCH 16/17] dt-bindings: hwmon: ti,ina2xx: Add INA700 Guenter Roeck
2025-09-05 20:41 ` [PATCH 17/17] hwmon: (ina238) Add support for INA700 Guenter Roeck
2025-09-07 23:00 ` [PATCH 00/17] hwmon: (ina238) Various improvements and added chip support Chris Packham
2025-09-07 23:32 ` Guenter Roeck
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=20250905204159.2618403-6-linux@roeck-us.net \
--to=linux@roeck-us.net \
--cc=chris.packham@alliedtelesis.co.nz \
--cc=christian.kahr@sie.at \
--cc=devicetree@vger.kernel.org \
--cc=krzk+dt@kernel.org \
--cc=linux-hwmon@vger.kernel.org \
/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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox