Skip to contentSkip to conversation

iio: humidity: hts221: update probe and logging implementations

PATCH In review Adi Nata 3 participants 10 messages Aug 13 – Aug 22

Subsystems (2)
lore
Share & tools
lore

Adi Nata

author
#0 lore raw

v2 cover iio: humidity: hts221: update probe and logging implementations

HTS221 is an older driver. This series groups a few small modernizations based on current development practices.

- Report available values via read_avail()
- Add a blank line after variable declarations (style)
- Allow unknown whoami for DT fallback instead of erroring out
- Use dev_err_probe() in probe paths
- Reject calibration data that would cause division by zero

No HTS221 hardware was available. Verified with checkpatch --strict and builds of hts221, hts221_i2c and hts221_spi. Probe and calibration error paths were exercised against i2c-stub with register contents pre-loaded via i2cset.

---
Changes in v2:
- Use a local 'dev' pointer instead of hw->dev in error logging helpers
- Fix punctuation in the commit message of patch 2/5
- Adjust commit message for patch 1/5 to include fixes due to ABI change of in_humidityrelative_oversampling_ratio_available

v1: https://lore.kernel.org/all/[email protected]/

Thanks to the reviewers for all the time you spent going through the code and the comments.

Adi Nata (5):
iio: humidity: hts221: report available values via read_avail()
iio: humidity: hts221: Add a blank line after variable declarations
iio: humidity: hts221: Allow unknown whoami for DT fallback
iio: humidity: hts221: use dev_err_probe() in probe paths
iio: humidity: hts221: fix division by zero in calibration parsing

 drivers/iio/humidity/hts221_buffer.c |   1 +
 drivers/iio/humidity/hts221_core.c   | 168 ++++++++++++---------------
 drivers/iio/humidity/hts221_i2c.c    |  11 +-
 drivers/iio/humidity/hts221_spi.c    |  11 +-
 4 files changed, 84 insertions(+), 107 deletions(-)
Signature · 1 line
2.47.3
author
#1 lore raw

PATCH v2 1/5 iio: humidity: hts221: report available values via read_avail()

Replace the custom oversampling_ratio_available sysfs attributes with the standard IIO read_avail() callback. This lets the IIO core create and format *_available attributes and exposes the lists to in-kernel consumers.

As a consequence, the incorrectly named in_humidity_oversampling_ratio_available attribute is replaced by the standard in_humidityrelative_oversampling_ratio_available name for the IIO_HUMIDITYRELATIVE channel. The temperature oversampling and sampling frequency available attribute names are unchanged.

Fixes e4a70e3e7d84 ("iio: humidity: add support to hts221 rh/temp combo device")
---
 drivers/iio/humidity/hts221_core.c | 106 +++++++++++------------------
 1 file changed, 40 insertions(+), 66 deletions(-)
drivers/iio/humidity/hts221_core.c +40 -66
index bfeb0a60d3af..76a391f421f5 100644
@@ -10,7 +10,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/device.h>
-#include <linux/iio/sysfs.h>
 #include <linux/delay.h>
 #include <linux/pm.h>
 #include <linux/regmap.h>
