xref: /linux/drivers/hwmon/sht4x.c (revision 505c2549373f3aa9ee16493f872e57876ffb70b1)
1*505c2549SNavin Sankar Velliangiri // SPDX-License-Identifier: GPL-2.0-only
2*505c2549SNavin Sankar Velliangiri 
3*505c2549SNavin Sankar Velliangiri /*
4*505c2549SNavin Sankar Velliangiri  * Copyright (c) Linumiz 2021
5*505c2549SNavin Sankar Velliangiri  *
6*505c2549SNavin Sankar Velliangiri  * sht4x.c - Linux hwmon driver for SHT4x Temperature and Humidity sensor
7*505c2549SNavin Sankar Velliangiri  *
8*505c2549SNavin Sankar Velliangiri  * Author: Navin Sankar Velliangiri <navin@linumiz.com>
9*505c2549SNavin Sankar Velliangiri  */
10*505c2549SNavin Sankar Velliangiri 
11*505c2549SNavin Sankar Velliangiri #include <linux/crc8.h>
12*505c2549SNavin Sankar Velliangiri #include <linux/delay.h>
13*505c2549SNavin Sankar Velliangiri #include <linux/hwmon.h>
14*505c2549SNavin Sankar Velliangiri #include <linux/i2c.h>
15*505c2549SNavin Sankar Velliangiri #include <linux/jiffies.h>
16*505c2549SNavin Sankar Velliangiri #include <linux/module.h>
17*505c2549SNavin Sankar Velliangiri 
18*505c2549SNavin Sankar Velliangiri /*
19*505c2549SNavin Sankar Velliangiri  * Poll intervals (in milliseconds)
20*505c2549SNavin Sankar Velliangiri  */
21*505c2549SNavin Sankar Velliangiri #define SHT4X_MIN_POLL_INTERVAL	2000
22*505c2549SNavin Sankar Velliangiri 
23*505c2549SNavin Sankar Velliangiri /*
24*505c2549SNavin Sankar Velliangiri  * I2C command delays (in microseconds)
25*505c2549SNavin Sankar Velliangiri  */
26*505c2549SNavin Sankar Velliangiri #define SHT4X_MEAS_DELAY	1000
27*505c2549SNavin Sankar Velliangiri #define SHT4X_DELAY_EXTRA	10000
28*505c2549SNavin Sankar Velliangiri 
29*505c2549SNavin Sankar Velliangiri /*
30*505c2549SNavin Sankar Velliangiri  * Command Bytes
31*505c2549SNavin Sankar Velliangiri  */
32*505c2549SNavin Sankar Velliangiri #define SHT4X_CMD_MEASURE_HPM	0b11111101
33*505c2549SNavin Sankar Velliangiri #define SHT4X_CMD_RESET		0b10010100
34*505c2549SNavin Sankar Velliangiri 
35*505c2549SNavin Sankar Velliangiri #define SHT4X_CMD_LEN		1
36*505c2549SNavin Sankar Velliangiri #define SHT4X_CRC8_LEN		1
37*505c2549SNavin Sankar Velliangiri #define SHT4X_WORD_LEN		2
38*505c2549SNavin Sankar Velliangiri #define SHT4X_RESPONSE_LENGTH	6
39*505c2549SNavin Sankar Velliangiri #define SHT4X_CRC8_POLYNOMIAL	0x31
40*505c2549SNavin Sankar Velliangiri #define SHT4X_CRC8_INIT		0xff
41*505c2549SNavin Sankar Velliangiri #define SHT4X_MIN_TEMPERATURE	-45000
42*505c2549SNavin Sankar Velliangiri #define SHT4X_MAX_TEMPERATURE	125000
43*505c2549SNavin Sankar Velliangiri #define SHT4X_MIN_HUMIDITY	0
44*505c2549SNavin Sankar Velliangiri #define SHT4X_MAX_HUMIDITY	100000
45*505c2549SNavin Sankar Velliangiri 
46*505c2549SNavin Sankar Velliangiri DECLARE_CRC8_TABLE(sht4x_crc8_table);
47*505c2549SNavin Sankar Velliangiri 
48*505c2549SNavin Sankar Velliangiri /**
49*505c2549SNavin Sankar Velliangiri  * struct sht4x_data - All the data required to operate an SHT4X chip
50*505c2549SNavin Sankar Velliangiri  * @client: the i2c client associated with the SHT4X
51*505c2549SNavin Sankar Velliangiri  * @lock: a mutex that is used to prevent parallel access to the i2c client
52*505c2549SNavin Sankar Velliangiri  * @update_interval: the minimum poll interval
53*505c2549SNavin Sankar Velliangiri  * @last_updated: the previous time that the SHT4X was polled
54*505c2549SNavin Sankar Velliangiri  * @temperature: the latest temperature value received from the SHT4X
55*505c2549SNavin Sankar Velliangiri  * @humidity: the latest humidity value received from the SHT4X
56*505c2549SNavin Sankar Velliangiri  */
57*505c2549SNavin Sankar Velliangiri struct sht4x_data {
58*505c2549SNavin Sankar Velliangiri 	struct i2c_client	*client;
59*505c2549SNavin Sankar Velliangiri 	struct mutex		lock;	/* atomic read data updates */
60*505c2549SNavin Sankar Velliangiri 	bool			valid;	/* validity of fields below */
61*505c2549SNavin Sankar Velliangiri 	long			update_interval;	/* in milli-seconds */
62*505c2549SNavin Sankar Velliangiri 	long			last_updated;	/* in jiffies */
63*505c2549SNavin Sankar Velliangiri 	s32			temperature;
64*505c2549SNavin Sankar Velliangiri 	s32			humidity;
65*505c2549SNavin Sankar Velliangiri };
66*505c2549SNavin Sankar Velliangiri 
67*505c2549SNavin Sankar Velliangiri /**
68*505c2549SNavin Sankar Velliangiri  * sht4x_read_values() - read and parse the raw data from the SHT4X
69*505c2549SNavin Sankar Velliangiri  * @sht4x_data: the struct sht4x_data to use for the lock
70*505c2549SNavin Sankar Velliangiri  * Return: 0 if succesfull, 1 if not
71*505c2549SNavin Sankar Velliangiri  */
72*505c2549SNavin Sankar Velliangiri static int sht4x_read_values(struct sht4x_data *data)
73*505c2549SNavin Sankar Velliangiri {
74*505c2549SNavin Sankar Velliangiri 	int ret = 0;
75*505c2549SNavin Sankar Velliangiri 	u16 t_ticks, rh_ticks;
76*505c2549SNavin Sankar Velliangiri 	unsigned long next_update;
77*505c2549SNavin Sankar Velliangiri 	struct i2c_client *client = data->client;
78*505c2549SNavin Sankar Velliangiri 	u8 crc, raw_data[SHT4X_RESPONSE_LENGTH],
79*505c2549SNavin Sankar Velliangiri 	cmd[] = {SHT4X_CMD_MEASURE_HPM};
80*505c2549SNavin Sankar Velliangiri 
81*505c2549SNavin Sankar Velliangiri 	mutex_lock(&data->lock);
82*505c2549SNavin Sankar Velliangiri 	next_update = data->last_updated +
83*505c2549SNavin Sankar Velliangiri 		      msecs_to_jiffies(data->update_interval);
84*505c2549SNavin Sankar Velliangiri 	if (!data->valid || time_after(jiffies, next_update)) {
85*505c2549SNavin Sankar Velliangiri 		ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN);
86*505c2549SNavin Sankar Velliangiri 		if (ret < 0)
87*505c2549SNavin Sankar Velliangiri 			goto unlock;
88*505c2549SNavin Sankar Velliangiri 
89*505c2549SNavin Sankar Velliangiri 		usleep_range(SHT4X_MEAS_DELAY,
90*505c2549SNavin Sankar Velliangiri 			     SHT4X_MEAS_DELAY + SHT4X_DELAY_EXTRA);
91*505c2549SNavin Sankar Velliangiri 
92*505c2549SNavin Sankar Velliangiri 		ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH);
93*505c2549SNavin Sankar Velliangiri 		if (ret != SHT4X_RESPONSE_LENGTH) {
94*505c2549SNavin Sankar Velliangiri 			if (ret >= 0)
95*505c2549SNavin Sankar Velliangiri 				ret = -ENODATA;
96*505c2549SNavin Sankar Velliangiri 
97*505c2549SNavin Sankar Velliangiri 			goto unlock;
98*505c2549SNavin Sankar Velliangiri 		}
99*505c2549SNavin Sankar Velliangiri 
100*505c2549SNavin Sankar Velliangiri 		t_ticks = raw_data[0] << 8 | raw_data[1];
101*505c2549SNavin Sankar Velliangiri 		rh_ticks = raw_data[3] << 8 | raw_data[4];
102*505c2549SNavin Sankar Velliangiri 
103*505c2549SNavin Sankar Velliangiri 		crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
104*505c2549SNavin Sankar Velliangiri 		if (crc != raw_data[2]) {
105*505c2549SNavin Sankar Velliangiri 			dev_err(&client->dev, "data integrity check failed\n");
106*505c2549SNavin Sankar Velliangiri 			ret = -EIO;
107*505c2549SNavin Sankar Velliangiri 			goto unlock;
108*505c2549SNavin Sankar Velliangiri 		}
109*505c2549SNavin Sankar Velliangiri 
110*505c2549SNavin Sankar Velliangiri 		crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
111*505c2549SNavin Sankar Velliangiri 		if (crc != raw_data[5]) {
112*505c2549SNavin Sankar Velliangiri 			dev_err(&client->dev, "data integrity check failed\n");
113*505c2549SNavin Sankar Velliangiri 			ret = -EIO;
114*505c2549SNavin Sankar Velliangiri 			goto unlock;
115*505c2549SNavin Sankar Velliangiri 		}
116*505c2549SNavin Sankar Velliangiri 
117*505c2549SNavin Sankar Velliangiri 		data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000;
118*505c2549SNavin Sankar Velliangiri 		data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000;
119*505c2549SNavin Sankar Velliangiri 		data->last_updated = jiffies;
120*505c2549SNavin Sankar Velliangiri 		data->valid = true;
121*505c2549SNavin Sankar Velliangiri 	}
122*505c2549SNavin Sankar Velliangiri 
123*505c2549SNavin Sankar Velliangiri unlock:
124*505c2549SNavin Sankar Velliangiri 	mutex_unlock(&data->lock);
125*505c2549SNavin Sankar Velliangiri 	return ret;
126*505c2549SNavin Sankar Velliangiri }
127*505c2549SNavin Sankar Velliangiri 
128*505c2549SNavin Sankar Velliangiri static ssize_t sht4x_interval_write(struct sht4x_data *data, long val)
129*505c2549SNavin Sankar Velliangiri {
130*505c2549SNavin Sankar Velliangiri 	data->update_interval = clamp_val(val, SHT4X_MIN_POLL_INTERVAL, UINT_MAX);
131*505c2549SNavin Sankar Velliangiri 
132*505c2549SNavin Sankar Velliangiri 	return 0;
133*505c2549SNavin Sankar Velliangiri }
134*505c2549SNavin Sankar Velliangiri 
135*505c2549SNavin Sankar Velliangiri /**
136*505c2549SNavin Sankar Velliangiri  * sht4x_interval_read() - read the minimum poll interval
137*505c2549SNavin Sankar Velliangiri  *			   in milliseconds
138*505c2549SNavin Sankar Velliangiri  */
139*505c2549SNavin Sankar Velliangiri static size_t sht4x_interval_read(struct sht4x_data *data, long *val)
140*505c2549SNavin Sankar Velliangiri {
141*505c2549SNavin Sankar Velliangiri 	*val = data->update_interval;
142*505c2549SNavin Sankar Velliangiri 	return 0;
143*505c2549SNavin Sankar Velliangiri }
144*505c2549SNavin Sankar Velliangiri 
145*505c2549SNavin Sankar Velliangiri /**
146*505c2549SNavin Sankar Velliangiri  * sht4x_temperature1_read() - read the temperature in millidegrees
147*505c2549SNavin Sankar Velliangiri  */
148*505c2549SNavin Sankar Velliangiri static int sht4x_temperature1_read(struct sht4x_data *data, long *val)
149*505c2549SNavin Sankar Velliangiri {
150*505c2549SNavin Sankar Velliangiri 	int ret;
151*505c2549SNavin Sankar Velliangiri 
152*505c2549SNavin Sankar Velliangiri 	ret = sht4x_read_values(data);
153*505c2549SNavin Sankar Velliangiri 	if (ret < 0)
154*505c2549SNavin Sankar Velliangiri 		return ret;
155*505c2549SNavin Sankar Velliangiri 
156*505c2549SNavin Sankar Velliangiri 	*val = data->temperature;
157*505c2549SNavin Sankar Velliangiri 
158*505c2549SNavin Sankar Velliangiri 	return 0;
159*505c2549SNavin Sankar Velliangiri }
160*505c2549SNavin Sankar Velliangiri 
161*505c2549SNavin Sankar Velliangiri /**
162*505c2549SNavin Sankar Velliangiri  * sht4x_humidity1_read() - read a relative humidity in millipercent
163*505c2549SNavin Sankar Velliangiri  */
164*505c2549SNavin Sankar Velliangiri static int sht4x_humidity1_read(struct sht4x_data *data, long *val)
165*505c2549SNavin Sankar Velliangiri {
166*505c2549SNavin Sankar Velliangiri 	int ret;
167*505c2549SNavin Sankar Velliangiri 
168*505c2549SNavin Sankar Velliangiri 	ret = sht4x_read_values(data);
169*505c2549SNavin Sankar Velliangiri 	if (ret < 0)
170*505c2549SNavin Sankar Velliangiri 		return ret;
171*505c2549SNavin Sankar Velliangiri 
172*505c2549SNavin Sankar Velliangiri 	*val = data->humidity;
173*505c2549SNavin Sankar Velliangiri 
174*505c2549SNavin Sankar Velliangiri 	return 0;
175*505c2549SNavin Sankar Velliangiri }
176*505c2549SNavin Sankar Velliangiri 
177*505c2549SNavin Sankar Velliangiri static umode_t sht4x_hwmon_visible(const void *data,
178*505c2549SNavin Sankar Velliangiri 				   enum hwmon_sensor_types type,
179*505c2549SNavin Sankar Velliangiri 				   u32 attr, int channel)
180*505c2549SNavin Sankar Velliangiri {
181*505c2549SNavin Sankar Velliangiri 	switch (type) {
182*505c2549SNavin Sankar Velliangiri 	case hwmon_temp:
183*505c2549SNavin Sankar Velliangiri 	case hwmon_humidity:
184*505c2549SNavin Sankar Velliangiri 		return 0444;
185*505c2549SNavin Sankar Velliangiri 	case hwmon_chip:
186*505c2549SNavin Sankar Velliangiri 		return 0644;
187*505c2549SNavin Sankar Velliangiri 	default:
188*505c2549SNavin Sankar Velliangiri 		return 0;
189*505c2549SNavin Sankar Velliangiri 	}
190*505c2549SNavin Sankar Velliangiri }
191*505c2549SNavin Sankar Velliangiri 
192*505c2549SNavin Sankar Velliangiri static int sht4x_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
193*505c2549SNavin Sankar Velliangiri 			    u32 attr, int channel, long *val)
194*505c2549SNavin Sankar Velliangiri {
195*505c2549SNavin Sankar Velliangiri 	struct sht4x_data *data = dev_get_drvdata(dev);
196*505c2549SNavin Sankar Velliangiri 
197*505c2549SNavin Sankar Velliangiri 	switch (type) {
198*505c2549SNavin Sankar Velliangiri 	case hwmon_temp:
199*505c2549SNavin Sankar Velliangiri 		return sht4x_temperature1_read(data, val);
200*505c2549SNavin Sankar Velliangiri 	case hwmon_humidity:
201*505c2549SNavin Sankar Velliangiri 		return sht4x_humidity1_read(data, val);
202*505c2549SNavin Sankar Velliangiri 	case hwmon_chip:
203*505c2549SNavin Sankar Velliangiri 		return sht4x_interval_read(data, val);
204*505c2549SNavin Sankar Velliangiri 	default:
205*505c2549SNavin Sankar Velliangiri 		return -EOPNOTSUPP;
206*505c2549SNavin Sankar Velliangiri 	}
207*505c2549SNavin Sankar Velliangiri }
208*505c2549SNavin Sankar Velliangiri 
209*505c2549SNavin Sankar Velliangiri static int sht4x_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
210*505c2549SNavin Sankar Velliangiri 			     u32 attr, int channel, long val)
211*505c2549SNavin Sankar Velliangiri {
212*505c2549SNavin Sankar Velliangiri 	struct sht4x_data *data = dev_get_drvdata(dev);
213*505c2549SNavin Sankar Velliangiri 
214*505c2549SNavin Sankar Velliangiri 	switch (type) {
215*505c2549SNavin Sankar Velliangiri 	case hwmon_chip:
216*505c2549SNavin Sankar Velliangiri 		return sht4x_interval_write(data, val);
217*505c2549SNavin Sankar Velliangiri 	default:
218*505c2549SNavin Sankar Velliangiri 		return -EOPNOTSUPP;
219*505c2549SNavin Sankar Velliangiri 	}
220*505c2549SNavin Sankar Velliangiri }
221*505c2549SNavin Sankar Velliangiri 
222*505c2549SNavin Sankar Velliangiri static const struct hwmon_channel_info *sht4x_info[] = {
223*505c2549SNavin Sankar Velliangiri 	HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL),
224*505c2549SNavin Sankar Velliangiri 	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
225*505c2549SNavin Sankar Velliangiri 	HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT),
226*505c2549SNavin Sankar Velliangiri 	NULL,
227*505c2549SNavin Sankar Velliangiri };
228*505c2549SNavin Sankar Velliangiri 
229*505c2549SNavin Sankar Velliangiri static const struct hwmon_ops sht4x_hwmon_ops = {
230*505c2549SNavin Sankar Velliangiri 	.is_visible = sht4x_hwmon_visible,
231*505c2549SNavin Sankar Velliangiri 	.read = sht4x_hwmon_read,
232*505c2549SNavin Sankar Velliangiri 	.write = sht4x_hwmon_write,
233*505c2549SNavin Sankar Velliangiri };
234*505c2549SNavin Sankar Velliangiri 
235*505c2549SNavin Sankar Velliangiri static const struct hwmon_chip_info sht4x_chip_info = {
236*505c2549SNavin Sankar Velliangiri 	.ops = &sht4x_hwmon_ops,
237*505c2549SNavin Sankar Velliangiri 	.info = sht4x_info,
238*505c2549SNavin Sankar Velliangiri };
239*505c2549SNavin Sankar Velliangiri 
240*505c2549SNavin Sankar Velliangiri static int sht4x_probe(struct i2c_client *client,
241*505c2549SNavin Sankar Velliangiri 		       const struct i2c_device_id *sht4x_id)
242*505c2549SNavin Sankar Velliangiri {
243*505c2549SNavin Sankar Velliangiri 	struct device *device = &client->dev;
244*505c2549SNavin Sankar Velliangiri 	struct device *hwmon_dev;
245*505c2549SNavin Sankar Velliangiri 	struct sht4x_data *data;
246*505c2549SNavin Sankar Velliangiri 	u8 cmd[] = {SHT4X_CMD_RESET};
247*505c2549SNavin Sankar Velliangiri 	int ret;
248*505c2549SNavin Sankar Velliangiri 
249*505c2549SNavin Sankar Velliangiri 	/*
250*505c2549SNavin Sankar Velliangiri 	 * we require full i2c support since the sht4x uses multi-byte read and
251*505c2549SNavin Sankar Velliangiri 	 * writes as well as multi-byte commands which are not supported by
252*505c2549SNavin Sankar Velliangiri 	 * the smbus protocol
253*505c2549SNavin Sankar Velliangiri 	 */
254*505c2549SNavin Sankar Velliangiri 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
255*505c2549SNavin Sankar Velliangiri 		return -EOPNOTSUPP;
256*505c2549SNavin Sankar Velliangiri 
257*505c2549SNavin Sankar Velliangiri 	data = devm_kzalloc(device, sizeof(*data), GFP_KERNEL);
258*505c2549SNavin Sankar Velliangiri 	if (!data)
259*505c2549SNavin Sankar Velliangiri 		return -ENOMEM;
260*505c2549SNavin Sankar Velliangiri 
261*505c2549SNavin Sankar Velliangiri 	data->update_interval = SHT4X_MIN_POLL_INTERVAL;
262*505c2549SNavin Sankar Velliangiri 	data->client = client;
263*505c2549SNavin Sankar Velliangiri 
264*505c2549SNavin Sankar Velliangiri 	mutex_init(&data->lock);
265*505c2549SNavin Sankar Velliangiri 
266*505c2549SNavin Sankar Velliangiri 	crc8_populate_msb(sht4x_crc8_table, SHT4X_CRC8_POLYNOMIAL);
267*505c2549SNavin Sankar Velliangiri 
268*505c2549SNavin Sankar Velliangiri 	ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN);
269*505c2549SNavin Sankar Velliangiri 	if (ret < 0)
270*505c2549SNavin Sankar Velliangiri 		return ret;
271*505c2549SNavin Sankar Velliangiri 	if (ret != SHT4X_CMD_LEN)
272*505c2549SNavin Sankar Velliangiri 		return -EIO;
273*505c2549SNavin Sankar Velliangiri 
274*505c2549SNavin Sankar Velliangiri 	hwmon_dev = devm_hwmon_device_register_with_info(device,
275*505c2549SNavin Sankar Velliangiri 							 client->name,
276*505c2549SNavin Sankar Velliangiri 							 data,
277*505c2549SNavin Sankar Velliangiri 							 &sht4x_chip_info,
278*505c2549SNavin Sankar Velliangiri 							 NULL);
279*505c2549SNavin Sankar Velliangiri 
280*505c2549SNavin Sankar Velliangiri 	return PTR_ERR_OR_ZERO(hwmon_dev);
281*505c2549SNavin Sankar Velliangiri }
282*505c2549SNavin Sankar Velliangiri 
283*505c2549SNavin Sankar Velliangiri static const struct i2c_device_id sht4x_id[] = {
284*505c2549SNavin Sankar Velliangiri 	{ "sht4x", 0 },
285*505c2549SNavin Sankar Velliangiri 	{ },
286*505c2549SNavin Sankar Velliangiri };
287*505c2549SNavin Sankar Velliangiri MODULE_DEVICE_TABLE(i2c, sht4x_id);
288*505c2549SNavin Sankar Velliangiri 
289*505c2549SNavin Sankar Velliangiri static struct i2c_driver sht4x_driver = {
290*505c2549SNavin Sankar Velliangiri 	.driver = {
291*505c2549SNavin Sankar Velliangiri 		.name = "sht4x",
292*505c2549SNavin Sankar Velliangiri 	},
293*505c2549SNavin Sankar Velliangiri 	.probe		= sht4x_probe,
294*505c2549SNavin Sankar Velliangiri 	.id_table	= sht4x_id,
295*505c2549SNavin Sankar Velliangiri };
296*505c2549SNavin Sankar Velliangiri 
297*505c2549SNavin Sankar Velliangiri module_i2c_driver(sht4x_driver);
298*505c2549SNavin Sankar Velliangiri 
299*505c2549SNavin Sankar Velliangiri MODULE_AUTHOR("Navin Sankar Velliangiri <navin@linumiz.com>");
300*505c2549SNavin Sankar Velliangiri MODULE_DESCRIPTION("Sensirion SHT4x humidity and temperature sensor driver");
301*505c2549SNavin Sankar Velliangiri MODULE_LICENSE("GPL v2");
302