1505c2549SNavin Sankar Velliangiri // SPDX-License-Identifier: GPL-2.0-only 2505c2549SNavin Sankar Velliangiri 3505c2549SNavin Sankar Velliangiri /* 4505c2549SNavin Sankar Velliangiri * Copyright (c) Linumiz 2021 5505c2549SNavin Sankar Velliangiri * 6505c2549SNavin Sankar Velliangiri * sht4x.c - Linux hwmon driver for SHT4x Temperature and Humidity sensor 7505c2549SNavin Sankar Velliangiri * 8505c2549SNavin Sankar Velliangiri * Author: Navin Sankar Velliangiri <navin@linumiz.com> 9505c2549SNavin Sankar Velliangiri */ 10505c2549SNavin Sankar Velliangiri 11505c2549SNavin Sankar Velliangiri #include <linux/crc8.h> 12505c2549SNavin Sankar Velliangiri #include <linux/delay.h> 13505c2549SNavin Sankar Velliangiri #include <linux/hwmon.h> 14505c2549SNavin Sankar Velliangiri #include <linux/i2c.h> 15505c2549SNavin Sankar Velliangiri #include <linux/jiffies.h> 16505c2549SNavin Sankar Velliangiri #include <linux/module.h> 17505c2549SNavin Sankar Velliangiri 18505c2549SNavin Sankar Velliangiri /* 19505c2549SNavin Sankar Velliangiri * Poll intervals (in milliseconds) 20505c2549SNavin Sankar Velliangiri */ 21505c2549SNavin Sankar Velliangiri #define SHT4X_MIN_POLL_INTERVAL 2000 22505c2549SNavin Sankar Velliangiri 23505c2549SNavin Sankar Velliangiri /* 24505c2549SNavin Sankar Velliangiri * I2C command delays (in microseconds) 25505c2549SNavin Sankar Velliangiri */ 260e4190d7SDavid Mosberger-Tang #define SHT4X_MEAS_DELAY_HPM 8200 /* see t_MEAS,h in datasheet */ 27505c2549SNavin Sankar Velliangiri #define SHT4X_DELAY_EXTRA 10000 28505c2549SNavin Sankar Velliangiri 29505c2549SNavin Sankar Velliangiri /* 30505c2549SNavin Sankar Velliangiri * Command Bytes 31505c2549SNavin Sankar Velliangiri */ 32505c2549SNavin Sankar Velliangiri #define SHT4X_CMD_MEASURE_HPM 0b11111101 33505c2549SNavin Sankar Velliangiri #define SHT4X_CMD_RESET 0b10010100 34505c2549SNavin Sankar Velliangiri 35505c2549SNavin Sankar Velliangiri #define SHT4X_CMD_LEN 1 36505c2549SNavin Sankar Velliangiri #define SHT4X_CRC8_LEN 1 37505c2549SNavin Sankar Velliangiri #define SHT4X_WORD_LEN 2 38505c2549SNavin Sankar Velliangiri #define SHT4X_RESPONSE_LENGTH 6 39505c2549SNavin Sankar Velliangiri #define SHT4X_CRC8_POLYNOMIAL 0x31 40505c2549SNavin Sankar Velliangiri #define SHT4X_CRC8_INIT 0xff 41505c2549SNavin Sankar Velliangiri #define SHT4X_MIN_TEMPERATURE -45000 42505c2549SNavin Sankar Velliangiri #define SHT4X_MAX_TEMPERATURE 125000 43505c2549SNavin Sankar Velliangiri #define SHT4X_MIN_HUMIDITY 0 44505c2549SNavin Sankar Velliangiri #define SHT4X_MAX_HUMIDITY 100000 45505c2549SNavin Sankar Velliangiri 46505c2549SNavin Sankar Velliangiri DECLARE_CRC8_TABLE(sht4x_crc8_table); 47505c2549SNavin Sankar Velliangiri 48505c2549SNavin Sankar Velliangiri /** 49505c2549SNavin Sankar Velliangiri * struct sht4x_data - All the data required to operate an SHT4X chip 50505c2549SNavin Sankar Velliangiri * @client: the i2c client associated with the SHT4X 51505c2549SNavin Sankar Velliangiri * @lock: a mutex that is used to prevent parallel access to the i2c client 52*10bd80e0SYang Li * @valid: validity of fields below 53505c2549SNavin Sankar Velliangiri * @update_interval: the minimum poll interval 54505c2549SNavin Sankar Velliangiri * @last_updated: the previous time that the SHT4X was polled 55505c2549SNavin Sankar Velliangiri * @temperature: the latest temperature value received from the SHT4X 56505c2549SNavin Sankar Velliangiri * @humidity: the latest humidity value received from the SHT4X 57505c2549SNavin Sankar Velliangiri */ 58505c2549SNavin Sankar Velliangiri struct sht4x_data { 59505c2549SNavin Sankar Velliangiri struct i2c_client *client; 60505c2549SNavin Sankar Velliangiri struct mutex lock; /* atomic read data updates */ 61505c2549SNavin Sankar Velliangiri bool valid; /* validity of fields below */ 62505c2549SNavin Sankar Velliangiri long update_interval; /* in milli-seconds */ 63505c2549SNavin Sankar Velliangiri long last_updated; /* in jiffies */ 64505c2549SNavin Sankar Velliangiri s32 temperature; 65505c2549SNavin Sankar Velliangiri s32 humidity; 66505c2549SNavin Sankar Velliangiri }; 67505c2549SNavin Sankar Velliangiri 68505c2549SNavin Sankar Velliangiri /** 69505c2549SNavin Sankar Velliangiri * sht4x_read_values() - read and parse the raw data from the SHT4X 70*10bd80e0SYang Li * @data: the struct sht4x_data to use for the lock 7107c6621aSJoe Perches * Return: 0 if successful, -ERRNO if not 72505c2549SNavin Sankar Velliangiri */ 73505c2549SNavin Sankar Velliangiri static int sht4x_read_values(struct sht4x_data *data) 74505c2549SNavin Sankar Velliangiri { 75505c2549SNavin Sankar Velliangiri int ret = 0; 76505c2549SNavin Sankar Velliangiri u16 t_ticks, rh_ticks; 77505c2549SNavin Sankar Velliangiri unsigned long next_update; 78505c2549SNavin Sankar Velliangiri struct i2c_client *client = data->client; 7907c6621aSJoe Perches u8 crc; 8007c6621aSJoe Perches u8 cmd[SHT4X_CMD_LEN] = {SHT4X_CMD_MEASURE_HPM}; 8107c6621aSJoe Perches u8 raw_data[SHT4X_RESPONSE_LENGTH]; 82505c2549SNavin Sankar Velliangiri 83505c2549SNavin Sankar Velliangiri mutex_lock(&data->lock); 84505c2549SNavin Sankar Velliangiri next_update = data->last_updated + 85505c2549SNavin Sankar Velliangiri msecs_to_jiffies(data->update_interval); 8607c6621aSJoe Perches 8707c6621aSJoe Perches if (data->valid && time_before_eq(jiffies, next_update)) 8807c6621aSJoe Perches goto unlock; 8907c6621aSJoe Perches 90505c2549SNavin Sankar Velliangiri ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN); 91505c2549SNavin Sankar Velliangiri if (ret < 0) 92505c2549SNavin Sankar Velliangiri goto unlock; 93505c2549SNavin Sankar Velliangiri 940e4190d7SDavid Mosberger-Tang usleep_range(SHT4X_MEAS_DELAY_HPM, SHT4X_MEAS_DELAY_HPM + SHT4X_DELAY_EXTRA); 95505c2549SNavin Sankar Velliangiri 96505c2549SNavin Sankar Velliangiri ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH); 97505c2549SNavin Sankar Velliangiri if (ret != SHT4X_RESPONSE_LENGTH) { 98505c2549SNavin Sankar Velliangiri if (ret >= 0) 99505c2549SNavin Sankar Velliangiri ret = -ENODATA; 100505c2549SNavin Sankar Velliangiri goto unlock; 101505c2549SNavin Sankar Velliangiri } 102505c2549SNavin Sankar Velliangiri 103505c2549SNavin Sankar Velliangiri t_ticks = raw_data[0] << 8 | raw_data[1]; 104505c2549SNavin Sankar Velliangiri rh_ticks = raw_data[3] << 8 | raw_data[4]; 105505c2549SNavin Sankar Velliangiri 106505c2549SNavin Sankar Velliangiri crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE); 107505c2549SNavin Sankar Velliangiri if (crc != raw_data[2]) { 108505c2549SNavin Sankar Velliangiri dev_err(&client->dev, "data integrity check failed\n"); 109505c2549SNavin Sankar Velliangiri ret = -EIO; 110505c2549SNavin Sankar Velliangiri goto unlock; 111505c2549SNavin Sankar Velliangiri } 112505c2549SNavin Sankar Velliangiri 113505c2549SNavin Sankar Velliangiri crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE); 114505c2549SNavin Sankar Velliangiri if (crc != raw_data[5]) { 115505c2549SNavin Sankar Velliangiri dev_err(&client->dev, "data integrity check failed\n"); 116505c2549SNavin Sankar Velliangiri ret = -EIO; 117505c2549SNavin Sankar Velliangiri goto unlock; 118505c2549SNavin Sankar Velliangiri } 119505c2549SNavin Sankar Velliangiri 120505c2549SNavin Sankar Velliangiri data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000; 121505c2549SNavin Sankar Velliangiri data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000; 122505c2549SNavin Sankar Velliangiri data->last_updated = jiffies; 123505c2549SNavin Sankar Velliangiri data->valid = true; 12407c6621aSJoe Perches ret = 0; 125505c2549SNavin Sankar Velliangiri 126505c2549SNavin Sankar Velliangiri unlock: 127505c2549SNavin Sankar Velliangiri mutex_unlock(&data->lock); 128505c2549SNavin Sankar Velliangiri return ret; 129505c2549SNavin Sankar Velliangiri } 130505c2549SNavin Sankar Velliangiri 131505c2549SNavin Sankar Velliangiri static ssize_t sht4x_interval_write(struct sht4x_data *data, long val) 132505c2549SNavin Sankar Velliangiri { 133f9c0cf8fSJason A. Donenfeld data->update_interval = clamp_val(val, SHT4X_MIN_POLL_INTERVAL, INT_MAX); 134505c2549SNavin Sankar Velliangiri 135505c2549SNavin Sankar Velliangiri return 0; 136505c2549SNavin Sankar Velliangiri } 137505c2549SNavin Sankar Velliangiri 13807c6621aSJoe Perches /* sht4x_interval_read() - read the minimum poll interval in milliseconds */ 139505c2549SNavin Sankar Velliangiri static size_t sht4x_interval_read(struct sht4x_data *data, long *val) 140505c2549SNavin Sankar Velliangiri { 141505c2549SNavin Sankar Velliangiri *val = data->update_interval; 142505c2549SNavin Sankar Velliangiri return 0; 143505c2549SNavin Sankar Velliangiri } 144505c2549SNavin Sankar Velliangiri 14507c6621aSJoe Perches /* sht4x_temperature1_read() - read the temperature in millidegrees */ 146505c2549SNavin Sankar Velliangiri static int sht4x_temperature1_read(struct sht4x_data *data, long *val) 147505c2549SNavin Sankar Velliangiri { 148505c2549SNavin Sankar Velliangiri int ret; 149505c2549SNavin Sankar Velliangiri 150505c2549SNavin Sankar Velliangiri ret = sht4x_read_values(data); 151505c2549SNavin Sankar Velliangiri if (ret < 0) 152505c2549SNavin Sankar Velliangiri return ret; 153505c2549SNavin Sankar Velliangiri 154505c2549SNavin Sankar Velliangiri *val = data->temperature; 155505c2549SNavin Sankar Velliangiri 156505c2549SNavin Sankar Velliangiri return 0; 157505c2549SNavin Sankar Velliangiri } 158505c2549SNavin Sankar Velliangiri 15907c6621aSJoe Perches /* sht4x_humidity1_read() - read a relative humidity in millipercent */ 160505c2549SNavin Sankar Velliangiri static int sht4x_humidity1_read(struct sht4x_data *data, long *val) 161505c2549SNavin Sankar Velliangiri { 162505c2549SNavin Sankar Velliangiri int ret; 163505c2549SNavin Sankar Velliangiri 164505c2549SNavin Sankar Velliangiri ret = sht4x_read_values(data); 165505c2549SNavin Sankar Velliangiri if (ret < 0) 166505c2549SNavin Sankar Velliangiri return ret; 167505c2549SNavin Sankar Velliangiri 168505c2549SNavin Sankar Velliangiri *val = data->humidity; 169505c2549SNavin Sankar Velliangiri 170505c2549SNavin Sankar Velliangiri return 0; 171505c2549SNavin Sankar Velliangiri } 172505c2549SNavin Sankar Velliangiri 173505c2549SNavin Sankar Velliangiri static umode_t sht4x_hwmon_visible(const void *data, 174505c2549SNavin Sankar Velliangiri enum hwmon_sensor_types type, 175505c2549SNavin Sankar Velliangiri u32 attr, int channel) 176505c2549SNavin Sankar Velliangiri { 177505c2549SNavin Sankar Velliangiri switch (type) { 178505c2549SNavin Sankar Velliangiri case hwmon_temp: 179505c2549SNavin Sankar Velliangiri case hwmon_humidity: 180505c2549SNavin Sankar Velliangiri return 0444; 181505c2549SNavin Sankar Velliangiri case hwmon_chip: 182505c2549SNavin Sankar Velliangiri return 0644; 183505c2549SNavin Sankar Velliangiri default: 184505c2549SNavin Sankar Velliangiri return 0; 185505c2549SNavin Sankar Velliangiri } 186505c2549SNavin Sankar Velliangiri } 187505c2549SNavin Sankar Velliangiri 188505c2549SNavin Sankar Velliangiri static int sht4x_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 189505c2549SNavin Sankar Velliangiri u32 attr, int channel, long *val) 190505c2549SNavin Sankar Velliangiri { 191505c2549SNavin Sankar Velliangiri struct sht4x_data *data = dev_get_drvdata(dev); 192505c2549SNavin Sankar Velliangiri 193505c2549SNavin Sankar Velliangiri switch (type) { 194505c2549SNavin Sankar Velliangiri case hwmon_temp: 195505c2549SNavin Sankar Velliangiri return sht4x_temperature1_read(data, val); 196505c2549SNavin Sankar Velliangiri case hwmon_humidity: 197505c2549SNavin Sankar Velliangiri return sht4x_humidity1_read(data, val); 198505c2549SNavin Sankar Velliangiri case hwmon_chip: 199505c2549SNavin Sankar Velliangiri return sht4x_interval_read(data, val); 200505c2549SNavin Sankar Velliangiri default: 201505c2549SNavin Sankar Velliangiri return -EOPNOTSUPP; 202505c2549SNavin Sankar Velliangiri } 203505c2549SNavin Sankar Velliangiri } 204505c2549SNavin Sankar Velliangiri 205505c2549SNavin Sankar Velliangiri static int sht4x_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 206505c2549SNavin Sankar Velliangiri u32 attr, int channel, long val) 207505c2549SNavin Sankar Velliangiri { 208505c2549SNavin Sankar Velliangiri struct sht4x_data *data = dev_get_drvdata(dev); 209505c2549SNavin Sankar Velliangiri 210505c2549SNavin Sankar Velliangiri switch (type) { 211505c2549SNavin Sankar Velliangiri case hwmon_chip: 212505c2549SNavin Sankar Velliangiri return sht4x_interval_write(data, val); 213505c2549SNavin Sankar Velliangiri default: 214505c2549SNavin Sankar Velliangiri return -EOPNOTSUPP; 215505c2549SNavin Sankar Velliangiri } 216505c2549SNavin Sankar Velliangiri } 217505c2549SNavin Sankar Velliangiri 21811c1dff5SKrzysztof Kozlowski static const struct hwmon_channel_info * const sht4x_info[] = { 219505c2549SNavin Sankar Velliangiri HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), 220505c2549SNavin Sankar Velliangiri HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), 221505c2549SNavin Sankar Velliangiri HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT), 222505c2549SNavin Sankar Velliangiri NULL, 223505c2549SNavin Sankar Velliangiri }; 224505c2549SNavin Sankar Velliangiri 225505c2549SNavin Sankar Velliangiri static const struct hwmon_ops sht4x_hwmon_ops = { 226505c2549SNavin Sankar Velliangiri .is_visible = sht4x_hwmon_visible, 227505c2549SNavin Sankar Velliangiri .read = sht4x_hwmon_read, 228505c2549SNavin Sankar Velliangiri .write = sht4x_hwmon_write, 229505c2549SNavin Sankar Velliangiri }; 230505c2549SNavin Sankar Velliangiri 231505c2549SNavin Sankar Velliangiri static const struct hwmon_chip_info sht4x_chip_info = { 232505c2549SNavin Sankar Velliangiri .ops = &sht4x_hwmon_ops, 233505c2549SNavin Sankar Velliangiri .info = sht4x_info, 234505c2549SNavin Sankar Velliangiri }; 235505c2549SNavin Sankar Velliangiri 236deeab9eaSStephen Kitt static int sht4x_probe(struct i2c_client *client) 237505c2549SNavin Sankar Velliangiri { 238505c2549SNavin Sankar Velliangiri struct device *device = &client->dev; 239505c2549SNavin Sankar Velliangiri struct device *hwmon_dev; 240505c2549SNavin Sankar Velliangiri struct sht4x_data *data; 241505c2549SNavin Sankar Velliangiri u8 cmd[] = {SHT4X_CMD_RESET}; 242505c2549SNavin Sankar Velliangiri int ret; 243505c2549SNavin Sankar Velliangiri 244505c2549SNavin Sankar Velliangiri /* 245505c2549SNavin Sankar Velliangiri * we require full i2c support since the sht4x uses multi-byte read and 246505c2549SNavin Sankar Velliangiri * writes as well as multi-byte commands which are not supported by 247505c2549SNavin Sankar Velliangiri * the smbus protocol 248505c2549SNavin Sankar Velliangiri */ 249505c2549SNavin Sankar Velliangiri if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) 250505c2549SNavin Sankar Velliangiri return -EOPNOTSUPP; 251505c2549SNavin Sankar Velliangiri 252505c2549SNavin Sankar Velliangiri data = devm_kzalloc(device, sizeof(*data), GFP_KERNEL); 253505c2549SNavin Sankar Velliangiri if (!data) 254505c2549SNavin Sankar Velliangiri return -ENOMEM; 255505c2549SNavin Sankar Velliangiri 256505c2549SNavin Sankar Velliangiri data->update_interval = SHT4X_MIN_POLL_INTERVAL; 257505c2549SNavin Sankar Velliangiri data->client = client; 258505c2549SNavin Sankar Velliangiri 259505c2549SNavin Sankar Velliangiri mutex_init(&data->lock); 260505c2549SNavin Sankar Velliangiri 261505c2549SNavin Sankar Velliangiri crc8_populate_msb(sht4x_crc8_table, SHT4X_CRC8_POLYNOMIAL); 262505c2549SNavin Sankar Velliangiri 263505c2549SNavin Sankar Velliangiri ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN); 264505c2549SNavin Sankar Velliangiri if (ret < 0) 265505c2549SNavin Sankar Velliangiri return ret; 266505c2549SNavin Sankar Velliangiri if (ret != SHT4X_CMD_LEN) 267505c2549SNavin Sankar Velliangiri return -EIO; 268505c2549SNavin Sankar Velliangiri 269505c2549SNavin Sankar Velliangiri hwmon_dev = devm_hwmon_device_register_with_info(device, 270505c2549SNavin Sankar Velliangiri client->name, 271505c2549SNavin Sankar Velliangiri data, 272505c2549SNavin Sankar Velliangiri &sht4x_chip_info, 273505c2549SNavin Sankar Velliangiri NULL); 274505c2549SNavin Sankar Velliangiri 275505c2549SNavin Sankar Velliangiri return PTR_ERR_OR_ZERO(hwmon_dev); 276505c2549SNavin Sankar Velliangiri } 277505c2549SNavin Sankar Velliangiri 278505c2549SNavin Sankar Velliangiri static const struct i2c_device_id sht4x_id[] = { 279505c2549SNavin Sankar Velliangiri { "sht4x", 0 }, 280505c2549SNavin Sankar Velliangiri { }, 281505c2549SNavin Sankar Velliangiri }; 282505c2549SNavin Sankar Velliangiri MODULE_DEVICE_TABLE(i2c, sht4x_id); 283505c2549SNavin Sankar Velliangiri 28462cfc057SDavid Mosberger-Tang static const struct of_device_id sht4x_of_match[] = { 28562cfc057SDavid Mosberger-Tang { .compatible = "sensirion,sht4x" }, 28662cfc057SDavid Mosberger-Tang { } 28762cfc057SDavid Mosberger-Tang }; 28862cfc057SDavid Mosberger-Tang MODULE_DEVICE_TABLE(of, sht4x_of_match); 28962cfc057SDavid Mosberger-Tang 290505c2549SNavin Sankar Velliangiri static struct i2c_driver sht4x_driver = { 291505c2549SNavin Sankar Velliangiri .driver = { 292505c2549SNavin Sankar Velliangiri .name = "sht4x", 29362cfc057SDavid Mosberger-Tang .of_match_table = sht4x_of_match, 294505c2549SNavin Sankar Velliangiri }, 2951975d167SUwe Kleine-König .probe = sht4x_probe, 296505c2549SNavin Sankar Velliangiri .id_table = sht4x_id, 297505c2549SNavin Sankar Velliangiri }; 298505c2549SNavin Sankar Velliangiri 299505c2549SNavin Sankar Velliangiri module_i2c_driver(sht4x_driver); 300505c2549SNavin Sankar Velliangiri 301505c2549SNavin Sankar Velliangiri MODULE_AUTHOR("Navin Sankar Velliangiri <navin@linumiz.com>"); 302505c2549SNavin Sankar Velliangiri MODULE_DESCRIPTION("Sensirion SHT4x humidity and temperature sensor driver"); 303505c2549SNavin Sankar Velliangiri MODULE_LICENSE("GPL v2"); 304