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 */ 26*0e4190d7SDavid 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 52505c2549SNavin Sankar Velliangiri * @update_interval: the minimum poll interval 53505c2549SNavin Sankar Velliangiri * @last_updated: the previous time that the SHT4X was polled 54505c2549SNavin Sankar Velliangiri * @temperature: the latest temperature value received from the SHT4X 55505c2549SNavin Sankar Velliangiri * @humidity: the latest humidity value received from the SHT4X 56505c2549SNavin Sankar Velliangiri */ 57505c2549SNavin Sankar Velliangiri struct sht4x_data { 58505c2549SNavin Sankar Velliangiri struct i2c_client *client; 59505c2549SNavin Sankar Velliangiri struct mutex lock; /* atomic read data updates */ 60505c2549SNavin Sankar Velliangiri bool valid; /* validity of fields below */ 61505c2549SNavin Sankar Velliangiri long update_interval; /* in milli-seconds */ 62505c2549SNavin Sankar Velliangiri long last_updated; /* in jiffies */ 63505c2549SNavin Sankar Velliangiri s32 temperature; 64505c2549SNavin Sankar Velliangiri s32 humidity; 65505c2549SNavin Sankar Velliangiri }; 66505c2549SNavin Sankar Velliangiri 67505c2549SNavin Sankar Velliangiri /** 68505c2549SNavin Sankar Velliangiri * sht4x_read_values() - read and parse the raw data from the SHT4X 69505c2549SNavin Sankar Velliangiri * @sht4x_data: the struct sht4x_data to use for the lock 7007c6621aSJoe Perches * Return: 0 if successful, -ERRNO if not 71505c2549SNavin Sankar Velliangiri */ 72505c2549SNavin Sankar Velliangiri static int sht4x_read_values(struct sht4x_data *data) 73505c2549SNavin Sankar Velliangiri { 74505c2549SNavin Sankar Velliangiri int ret = 0; 75505c2549SNavin Sankar Velliangiri u16 t_ticks, rh_ticks; 76505c2549SNavin Sankar Velliangiri unsigned long next_update; 77505c2549SNavin Sankar Velliangiri struct i2c_client *client = data->client; 7807c6621aSJoe Perches u8 crc; 7907c6621aSJoe Perches u8 cmd[SHT4X_CMD_LEN] = {SHT4X_CMD_MEASURE_HPM}; 8007c6621aSJoe Perches u8 raw_data[SHT4X_RESPONSE_LENGTH]; 81505c2549SNavin Sankar Velliangiri 82505c2549SNavin Sankar Velliangiri mutex_lock(&data->lock); 83505c2549SNavin Sankar Velliangiri next_update = data->last_updated + 84505c2549SNavin Sankar Velliangiri msecs_to_jiffies(data->update_interval); 8507c6621aSJoe Perches 8607c6621aSJoe Perches if (data->valid && time_before_eq(jiffies, next_update)) 8707c6621aSJoe Perches goto unlock; 8807c6621aSJoe Perches 89505c2549SNavin Sankar Velliangiri ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN); 90505c2549SNavin Sankar Velliangiri if (ret < 0) 91505c2549SNavin Sankar Velliangiri goto unlock; 92505c2549SNavin Sankar Velliangiri 93*0e4190d7SDavid Mosberger-Tang usleep_range(SHT4X_MEAS_DELAY_HPM, SHT4X_MEAS_DELAY_HPM + SHT4X_DELAY_EXTRA); 94505c2549SNavin Sankar Velliangiri 95505c2549SNavin Sankar Velliangiri ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH); 96505c2549SNavin Sankar Velliangiri if (ret != SHT4X_RESPONSE_LENGTH) { 97505c2549SNavin Sankar Velliangiri if (ret >= 0) 98505c2549SNavin Sankar Velliangiri ret = -ENODATA; 99505c2549SNavin Sankar Velliangiri goto unlock; 100505c2549SNavin Sankar Velliangiri } 101505c2549SNavin Sankar Velliangiri 102505c2549SNavin Sankar Velliangiri t_ticks = raw_data[0] << 8 | raw_data[1]; 103505c2549SNavin Sankar Velliangiri rh_ticks = raw_data[3] << 8 | raw_data[4]; 104505c2549SNavin Sankar Velliangiri 105505c2549SNavin Sankar Velliangiri crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE); 106505c2549SNavin Sankar Velliangiri if (crc != raw_data[2]) { 107505c2549SNavin Sankar Velliangiri dev_err(&client->dev, "data integrity check failed\n"); 108505c2549SNavin Sankar Velliangiri ret = -EIO; 109505c2549SNavin Sankar Velliangiri goto unlock; 110505c2549SNavin Sankar Velliangiri } 111505c2549SNavin Sankar Velliangiri 112505c2549SNavin Sankar Velliangiri crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE); 113505c2549SNavin Sankar Velliangiri if (crc != raw_data[5]) { 114505c2549SNavin Sankar Velliangiri dev_err(&client->dev, "data integrity check failed\n"); 115505c2549SNavin Sankar Velliangiri ret = -EIO; 116505c2549SNavin Sankar Velliangiri goto unlock; 117505c2549SNavin Sankar Velliangiri } 118505c2549SNavin Sankar Velliangiri 119505c2549SNavin Sankar Velliangiri data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000; 120505c2549SNavin Sankar Velliangiri data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000; 121505c2549SNavin Sankar Velliangiri data->last_updated = jiffies; 122505c2549SNavin Sankar Velliangiri data->valid = true; 12307c6621aSJoe Perches ret = 0; 124505c2549SNavin Sankar Velliangiri 125505c2549SNavin Sankar Velliangiri unlock: 126505c2549SNavin Sankar Velliangiri mutex_unlock(&data->lock); 127505c2549SNavin Sankar Velliangiri return ret; 128505c2549SNavin Sankar Velliangiri } 129505c2549SNavin Sankar Velliangiri 130505c2549SNavin Sankar Velliangiri static ssize_t sht4x_interval_write(struct sht4x_data *data, long val) 131505c2549SNavin Sankar Velliangiri { 132505c2549SNavin Sankar Velliangiri data->update_interval = clamp_val(val, SHT4X_MIN_POLL_INTERVAL, UINT_MAX); 133505c2549SNavin Sankar Velliangiri 134505c2549SNavin Sankar Velliangiri return 0; 135505c2549SNavin Sankar Velliangiri } 136505c2549SNavin Sankar Velliangiri 13707c6621aSJoe Perches /* sht4x_interval_read() - read the minimum poll interval in milliseconds */ 138505c2549SNavin Sankar Velliangiri static size_t sht4x_interval_read(struct sht4x_data *data, long *val) 139505c2549SNavin Sankar Velliangiri { 140505c2549SNavin Sankar Velliangiri *val = data->update_interval; 141505c2549SNavin Sankar Velliangiri return 0; 142505c2549SNavin Sankar Velliangiri } 143505c2549SNavin Sankar Velliangiri 14407c6621aSJoe Perches /* sht4x_temperature1_read() - read the temperature in millidegrees */ 145505c2549SNavin Sankar Velliangiri static int sht4x_temperature1_read(struct sht4x_data *data, long *val) 146505c2549SNavin Sankar Velliangiri { 147505c2549SNavin Sankar Velliangiri int ret; 148505c2549SNavin Sankar Velliangiri 149505c2549SNavin Sankar Velliangiri ret = sht4x_read_values(data); 150505c2549SNavin Sankar Velliangiri if (ret < 0) 151505c2549SNavin Sankar Velliangiri return ret; 152505c2549SNavin Sankar Velliangiri 153505c2549SNavin Sankar Velliangiri *val = data->temperature; 154505c2549SNavin Sankar Velliangiri 155505c2549SNavin Sankar Velliangiri return 0; 156505c2549SNavin Sankar Velliangiri } 157505c2549SNavin Sankar Velliangiri 15807c6621aSJoe Perches /* sht4x_humidity1_read() - read a relative humidity in millipercent */ 159505c2549SNavin Sankar Velliangiri static int sht4x_humidity1_read(struct sht4x_data *data, long *val) 160505c2549SNavin Sankar Velliangiri { 161505c2549SNavin Sankar Velliangiri int ret; 162505c2549SNavin Sankar Velliangiri 163505c2549SNavin Sankar Velliangiri ret = sht4x_read_values(data); 164505c2549SNavin Sankar Velliangiri if (ret < 0) 165505c2549SNavin Sankar Velliangiri return ret; 166505c2549SNavin Sankar Velliangiri 167505c2549SNavin Sankar Velliangiri *val = data->humidity; 168505c2549SNavin Sankar Velliangiri 169505c2549SNavin Sankar Velliangiri return 0; 170505c2549SNavin Sankar Velliangiri } 171505c2549SNavin Sankar Velliangiri 172505c2549SNavin Sankar Velliangiri static umode_t sht4x_hwmon_visible(const void *data, 173505c2549SNavin Sankar Velliangiri enum hwmon_sensor_types type, 174505c2549SNavin Sankar Velliangiri u32 attr, int channel) 175505c2549SNavin Sankar Velliangiri { 176505c2549SNavin Sankar Velliangiri switch (type) { 177505c2549SNavin Sankar Velliangiri case hwmon_temp: 178505c2549SNavin Sankar Velliangiri case hwmon_humidity: 179505c2549SNavin Sankar Velliangiri return 0444; 180505c2549SNavin Sankar Velliangiri case hwmon_chip: 181505c2549SNavin Sankar Velliangiri return 0644; 182505c2549SNavin Sankar Velliangiri default: 183505c2549SNavin Sankar Velliangiri return 0; 184505c2549SNavin Sankar Velliangiri } 185505c2549SNavin Sankar Velliangiri } 186505c2549SNavin Sankar Velliangiri 187505c2549SNavin Sankar Velliangiri static int sht4x_hwmon_read(struct device *dev, enum hwmon_sensor_types type, 188505c2549SNavin Sankar Velliangiri u32 attr, int channel, long *val) 189505c2549SNavin Sankar Velliangiri { 190505c2549SNavin Sankar Velliangiri struct sht4x_data *data = dev_get_drvdata(dev); 191505c2549SNavin Sankar Velliangiri 192505c2549SNavin Sankar Velliangiri switch (type) { 193505c2549SNavin Sankar Velliangiri case hwmon_temp: 194505c2549SNavin Sankar Velliangiri return sht4x_temperature1_read(data, val); 195505c2549SNavin Sankar Velliangiri case hwmon_humidity: 196505c2549SNavin Sankar Velliangiri return sht4x_humidity1_read(data, val); 197505c2549SNavin Sankar Velliangiri case hwmon_chip: 198505c2549SNavin Sankar Velliangiri return sht4x_interval_read(data, val); 199505c2549SNavin Sankar Velliangiri default: 200505c2549SNavin Sankar Velliangiri return -EOPNOTSUPP; 201505c2549SNavin Sankar Velliangiri } 202505c2549SNavin Sankar Velliangiri } 203505c2549SNavin Sankar Velliangiri 204505c2549SNavin Sankar Velliangiri static int sht4x_hwmon_write(struct device *dev, enum hwmon_sensor_types type, 205505c2549SNavin Sankar Velliangiri u32 attr, int channel, long val) 206505c2549SNavin Sankar Velliangiri { 207505c2549SNavin Sankar Velliangiri struct sht4x_data *data = dev_get_drvdata(dev); 208505c2549SNavin Sankar Velliangiri 209505c2549SNavin Sankar Velliangiri switch (type) { 210505c2549SNavin Sankar Velliangiri case hwmon_chip: 211505c2549SNavin Sankar Velliangiri return sht4x_interval_write(data, val); 212505c2549SNavin Sankar Velliangiri default: 213505c2549SNavin Sankar Velliangiri return -EOPNOTSUPP; 214505c2549SNavin Sankar Velliangiri } 215505c2549SNavin Sankar Velliangiri } 216505c2549SNavin Sankar Velliangiri 217505c2549SNavin Sankar Velliangiri static const struct hwmon_channel_info *sht4x_info[] = { 218505c2549SNavin Sankar Velliangiri HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL), 219505c2549SNavin Sankar Velliangiri HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), 220505c2549SNavin Sankar Velliangiri HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT), 221505c2549SNavin Sankar Velliangiri NULL, 222505c2549SNavin Sankar Velliangiri }; 223505c2549SNavin Sankar Velliangiri 224505c2549SNavin Sankar Velliangiri static const struct hwmon_ops sht4x_hwmon_ops = { 225505c2549SNavin Sankar Velliangiri .is_visible = sht4x_hwmon_visible, 226505c2549SNavin Sankar Velliangiri .read = sht4x_hwmon_read, 227505c2549SNavin Sankar Velliangiri .write = sht4x_hwmon_write, 228505c2549SNavin Sankar Velliangiri }; 229505c2549SNavin Sankar Velliangiri 230505c2549SNavin Sankar Velliangiri static const struct hwmon_chip_info sht4x_chip_info = { 231505c2549SNavin Sankar Velliangiri .ops = &sht4x_hwmon_ops, 232505c2549SNavin Sankar Velliangiri .info = sht4x_info, 233505c2549SNavin Sankar Velliangiri }; 234505c2549SNavin Sankar Velliangiri 235505c2549SNavin Sankar Velliangiri static int sht4x_probe(struct i2c_client *client, 236505c2549SNavin Sankar Velliangiri const struct i2c_device_id *sht4x_id) 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 284505c2549SNavin Sankar Velliangiri static struct i2c_driver sht4x_driver = { 285505c2549SNavin Sankar Velliangiri .driver = { 286505c2549SNavin Sankar Velliangiri .name = "sht4x", 287505c2549SNavin Sankar Velliangiri }, 288505c2549SNavin Sankar Velliangiri .probe = sht4x_probe, 289505c2549SNavin Sankar Velliangiri .id_table = sht4x_id, 290505c2549SNavin Sankar Velliangiri }; 291505c2549SNavin Sankar Velliangiri 292505c2549SNavin Sankar Velliangiri module_i2c_driver(sht4x_driver); 293505c2549SNavin Sankar Velliangiri 294505c2549SNavin Sankar Velliangiri MODULE_AUTHOR("Navin Sankar Velliangiri <navin@linumiz.com>"); 295505c2549SNavin Sankar Velliangiri MODULE_DESCRIPTION("Sensirion SHT4x humidity and temperature sensor driver"); 296505c2549SNavin Sankar Velliangiri MODULE_LICENSE("GPL v2"); 297