lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20250718-ina228-v2-3-227feb62f709@pengutronix.de>
Date: Fri, 18 Jul 2025 16:12:50 +0200
From: Jonas Rebmann <jre@...gutronix.de>
To: Jean Delvare <jdelvare@...e.com>, Guenter Roeck <linux@...ck-us.net>, 
 Rob Herring <robh@...nel.org>, Krzysztof Kozlowski <krzk+dt@...nel.org>, 
 Conor Dooley <conor+dt@...nel.org>
Cc: linux-hwmon@...r.kernel.org, linux-kernel@...r.kernel.org, 
 Krzysztof Kozlowski <krzk@...nel.org>, devicetree@...r.kernel.org, 
 kernel@...gutronix.de, Jonas Rebmann <jre@...gutronix.de>
Subject: [PATCH v2 3/3] hwmon: ina238: Add support for INA228

Add support for the Texas Instruments INA228 Ultra-Precise
Power/Energy/Charge Monitor.

The INA228 is very similar to the INA238 but offers four bits of extra
precision in the temperature, voltage and current measurement fields.
It also supports energy and charge monitoring, the latter of which is
not supported through this patch.

While it seems in the datasheet that some constants such as LSB values
differ between the 228 and the 238, they differ only for those registers
where four bits of precision have been added and they differ by a factor
of 16 (VBUS, VSHUNT, DIETEMP, CURRENT).

Therefore, the INA238 constants are still applicable with regard
to the bit of the same significance.

Signed-off-by: Jonas Rebmann <jre@...gutronix.de>
---
 drivers/hwmon/ina238.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 101 insertions(+), 5 deletions(-)

diff --git a/drivers/hwmon/ina238.c b/drivers/hwmon/ina238.c
index 5e43bbe8817d..5a394eeff676 100644
--- a/drivers/hwmon/ina238.c
+++ b/drivers/hwmon/ina238.c
@@ -6,6 +6,7 @@
  * Copyright (C) 2021 Nathan Rossi <nathan.rossi@...i.com>
  */
 
+#include <linux/bitops.h>
 #include <linux/err.h>
 #include <linux/hwmon.h>
 #include <linux/i2c.h>
@@ -107,6 +108,7 @@
 #define INA238_DIE_TEMP_LSB		1250000 /* 125.0000 mC/lsb */
 #define SQ52206_BUS_VOLTAGE_LSB		3750 /* 3.75 mV/lsb */
 #define SQ52206_DIE_TEMP_LSB		78125 /* 7.8125 mC/lsb */
