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