[PATCH v2 2/2] hwmon: (sht4x): Add support for Sensirion STS4x temperature sensors

Alessandro Zini <[email protected]>
Newsgroups org.kernel.vger.linux-hwmon,org.kernel.vger.linux-devicetree,org.kernel.vger.linux-doc,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
The Sensirion STS4x series is the temperature-only variant of the
SHT4x family. It shares the same I2C command set, conversion formulas,
CRC checksum, and timing with the SHT4x, but only returns temperature
data (3 bytes: 2 data bytes + 1 CRC byte).

Add support for the STS4x series by dynamically adjusting the read
response length, suppressing humidity channel attributes when probed
as STS4x, and omitting heater sysfs attributes.

Link: https://sensirion.com/resource/datasheet/sts4x
Signed-off-by: Alessandro Zini <[email protected]>
---
Changes in v2:
- Unconditionally initialize data->heating_complete in probe to avoid
  an msleep delay on boot with INITIAL_JIFFIES.
- Bypass heater delay checks in sht4x_read_values() when chip is sts4x.

 Documentation/hwmon/sht4x.rst | 17 ++++++++--
 drivers/hwmon/sht4x.c         | 62 +++++++++++++++++++++++++----------
 2 files changed, 59 insertions(+), 20 deletions(-)

diff --git a/Documentation/hwmon/sht4x.rst b/Documentation/hwmon/sht4x.rst
index ba094ad0e2816..b9564632a1be2 100644
--- a/Documentation/hwmon/sht4x.rst
+++ b/Documentation/hwmon/sht4x.rst
@@ -15,6 +15,16 @@ Supported Chips:
 
       English: https://www.sensirion.com/fileadmin/user_upload/customers/sensirion/Dokumente/2_Humidity_Sensors/Datasheets/Sensirion_Humidity_Sensors_SHT4x_Datasheet.pdf
 
+  * Sensirion STS4X
+
+    Prefix: 'sts4x'
+
+    Addresses scanned: None
+
+    Datasheet:
+
+      English: https://sensirion.com/resource/datasheet/sts4x
+
 Author: Navin Sankar Velliangiri <[email protected]>
 
 
@@ -22,9 +32,10 @@ Description
 -----------
 
 This driver implements support for the Sensirion SHT4x chip, a humidity
-and temperature sensor. Temperature is measured in degree celsius, relative
-humidity is expressed as a percentage. In sysfs interface, all values are
-scaled by 1000, i.e. the value for 31.5 degrees celsius is 31500.
+and temperature sensor, and the Sensirion STS4x chip, a temperature sensor.
+Temperature is measured in degree celsius, relative humidity is expressed as a
+percentage (on SHT4x only). In sysfs interface, all values are scaled by 1000,
+i.e. the value for 31.5 degrees celsius is 31500.
 
 Usage Notes
 -----------
diff --git a/drivers/hwmon/sht4x.c b/drivers/hwmon/sht4x.c
index 9cace0e8acdab..daca24cfbcd52 100644
--- a/drivers/hwmon/sht4x.c
+++ b/drivers/hwmon/sht4x.c
@@ -43,6 +43,7 @@
 #define SHT4X_CRC8_LEN		1
 #define SHT4X_WORD_LEN		2
 #define SHT4X_RESPONSE_LENGTH	6
+#define STS4X_RESPONSE_LENGTH	3
 #define SHT4X_CRC8_POLYNOMIAL	0x31
 #define SHT4X_CRC8_INIT		0xff
 #define SHT4X_MIN_TEMPERATURE	-45000
@@ -52,9 +53,15 @@
 
 DECLARE_CRC8_TABLE(sht4x_crc8_table);
 
