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