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-next>] [day] [month] [year] [list]
Message-ID: <ZOILXb-1ObJRB1ck@lguegan-thinkpad>
Date:   Sun, 20 Aug 2023 14:47:25 +0200
From:   Loic Guegan <loic.guegan@...lbox.org>
To:     Jean Delvare <jdelvare@...e.com>,
        Guenter Roeck <linux@...ck-us.net>,
        linux-kernel@...r.kernel.org, linux-hwmon@...r.kernel.org
Subject: [PATCH] hwmon: Add support for the INA260 chip to the INA219 and
 compatibles driver

This patch allows to retrieve current, bus voltage and power
measurements from the INA260 using its default configuration.

As the INA260 provides integrated shunt, the "ishunt" configuration
parameter is introduced. As such, proper attributes can be attached
with hwmon and shunt calibration related routnies can be ignored. In
addition, the actual INA2XX current register address differ from the
INA260 as it does not provide a shunt register. Thus, a specific
register address INA2XX_CURRENT_ISHUNT for chips with integrated
shunts is proposed.

Signed-off-by: Loic Guegan <loic.guegan@...lbox.org>
---
 drivers/hwmon/Kconfig  |  2 +-
 drivers/hwmon/ina2xx.c | 77 ++++++++++++++++++++++++++++++++++--------
 2 files changed, 64 insertions(+), 15 deletions(-)

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 307477b8a371..69b75a62c1cf 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -2022,7 +2022,7 @@ config SENSORS_INA2XX
 	select REGMAP_I2C
 	help
 	  If you say yes here you get support for INA219, INA220, INA226,
-	  INA230, and INA231 power monitor chips.
+	  INA230, INA231 and INA260 power monitor chips.
 
 	  The INA2xx driver is configured for the default configuration of
 	  the part as described in the datasheet.
diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
index cfd7efef5cdf..e0b9604ae0b7 100644
--- a/drivers/hwmon/ina2xx.c
+++ b/drivers/hwmon/ina2xx.c
@@ -45,6 +45,7 @@
 #define INA2XX_BUS_VOLTAGE		0x02 /* readonly */
 #define INA2XX_POWER			0x03 /* readonly */
 #define INA2XX_CURRENT			0x04 /* readonly */
+#define INA2XX_CURRENT_ISHUNT		0x01 /* readonly */
 #define INA2XX_CALIBRATION		0x05
 
 /* INA226 register definitions */
@@ -55,18 +56,22 @@
 /* register count */
 #define INA219_REGISTERS		6
 #define INA226_REGISTERS		8
+#define INA260_REGISTERS		8
 
 #define INA2XX_MAX_REGISTERS		8
 
 /* settings - depend on use case */
 #define INA219_CONFIG_DEFAULT		0x399F	/* PGA=8 */
 #define INA226_CONFIG_DEFAULT		0x4527	/* averages=16 */
+#define INA260_CONFIG_DEFAULT		0x6127	/* default from datasheet */
 
 /* worst case is 68.10 ms (~14.6Hz, ina219) */
 #define INA2XX_CONVERSION_RATE		15
 #define INA2XX_MAX_DELAY		69 /* worst case delay in ms */
 
 #define INA2XX_RSHUNT_DEFAULT		10000
+#define INA2XX_CURRENT_LSB_uA_DEFAULT	1250
+#define INA2XX_POWER_LSB_uW_DEFAULT	10000
 
 /* bit mask for reading the averaging setting in the configuration register */
 #define INA226_AVG_RD_MASK		0x0E00
@@ -99,10 +104,11 @@ static struct regmap_config ina2xx_regmap_config = {
 	.val_bits = 16,
 };
 