+#define INA228_DIE_TEMP_LSB		78125 /* 7.8125 mC/lsb */
 
 static const struct regmap_config ina238_regmap_config = {
 	.max_register = INA238_REGISTERS,
@@ -114,9 +116,10 @@ static const struct regmap_config ina238_regmap_config = {
 	.val_bits = 16,
 };
 
-enum ina238_ids { ina238, ina237, sq52206 };
+enum ina238_ids { ina238, ina237, sq52206, ina228 };
 
 struct ina238_config {
+	bool has_20bit_voltage_current; /* vshunt, vbus and current are 20-bit fields */
 	bool has_power_highest;		/* chip detection power peak */
 	bool has_energy;		/* chip detection energy */
 	u8 temp_shift;			/* fixed parameters for temp calculate */
@@ -137,6 +140,7 @@ struct ina238_data {
 
 static const struct ina238_config ina238_config[] = {
 	[ina238] = {
+		.has_20bit_voltage_current = false,
 		.has_energy = false,
 		.has_power_highest = false,
 		.temp_shift = 4,
@@ -146,6 +150,7 @@ static const struct ina238_config ina238_config[] = {
 		.temp_lsb = INA238_DIE_TEMP_LSB,
 	},
 	[ina237] = {
+		.has_20bit_voltage_current = false,
 		.has_energy = false,
 		.has_power_highest = false,
 		.temp_shift = 4,
@@ -155,6 +160,7 @@ static const struct ina238_config ina238_config[] = {
 		.temp_lsb = INA238_DIE_TEMP_LSB,
 	},
 	[sq52206] = {
+		.has_20bit_voltage_current = false,
 		.has_energy = true,
 		.has_power_highest = true,
 		.temp_shift = 0,
@@ -163,6 +169,16 @@ static const struct ina238_config ina238_config[] = {
 		.bus_voltage_lsb = SQ52206_BUS_VOLTAGE_LSB,
 		.temp_lsb = SQ52206_DIE_TEMP_LSB,
 	},
+	[ina228] = {
+		.has_20bit_voltage_current = true,
+		.has_energy = true,
+		.has_power_highest = false,
+		.temp_shift = 0,
+		.power_calculate_factor = 20,
+		.config_default = INA238_CONFIG_DEFAULT,
+		.bus_voltage_lsb = INA238_BUS_VOLTAGE_LSB,
+		.temp_lsb = INA228_DIE_TEMP_LSB,
+	},
 };
 
 static int ina238_read_reg24(const struct i2c_client *client, u8 reg, u32 *val)
@@ -199,6 +215,65 @@ static int ina238_read_reg40(const struct i2c_client *client, u8 reg, u64 *val)
 	return 0;
 }
 
+static int ina238_read_field_s20(const struct i2c_client *client, u8 reg, s32 *val)
+{
+	u32 regval;
+	int err;
+
+	err = ina238_read_reg24(client, reg, &regval);
+	if (err)
+		return err;
+
+	/* bits 3-0 Reserved, always zero */
+	regval >>= 4;
+
+	*val = sign_extend32(regval, 19);
+
+	return 0;
+}
+
+static int ina228_read_shunt_voltage(struct device *dev, u32 attr, int channel,
+				     long *val)
+{
+	struct ina238_data *data = dev_get_drvdata(dev);
+	int regval;
+	int err;
+
+	err = ina238_read_field_s20(data->client, INA238_SHUNT_VOLTAGE, &regval);
+	if (err)
+		return err;
+
+	/*
+	 * gain of 1 -> LSB / 4
+	 * This field has 16 bit on ina238. ina228 adds another 4 bits of
+	 * precision. ina238 conversion factors can still be applied when
+	 * dividing by 16.
+	 */
+	*val = (regval * INA238_SHUNT_VOLTAGE_LSB) * data->gain / (1000 * 4) / 16;
+	return 0;
+}
+
+static int ina228_read_bus_voltage(struct device *dev, u32 attr, int channel,
+				   long *val)
+{
+	struct ina238_data *data = dev_get_drvdata(dev);
+	int regval;
+	int err;
+
+	err = ina238_read_field_s20(data->client, INA238_BUS_VOLTAGE, &regval);
+	if (err)
+		return err;
+
+	/*
+	 * gain of 1 -> LSB / 4
+	 * This field has 16 bit on ina238. ina228 adds another 4 bits of
+	 * precision. ina238 conversion factors can still be applied when
+	 * dividing by 16.
+	 */
+	*val = (regval * data->config->bus_voltage_lsb) / 1000 / 16;
+	return 0;
+}
+
 static int ina238_read_in(struct device *dev, u32 attr, int channel,
 			  long *val)
 {
@@ -211,6 +286,8 @@ static int ina238_read_in(struct device *dev, u32 attr, int channel,
 	case 0:
 		switch (attr) {
 		case hwmon_in_input:
+			if (data->config->has_20bit_voltage_current)
+				return ina228_read_shunt_voltage(dev, attr, channel, val);
 			reg = INA238_SHUNT_VOLTAGE;
 			break;
 		case hwmon_in_max:
@@ -234,6 +311,8 @@ static int ina238_read_in(struct device *dev, u32 attr, int channel,
 	case 1:
 		switch (attr) {
 		case hwmon_in_input:
+			if (data->config->has_20bit_voltage_current)
+				return ina228_read_bus_voltage(dev, attr, channel, val);
 			reg = INA238_BUS_VOLTAGE;
 			break;
 		case hwmon_in_max:
@@ -341,13 +420,25 @@ static int ina238_read_current(struct device *dev, u32 attr, long *val)
 
 	switch (attr) {
 	case hwmon_curr_input:
-		err = regmap_read(data->regmap, INA238_CURRENT, &regval);
-		if (err < 0)
-			return err;
+		if (data->config->has_20bit_voltage_current) {
+			err = ina238_read_field_s20(data->client, INA238_CURRENT, &regval);
+			if (err)
+				return err;
+		} else {
+			err = regmap_read(data->regmap, INA238_CURRENT, &regval);
+			if (err < 0)
+				return err;
+			/* sign-extend */
+			regval = (s16)regval;
+		}
 
 		/* Signed register, fixed 1mA current lsb. result in mA */
-		*val = div_s64((s16)regval * INA238_FIXED_SHUNT * data->gain,
+		*val = div_s64((s64)regval * INA238_FIXED_SHUNT * data->gain,
 			       data->rshunt * 4);
+
+		/* Account for 4 bit offset */
+		if (data->config->has_20bit_voltage_current)
+			*val /= 16;
 		break;
 	default:
 		return -EOPNOTSUPP;
@@ -750,6 +841,7 @@ static int ina238_probe(struct i2c_client *client)
 }
 
 static const struct i2c_device_id ina238_id[] = {
+	{ "ina228", ina228 },
 	{ "ina237", ina237 },
 	{ "ina238", ina238 },
 	{ "sq52206", sq52206 },
@@ -758,6 +850,10 @@ static const struct i2c_device_id ina238_id[] = {
 MODULE_DEVICE_TABLE(i2c, ina238_id);
 
 static const struct of_device_id __maybe_unused ina238_of_match[] = {
+	{
+		.compatible = "ti,ina228",
+		.data = (void *)ina228
+	},
 	{
 		.compatible = "ti,ina237",
 		.data = (void *)ina237

-- 
2.50.1.319.g90c0775e97


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