@@ -49,7 +48,7 @@ struct hts221_odr {
 struct hts221_avg {
 	u8 addr;
 	u8 mask;
-	u16 avg_avl[HTS221_AVG_DEPTH];
+	int avg_avl[HTS221_AVG_DEPTH];
 };
 
 static const struct hts221_odr hts221_odr_table[] = {
@@ -58,6 +57,8 @@ static const struct hts221_odr hts221_odr_table[] = {
 	{ 13, 0x03 },	/* 12.5Hz */
 };
 
+static const int hts221_odr_avail[] = { 1, 7, 13 };
+
 static const struct hts221_avg hts221_avg_list[] = {
 	{
 		.addr = 0x10,
@@ -97,7 +98,11 @@ static const struct iio_chan_spec hts221_channels[] = {
 				      BIT(IIO_CHAN_INFO_OFFSET) |
 				      BIT(IIO_CHAN_INFO_SCALE) |
 				      BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+		.info_mask_separate_available =
+				BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_shared_by_all_available =
+				BIT(IIO_CHAN_INFO_SAMP_FREQ),
 		.scan_index = 0,
 		.scan_type = {
 			.sign = 's',
@@ -113,7 +118,11 @@ static const struct iio_chan_spec hts221_channels[] = {
 				      BIT(IIO_CHAN_INFO_OFFSET) |
 				      BIT(IIO_CHAN_INFO_SCALE) |
 				      BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+		.info_mask_separate_available =
+				BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
 		.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+		.info_mask_shared_by_all_available =
+				BIT(IIO_CHAN_INFO_SAMP_FREQ),
 		.scan_index = 1,
 		.scan_type = {
 			.sign = 's',
@@ -192,53 +201,35 @@ static int hts221_update_avg(struct hts221_hw *hw,
 	return 0;
 }
 
-static ssize_t hts221_sysfs_sampling_freq(struct device *dev,
-					  struct device_attribute *attr,
-					  char *buf)
-{
-	int i;
-	ssize_t len = 0;
-
-	for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
-		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
-				 hts221_odr_table[i].hz);
-	buf[len - 1] = '\n';
-
-	return len;
-}
-
-static ssize_t
-hts221_sysfs_rh_oversampling_avail(struct device *dev,
-				   struct device_attribute *attr,
-				   char *buf)
-{
-	const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_H];
-	ssize_t len = 0;
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
-		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
-				 avg->avg_avl[i]);
-	buf[len - 1] = '\n';
-
-	return len;
-}
-
-static ssize_t
-hts221_sysfs_temp_oversampling_avail(struct device *dev,
-				     struct device_attribute *attr,
-				     char *buf)
+static int hts221_read_avail(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan,
+			     const int **vals, int *type, int *length,
+			     long mask)
 {
-	const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_T];
-	ssize_t len = 0;
-	int i;
-
-	for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
-		len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
-				 avg->avg_avl[i]);
-	buf[len - 1] = '\n';
-
-	return len;
+	switch (mask) {
+	case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+		switch (chan->type) {
+		case IIO_HUMIDITYRELATIVE:
+			*vals = hts221_avg_list[HTS221_SENSOR_H].avg_avl;
+			*length = ARRAY_SIZE(hts221_avg_list[HTS221_SENSOR_H].avg_avl);
+			break;
+		case IIO_TEMP:
+			*vals = hts221_avg_list[HTS221_SENSOR_T].avg_avl;
+			*length = ARRAY_SIZE(hts221_avg_list[HTS221_SENSOR_T].avg_avl);
+			break;
+		default:
+			return -EINVAL;
+		}
+		*type = IIO_VAL_INT;
+		return IIO_AVAIL_LIST;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*vals = hts221_odr_avail;
+		*type = IIO_VAL_INT;
+		*length = ARRAY_SIZE(hts221_odr_avail);
+		return IIO_AVAIL_LIST;
+	default:
+		return -EINVAL;
+	}
 }
 
 int hts221_set_enable(struct hts221_hw *hw, bool enable)
@@ -521,27 +512,10 @@ static int hts221_validate_trigger(struct iio_dev *iio_dev,
 	return hw->trig == trig ? 0 : -EINVAL;
 }
 
-static IIO_DEVICE_ATTR(in_humidity_oversampling_ratio_available, S_IRUGO,
-		       hts221_sysfs_rh_oversampling_avail, NULL, 0);
-static IIO_DEVICE_ATTR(in_temp_oversampling_ratio_available, S_IRUGO,
-		       hts221_sysfs_temp_oversampling_avail, NULL, 0);
-static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hts221_sysfs_sampling_freq);
-
-static struct attribute *hts221_attributes[] = {
-	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
-	&iio_dev_attr_in_humidity_oversampling_ratio_available.dev_attr.attr,
-	&iio_dev_attr_in_temp_oversampling_ratio_available.dev_attr.attr,
-	NULL,
-};
-
-static const struct attribute_group hts221_attribute_group = {
-	.attrs = hts221_attributes,
-};
-
 static const struct iio_info hts221_info = {
-	.attrs = &hts221_attribute_group,
 	.read_raw = hts221_read_raw,
 	.write_raw = hts221_write_raw,
+	.read_avail = hts221_read_avail,
 	.validate_trigger = hts221_validate_trigger,
 };
 
Signature · 1 line
2.47.3
author
#2 lore raw

PATCH v2 2/5 iio: humidity: hts221: Add a blank line after variable declarations

In reply to Adi Nata

Improve code readability per checkpatch warning.

---
 drivers/iio/humidity/hts221_buffer.c | 1 +
 1 file changed, 1 insertion(+)
drivers/iio/humidity/hts221_buffer.c +1 -0
index 4d03db19063e..c868549d09c0 100644
@@ -194,6 +194,7 @@ static irqreturn_t hts221_buffer_handler_thread(int irq, void *p)
 int hts221_allocate_buffers(struct iio_dev *iio_dev)
 {
 	struct hts221_hw *hw = iio_priv(iio_dev);
+
 	return devm_iio_triggered_buffer_setup(hw->dev, iio_dev,
 					NULL, hts221_buffer_handler_thread,
 					&hts221_buffer_ops);
Signature · 1 line
2.47.3
author
#3 lore raw

PATCH v2 3/5 iio: humidity: hts221: Allow unknown whoami for DT fallback

In reply to Adi Nata

The WHOAMI check currently returns -ENODEV when the chip ID is not 0xbc. That rejects Device Tree fallback compatibles.

Keep failing if the WHOAMI register cannot be read. On an unexpected ID, log it and continue so OF fallback matching can work.

Use dev_err_probe() for the read-failure path.

---
 drivers/iio/humidity/hts221_core.c | 14 +++++---------
 1 file changed, 5 insertions(+), 9 deletions(-)
drivers/iio/humidity/hts221_core.c +5 -9
index 76a391f421f5..b73c26aae9ec 100644
@@ -136,19 +136,15 @@ static const struct iio_chan_spec hts221_channels[] = {
 
 static int hts221_check_whoami(struct hts221_hw *hw)
 {
+	struct device *dev = hw->dev;
 	int err, data;
 
 	err = regmap_read(hw->regmap, HTS221_REG_WHOAMI_ADDR, &data);
-	if (err < 0) {
-		dev_err(hw->dev, "failed to read whoami register\n");
-		return err;
-	}
+	if (err < 0)
+		return dev_err_probe(dev, err, "failed to read whoami register\n");
 
-	if (data != HTS221_REG_WHOAMI_VAL) {
-		dev_err(hw->dev, "wrong whoami {%02x vs %02x}\n",
-			data, HTS221_REG_WHOAMI_VAL);
-		return -ENODEV;
-	}
+	if (data != HTS221_REG_WHOAMI_VAL)
+		dev_info(dev, "unexpected whoami 0x%02x, continuing\n", data);
 
 	return 0;
 }
Signature · 1 line
2.47.3
author
#4 lore raw

PATCH v2 4/5 iio: humidity: hts221: use dev_err_probe() in probe paths

In reply to Adi Nata

Convert remaining probe-time error logs to dev_err_probe() so the errno is included and deferred probe failures stay quiet.

---
 drivers/iio/humidity/hts221_core.c | 26 ++++++++------------------
 drivers/iio/humidity/hts221_i2c.c  | 11 ++++-------
 drivers/iio/humidity/hts221_spi.c  | 11 ++++-------
 3 files changed, 16 insertions(+), 32 deletions(-)
drivers/iio/humidity/hts221_core.c +8 -18
index b73c26aae9ec..fda38dfc6742 100644
@@ -578,33 +578,23 @@ int hts221_probe(struct device *dev, int irq, const char *name,
 
 	/* configure humidity sensor */
 	err = hts221_parse_rh_caldata(hw);
-	if (err < 0) {
-		dev_err(hw->dev, "failed to get rh calibration data\n");
-		return err;
-	}
+	if (err < 0)
+		return dev_err_probe(dev, err, "failed to get rh calibration data\n");
 
 	data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3];
 	err = hts221_update_avg(hw, HTS221_SENSOR_H, data);
-	if (err < 0) {
-		dev_err(hw->dev, "failed to set rh oversampling ratio\n");
-		return err;
-	}
+	if (err < 0)
+		return dev_err_probe(dev, err, "failed to set rh oversampling ratio\n");
 
 	/* configure temperature sensor */
 	err = hts221_parse_temp_caldata(hw);
-	if (err < 0) {
-		dev_err(hw->dev,
-			"failed to get temperature calibration data\n");
-		return err;
-	}
+	if (err < 0)
+		return dev_err_probe(dev, err, "failed to get temperature calibration data\n");
 
 	data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3];
 	err = hts221_update_avg(hw, HTS221_SENSOR_T, data);
-	if (err < 0) {
-		dev_err(hw->dev,
-			"failed to set temperature oversampling ratio\n");
-		return err;
-	}
+	if (err < 0)
+		return dev_err_probe(dev, err, "failed to set temperature oversampling ratio\n");
 
 	if (hw->irq > 0) {
 		err = hts221_allocate_buffers(iio_dev);
drivers/iio/humidity/hts221_i2c.c +4 -7
index 40276abc5d2e..e6dc9532cfbd 100644
@@ -26,17 +26,14 @@ static const struct regmap_config hts221_i2c_regmap_config = {
 
 static int hts221_i2c_probe(struct i2c_client *client)
 {
+	struct device *dev = &client->dev;
 	struct regmap *regmap;
 
 	regmap = devm_regmap_init_i2c(client, &hts221_i2c_regmap_config);
-	if (IS_ERR(regmap)) {
-		dev_err(&client->dev, "Failed to register i2c regmap %ld\n",
-			PTR_ERR(regmap));
-		return PTR_ERR(regmap);
-	}
+	if (IS_ERR(regmap))
+		return dev_err_probe(dev, PTR_ERR(regmap), "Failed to register i2c regmap\n");
 
-	return hts221_probe(&client->dev, client->irq,
-			    client->name, regmap);
+	return hts221_probe(dev, client->irq, client->name, regmap);
 }
 
 static const struct acpi_device_id hts221_acpi_match[] = {
drivers/iio/humidity/hts221_spi.c +4 -7
index e6fef2acd523..8bcc1b5d150e 100644
@@ -27,17 +27,14 @@ static const struct regmap_config hts221_spi_regmap_config = {
 
 static int hts221_spi_probe(struct spi_device *spi)
 {
+	struct device *dev = &spi->dev;
 	struct regmap *regmap;
 
 	regmap = devm_regmap_init_spi(spi, &hts221_spi_regmap_config);
-	if (IS_ERR(regmap)) {
-		dev_err(&spi->dev, "Failed to register spi regmap %ld\n",
-			PTR_ERR(regmap));
-		return PTR_ERR(regmap);
-	}
+	if (IS_ERR(regmap))
+		return dev_err_probe(dev, PTR_ERR(regmap), "Failed to register spi regmap\n");
 
-	return hts221_probe(&spi->dev, spi->irq,
-			    spi->modalias, regmap);
+	return hts221_probe(dev, spi->irq, spi->modalias, regmap);
 }
 
 static const struct of_device_id hts221_spi_of_match[] = {
Signature · 1 line
2.47.3
author
#5 lore raw

PATCH v2 5/5 iio: humidity: hts221: fix division by zero in calibration parsing

In reply to Adi Nata

hts221_parse_rh_caldata() and hts221_parse_temp_caldata() divide by (cal_x1 - cal_x0) without checking that the two calibration points differ can cause division by zero.

Reject zero divisor with -EINVAL, logging the offending calibration values. A device with such calibration data cannot produce meaningful scale or offset values anyway.

---
 drivers/iio/humidity/hts221_core.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)
drivers/iio/humidity/hts221_core.c +22 -0
index fda38dfc6742..47a10884d812 100644
@@ -245,6 +245,7 @@ int hts221_set_enable(struct hts221_hw *hw, bool enable)
 
 static int hts221_parse_temp_caldata(struct hts221_hw *hw)
 {
+	struct device *dev = hw->dev;
 	int err, *slope, *b_gen, cal0, cal1;
 	s16 cal_x0, cal_x1, cal_y0, cal_y1;
 	__le16 val;
@@ -275,10 +276,20 @@ static int hts221_parse_temp_caldata(struct hts221_hw *hw)
 		return err;
 	cal_x1 = le16_to_cpu(val);
 
+	if (cal_x1 == cal_x0)
+		return dev_err_probe(dev, -EINVAL,
+				     "invalid temperature calibration points (x0 %d, x1 %d)\n",
+				     cal_x0, cal_x1);
+
 	slope = &hw->sensors[HTS221_SENSOR_T].slope;
 	b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen;
 
 	*slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
+	if (!*slope)
+		return dev_err_probe(dev, -EINVAL,
+				     "invalid temperature calibration slope (y0 %d, y1 %d)\n",
+				     cal_y0, cal_y1);
+
 	*b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
 		 (cal_x1 - cal_x0);
 	*b_gen *= 8;
@@ -288,6 +299,7 @@ static int hts221_parse_temp_caldata(struct hts221_hw *hw)
 
 static int hts221_parse_rh_caldata(struct hts221_hw *hw)
 {
+	struct device *dev = hw->dev;
 	int err, *slope, *b_gen, data;
 	s16 cal_x0, cal_x1, cal_y0, cal_y1;
 	__le16 val;
@@ -314,10 +326,20 @@ static int hts221_parse_rh_caldata(struct hts221_hw *hw)
 		return err;
 	cal_x1 = le16_to_cpu(val);
 
+	if (cal_x1 == cal_x0)
+		return dev_err_probe(dev, -EINVAL,
+				     "invalid rh calibration points (x0 %d, x1 %d)\n",
+				     cal_x0, cal_x1);
+
 	slope = &hw->sensors[HTS221_SENSOR_H].slope;
 	b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen;
 
 	*slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
+	if (!*slope)
+		return dev_err_probe(dev, -EINVAL,
+				     "invalid rh calibration slope (y0 %d, y1 %d)\n",
+				     cal_y0, cal_y1);
+
 	*b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
 		 (cal_x1 - cal_x0);
 	*b_gen *= 8;
Signature · 1 line
2.47.3
maintainer
#6 lore raw
In reply to Adi Nata
Quoted 14 lines · On Thu, 13 Aug 2026 23:31:24 +0800 Adi Nata…

HTS221 is an older driver. This series groups a few small modernizations based on current development practices.

- Report available values via read_avail()
- Add a blank line after variable declarations (style)
- Allow unknown whoami for DT fallback instead of erroring out
- Use dev_err_probe() in probe paths
- Reject calibration data that would cause division by zero

No HTS221 hardware was available. Verified with checkpatch --strict and builds of hts221, hts221_i2c and hts221_spi. Probe and calibration error paths were exercised against i2c-stub with register contents pre-loaded via i2cset.

Quoted 7 lines

---
Changes in v2:
- Use a local 'dev' pointer instead of hw->dev in error logging helpers
- Fix punctuation in the commit message of patch 2/5
- Adjust commit message for patch 1/5 to include fixes due to ABI change of in_humidityrelative_oversampling_ratio_available

Looks like you've fixed up the stuff Andy pointed out in v1 and all looked reasonable to me, so applied to the testing branch of iio.git.

Note that branch will be rebased on 7.3-rc1 once available and only then pushed out for linux-next to pick up.

Thanks,

Jonathan

Quoted 18 lines

v1: https://lore.kernel.org/all/[email protected]/

Thanks to the reviewers for all the time you spent going through the code and the comments.

Adi Nata (5):
iio: humidity: hts221: report available values via read_avail()
iio: humidity: hts221: Add a blank line after variable declarations
iio: humidity: hts221: Allow unknown whoami for DT fallback
iio: humidity: hts221: use dev_err_probe() in probe paths
iio: humidity: hts221: fix division by zero in calibration parsing

 drivers/iio/humidity/hts221_buffer.c |   1 +
 drivers/iio/humidity/hts221_core.c   | 168 ++++++++++++---------------
 drivers/iio/humidity/hts221_i2c.c    |  11 +-
 drivers/iio/humidity/hts221_spi.c    |  11 +-
 4 files changed, 84 insertions(+), 107 deletions(-)
Reviewed-by on the v2 series
#7 lore raw
In reply to Adi Nata
Quoted 9 lines · Adi Nata

HTS221 is an older driver. This series groups a few small modernizations based on current development practices.

- Report available values via read_avail()
- Add a blank line after variable declarations (style)
- Allow unknown whoami for DT fallback instead of erroring out
- Use dev_err_probe() in probe paths
- Reject calibration data that would cause division by zero

Quoted 4 lines

No HTS221 hardware was available. Verified with checkpatch --strict and builds of hts221, hts221_i2c and hts221_spi. Probe and calibration error paths were exercised against i2c-stub with register contents pre-loaded via i2cset.

Signature · 2 lines
With Best Regards,
Andy Shevchenko
author
#8 lore raw
Quoted 6 lines

Looks like you've fixed up the stuff Andy pointed out in v1 and all looked reasonable to me, so applied to the testing branch of iio.git.

Note that branch will be rebased on 7.3-rc1 once available and only then pushed out for linux-next to pick up.

Thank you for the advice and help.

Regards,
Adi Nata

maintainer
#9 lore raw
Quoted 9 lines · On Mon, 17 Aug 2026 22:55:17 +0800 Adi <[email protected]>…
Quoted 6 lines

Looks like you've fixed up the stuff Andy pointed out in v1 and all looked reasonable to me, so applied to the testing branch of iio.git.

Note that branch will be rebased on 7.3-rc1 once available and only then pushed out for linux-next to pick up.

Thank you for the advice and help.

Added the tag.
Thanks

J

Quoted 2 lines

Regards,
Adi Nata