+enum sht4x_chips {
+	sht4x,
+	sts4x,
+};
+
 /**
  * struct sht4x_data - All the data required to operate an SHT4X chip
  * @client: the i2c client associated with the SHT4X
+ * @chip_id: the chip type (sht4x or sts4x)
  * @heating_complete: the time that the last heating finished
  * @data_pending: true if and only if there are measurements to retrieve after heating
  * @heater_power: the power at which the heater will be started
@@ -67,6 +74,7 @@ DECLARE_CRC8_TABLE(sht4x_crc8_table);
  */
 struct sht4x_data {
 	struct i2c_client	*client;
+	enum sht4x_chips	chip_id;
 	unsigned long		heating_complete;	/* in jiffies */
 	bool			data_pending;
 	u32			heater_power;	/* in milli-watts */
@@ -92,11 +100,15 @@ static int sht4x_read_values(struct sht4x_data *data)
 	u8 crc;
 	u8 cmd[SHT4X_CMD_LEN] = {SHT4X_CMD_MEASURE_HPM};
 	u8 raw_data[SHT4X_RESPONSE_LENGTH];
+	size_t response_length = data->chip_id == sts4x ?
+				 STS4X_RESPONSE_LENGTH : SHT4X_RESPONSE_LENGTH;
 	unsigned long curr_jiffies;
 
-	curr_jiffies = jiffies;
-	if (time_before(curr_jiffies, data->heating_complete))
-		msleep(jiffies_to_msecs(data->heating_complete - curr_jiffies));
+	if (data->chip_id != sts4x) {
+		curr_jiffies = jiffies;
+		if (time_before(curr_jiffies, data->heating_complete))
+			msleep(jiffies_to_msecs(data->heating_complete - curr_jiffies));
+	}
 
 	if (data->data_pending &&
 	    time_before(jiffies, data->heating_complete + data->update_interval)) {
@@ -115,15 +127,14 @@ static int sht4x_read_values(struct sht4x_data *data)
 		usleep_range(SHT4X_MEAS_DELAY_HPM, SHT4X_MEAS_DELAY_HPM + SHT4X_DELAY_EXTRA);
 	}
 
-	ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH);
-	if (ret != SHT4X_RESPONSE_LENGTH) {
+	ret = i2c_master_recv(client, raw_data, response_length);
+	if (ret != response_length) {
 		if (ret >= 0)
 			ret = -ENODATA;
 		return ret;
 	}
 
 	t_ticks = raw_data[0] << 8 | raw_data[1];
-	rh_ticks = raw_data[3] << 8 | raw_data[4];
 
 	crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
 	if (crc != raw_data[2]) {
@@ -131,14 +142,19 @@ static int sht4x_read_values(struct sht4x_data *data)
 		return -EIO;
 	}
 
-	crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
-	if (crc != raw_data[5]) {
-		dev_err(&client->dev, "data integrity check failed\n");
-		return -EIO;
+	data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000;
+
+	if (data->chip_id != sts4x) {
+		rh_ticks = raw_data[3] << 8 | raw_data[4];
+		crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
+		if (crc != raw_data[5]) {
+			dev_err(&client->dev, "data integrity check failed\n");
+			return -EIO;
+		}
+
+		data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000;
 	}
 
-	data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000;
-	data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000;
 	data->last_updated = jiffies;
 	data->valid = true;
 	return 0;
@@ -190,9 +206,14 @@ static umode_t sht4x_hwmon_visible(const void *data,
 				   enum hwmon_sensor_types type,
 				   u32 attr, int channel)
 {
+	const struct sht4x_data *chip_data = data;
+
 	switch (type) {
 	case hwmon_temp:
+		return 0444;
 	case hwmon_humidity:
+		if (chip_data->chip_id == sts4x)
+			return 0;
 		return 0444;
 	case hwmon_chip:
 		return 0644;
@@ -382,6 +403,7 @@ static const struct hwmon_chip_info sht4x_chip_info = {
 
 static int sht4x_probe(struct i2c_client *client)
 {
+	const struct attribute_group **groups = NULL;
 	struct device *device = &client->dev;
 	struct device *hwmon_dev;
 	struct sht4x_data *data;
@@ -400,11 +422,15 @@ static int sht4x_probe(struct i2c_client *client)
 	if (!data)
 		return -ENOMEM;
 
+	data->chip_id = (uintptr_t)i2c_get_match_data(client);
 	data->update_interval = SHT4X_MIN_POLL_INTERVAL;
 	data->client = client;
-	data->heater_power = 200;
-	data->heater_time = 1000;
 	data->heating_complete = jiffies;
+	if (data->chip_id != sts4x) {
+		data->heater_power = 200;
+		data->heater_time = 1000;
+		groups = sht4x_groups;
+	}
 
 	crc8_populate_msb(sht4x_crc8_table, SHT4X_CRC8_POLYNOMIAL);
 
@@ -418,19 +444,21 @@ static int sht4x_probe(struct i2c_client *client)
 							 client->name,
 							 data,
 							 &sht4x_chip_info,
-							 sht4x_groups);
+							 groups);
 
 	return PTR_ERR_OR_ZERO(hwmon_dev);
 }
 
 static const struct i2c_device_id sht4x_id[] = {
-	{ .name = "sht4x" },
+	{ .name = "sht4x", .driver_data = sht4x },
+	{ .name = "sts4x", .driver_data = sts4x },
 	{ }
 };
 MODULE_DEVICE_TABLE(i2c, sht4x_id);
 
 static const struct of_device_id sht4x_of_match[] = {
-	{ .compatible = "sensirion,sht4x" },
+	{ .compatible = "sensirion,sht4x", .data = (void *)sht4x },
+	{ .compatible = "sensirion,sts40", .data = (void *)sts4x },
 	{ }
 };
 MODULE_DEVICE_TABLE(of, sht4x_of_match);
-- 
2.55.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.