-enum ina2xx_ids { ina219, ina226 };
+enum ina2xx_ids { ina219, ina226, ina260 };
 
 struct ina2xx_config {
 	u16 config_default;
+	u8 ishunt;				/* integrated shunt? */
 	int calibration_value;
 	int registers;
 	int shunt_div;
@@ -126,6 +132,7 @@ struct ina2xx_data {
 static const struct ina2xx_config ina2xx_config[] = {
 	[ina219] = {
 		.config_default = INA219_CONFIG_DEFAULT,
+		.ishunt = 0,
 		.calibration_value = 4096,
 		.registers = INA219_REGISTERS,
 		.shunt_div = 100,
@@ -135,6 +142,7 @@ static const struct ina2xx_config ina2xx_config[] = {
 	},
 	[ina226] = {
 		.config_default = INA226_CONFIG_DEFAULT,
+		.ishunt = 0,
 		.calibration_value = 2048,
 		.registers = INA226_REGISTERS,
 		.shunt_div = 400,
@@ -142,6 +150,14 @@ static const struct ina2xx_config ina2xx_config[] = {
 		.bus_voltage_lsb = 1250,
 		.power_lsb_factor = 25,
 	},
+	[ina260] = {
+		.config_default = INA260_CONFIG_DEFAULT,
+		.ishunt = 1,
+		.registers = INA260_REGISTERS,
+		.shunt_div = 1,
+		.bus_voltage_shift = 0,
+		.bus_voltage_lsb = 1250,
+	},
 };
 
 /*
@@ -201,7 +217,10 @@ static int ina2xx_init(struct ina2xx_data *data)
 	if (ret < 0)
 		return ret;
 
-	return ina2xx_calibrate(data);
+	if (!data->config->ishunt)
+		return ina2xx_calibrate(data);
+
+	return 0;
 }
 
 static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval)
@@ -213,6 +232,10 @@ static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval)
 
 	for (retry = 5; retry; retry--) {
 
+		/* Shunt register is used for current register when using integrated shunt */
+		if (data->config->ishunt && reg == INA2XX_CURRENT)
+			reg = INA2XX_CURRENT_ISHUNT;
+
 		ret = regmap_read(data->regmap, reg, regval);
 		if (ret < 0)
 			return ret;
@@ -227,7 +250,7 @@ static int ina2xx_read_reg(struct device *dev, int reg, unsigned int *regval)
 		 * We do that extra read of the calibration register if there
 		 * is some hint of a chip reset.
 		 */
-		if (*regval == 0) {
+		if (*regval == 0 && !data->config->ishunt) {
 			unsigned int cal;
 
 			ret = regmap_read(data->regmap, INA2XX_CALIBRATION,
@@ -594,6 +617,18 @@ static const struct attribute_group ina2xx_group = {
 	.attrs = ina2xx_attrs,
 };
 
+/* integrated shunt devices attributes */
+static struct attribute *ina2xx_ishunt_attrs[] = {
+	&sensor_dev_attr_in1_input.dev_attr.attr,
+	&sensor_dev_attr_curr1_input.dev_attr.attr,
+	&sensor_dev_attr_power1_input.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ina2xx_ishunt_group = {
+	.attrs = ina2xx_ishunt_attrs,
+};
+
 static struct attribute *ina226_attrs[] = {
 	&sensor_dev_attr_in0_crit.dev_attr.attr,
 	&sensor_dev_attr_in0_lcrit.dev_attr.attr,
@@ -637,16 +672,19 @@ static int ina2xx_probe(struct i2c_client *client)
 	data->config = &ina2xx_config[chip];
 	mutex_init(&data->config_lock);
 
-	if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) {
-		struct ina2xx_platform_data *pdata = dev_get_platdata(dev);
+	/* Devices with no integrated shunt */
+	if (!data->config->ishunt) {
+		if (of_property_read_u32(dev->of_node, "shunt-resistor", &val) < 0) {
+			struct ina2xx_platform_data *pdata = dev_get_platdata(dev);
 
-		if (pdata)
-			val = pdata->shunt_uohms;
-		else
-			val = INA2XX_RSHUNT_DEFAULT;
-	}
+			if (pdata)
+				val = pdata->shunt_uohms;
+			else
+				val = INA2XX_RSHUNT_DEFAULT;
+		}
 
-	ina2xx_set_shunt(data, val);
+		ina2xx_set_shunt(data, val);
+	}
 
 	ina2xx_regmap_config.max_register = data->config->registers;
 
@@ -666,9 +704,15 @@ static int ina2xx_probe(struct i2c_client *client)
 		return -ENODEV;
 	}
 
-	data->groups[group++] = &ina2xx_group;
-	if (chip == ina226)
-		data->groups[group++] = &ina226_group;
+	if (data->config->ishunt) {
+		data->groups[group++] = &ina2xx_ishunt_group;
+		data->current_lsb_uA = INA2XX_CURRENT_LSB_uA_DEFAULT;
+		data->power_lsb_uW = INA2XX_POWER_LSB_uW_DEFAULT;
+	} else {
+		data->groups[group++] = &ina2xx_group;
+		if (chip == ina226)
+			data->groups[group++] = &ina226_group;
+	}
 
 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
 							   data, data->groups);
@@ -687,6 +731,7 @@ static const struct i2c_device_id ina2xx_id[] = {
 	{ "ina226", ina226 },
 	{ "ina230", ina226 },
 	{ "ina231", ina226 },
+	{ "ina260", ina260 },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, ina2xx_id);
@@ -712,6 +757,10 @@ static const struct of_device_id __maybe_unused ina2xx_of_match[] = {
 		.compatible = "ti,ina231",
 		.data = (void *)ina226
 	},
+	{
+		.compatible = "ti,ina260",
+		.data = (void *)ina260
+	},
 	{ },
 };
 MODULE_DEVICE_TABLE(of, ina2xx_of_match);
-- 
2.41.0

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