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