Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 
0003 /*
0004  * Copyright (c) Linumiz 2021
0005  *
0006  * sht4x.c - Linux hwmon driver for SHT4x Temperature and Humidity sensor
0007  *
0008  * Author: Navin Sankar Velliangiri <navin@linumiz.com>
0009  */
0010 
0011 #include <linux/crc8.h>
0012 #include <linux/delay.h>
0013 #include <linux/hwmon.h>
0014 #include <linux/i2c.h>
0015 #include <linux/jiffies.h>
0016 #include <linux/module.h>
0017 
0018 /*
0019  * Poll intervals (in milliseconds)
0020  */
0021 #define SHT4X_MIN_POLL_INTERVAL 2000
0022 
0023 /*
0024  * I2C command delays (in microseconds)
0025  */
0026 #define SHT4X_MEAS_DELAY_HPM    8200    /* see t_MEAS,h in datasheet */
0027 #define SHT4X_DELAY_EXTRA   10000
0028 
0029 /*
0030  * Command Bytes
0031  */
0032 #define SHT4X_CMD_MEASURE_HPM   0b11111101
0033 #define SHT4X_CMD_RESET     0b10010100
0034 
0035 #define SHT4X_CMD_LEN       1
0036 #define SHT4X_CRC8_LEN      1
0037 #define SHT4X_WORD_LEN      2
0038 #define SHT4X_RESPONSE_LENGTH   6
0039 #define SHT4X_CRC8_POLYNOMIAL   0x31
0040 #define SHT4X_CRC8_INIT     0xff
0041 #define SHT4X_MIN_TEMPERATURE   -45000
0042 #define SHT4X_MAX_TEMPERATURE   125000
0043 #define SHT4X_MIN_HUMIDITY  0
0044 #define SHT4X_MAX_HUMIDITY  100000
0045 
0046 DECLARE_CRC8_TABLE(sht4x_crc8_table);
0047 
0048 /**
0049  * struct sht4x_data - All the data required to operate an SHT4X chip
0050  * @client: the i2c client associated with the SHT4X
0051  * @lock: a mutex that is used to prevent parallel access to the i2c client
0052  * @update_interval: the minimum poll interval
0053  * @last_updated: the previous time that the SHT4X was polled
0054  * @temperature: the latest temperature value received from the SHT4X
0055  * @humidity: the latest humidity value received from the SHT4X
0056  */
0057 struct sht4x_data {
0058     struct i2c_client   *client;
0059     struct mutex        lock;   /* atomic read data updates */
0060     bool            valid;  /* validity of fields below */
0061     long            update_interval;    /* in milli-seconds */
0062     long            last_updated;   /* in jiffies */
0063     s32         temperature;
0064     s32         humidity;
0065 };
0066 
0067 /**
0068  * sht4x_read_values() - read and parse the raw data from the SHT4X
0069  * @sht4x_data: the struct sht4x_data to use for the lock
0070  * Return: 0 if successful, -ERRNO if not
0071  */
0072 static int sht4x_read_values(struct sht4x_data *data)
0073 {
0074     int ret = 0;
0075     u16 t_ticks, rh_ticks;
0076     unsigned long next_update;
0077     struct i2c_client *client = data->client;
0078     u8 crc;
0079     u8 cmd[SHT4X_CMD_LEN] = {SHT4X_CMD_MEASURE_HPM};
0080     u8 raw_data[SHT4X_RESPONSE_LENGTH];
0081 
0082     mutex_lock(&data->lock);
0083     next_update = data->last_updated +
0084               msecs_to_jiffies(data->update_interval);
0085 
0086     if (data->valid && time_before_eq(jiffies, next_update))
0087         goto unlock;
0088 
0089     ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN);
0090     if (ret < 0)
0091         goto unlock;
0092 
0093     usleep_range(SHT4X_MEAS_DELAY_HPM, SHT4X_MEAS_DELAY_HPM + SHT4X_DELAY_EXTRA);
0094 
0095     ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH);
0096     if (ret != SHT4X_RESPONSE_LENGTH) {
0097         if (ret >= 0)
0098             ret = -ENODATA;
0099         goto unlock;
0100     }
0101 
0102     t_ticks = raw_data[0] << 8 | raw_data[1];
0103     rh_ticks = raw_data[3] << 8 | raw_data[4];
0104 
0105     crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
0106     if (crc != raw_data[2]) {
0107         dev_err(&client->dev, "data integrity check failed\n");
0108         ret = -EIO;
0109         goto unlock;
0110     }
0111 
0112     crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
0113     if (crc != raw_data[5]) {
0114         dev_err(&client->dev, "data integrity check failed\n");
0115         ret = -EIO;
0116         goto unlock;
0117     }
0118 
0119     data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000;
0120     data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000;
0121     data->last_updated = jiffies;
0122     data->valid = true;
0123     ret = 0;
0124 
0125 unlock:
0126     mutex_unlock(&data->lock);
0127     return ret;
0128 }
0129 
0130 static ssize_t sht4x_interval_write(struct sht4x_data *data, long val)
0131 {
0132     data->update_interval = clamp_val(val, SHT4X_MIN_POLL_INTERVAL, UINT_MAX);
0133 
0134     return 0;
0135 }
0136 
0137 /* sht4x_interval_read() - read the minimum poll interval in milliseconds */
0138 static size_t sht4x_interval_read(struct sht4x_data *data, long *val)
0139 {
0140     *val = data->update_interval;
0141     return 0;
0142 }
0143 
0144 /* sht4x_temperature1_read() - read the temperature in millidegrees */
0145 static int sht4x_temperature1_read(struct sht4x_data *data, long *val)
0146 {
0147     int ret;
0148 
0149     ret = sht4x_read_values(data);
0150     if (ret < 0)
0151         return ret;
0152 
0153     *val = data->temperature;
0154 
0155     return 0;
0156 }
0157 
0158 /* sht4x_humidity1_read() - read a relative humidity in millipercent */
0159 static int sht4x_humidity1_read(struct sht4x_data *data, long *val)
0160 {
0161     int ret;
0162 
0163     ret = sht4x_read_values(data);
0164     if (ret < 0)
0165         return ret;
0166 
0167     *val = data->humidity;
0168 
0169     return 0;
0170 }
0171 
0172 static umode_t sht4x_hwmon_visible(const void *data,
0173                    enum hwmon_sensor_types type,
0174                    u32 attr, int channel)
0175 {
0176     switch (type) {
0177     case hwmon_temp:
0178     case hwmon_humidity:
0179         return 0444;
0180     case hwmon_chip:
0181         return 0644;
0182     default:
0183         return 0;
0184     }
0185 }
0186 
0187 static int sht4x_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
0188                 u32 attr, int channel, long *val)
0189 {
0190     struct sht4x_data *data = dev_get_drvdata(dev);
0191 
0192     switch (type) {
0193     case hwmon_temp:
0194         return sht4x_temperature1_read(data, val);
0195     case hwmon_humidity:
0196         return sht4x_humidity1_read(data, val);
0197     case hwmon_chip:
0198         return sht4x_interval_read(data, val);
0199     default:
0200         return -EOPNOTSUPP;
0201     }
0202 }
0203 
0204 static int sht4x_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
0205                  u32 attr, int channel, long val)
0206 {
0207     struct sht4x_data *data = dev_get_drvdata(dev);
0208 
0209     switch (type) {
0210     case hwmon_chip:
0211         return sht4x_interval_write(data, val);
0212     default:
0213         return -EOPNOTSUPP;
0214     }
0215 }
0216 
0217 static const struct hwmon_channel_info *sht4x_info[] = {
0218     HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL),
0219     HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
0220     HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT),
0221     NULL,
0222 };
0223 
0224 static const struct hwmon_ops sht4x_hwmon_ops = {
0225     .is_visible = sht4x_hwmon_visible,
0226     .read = sht4x_hwmon_read,
0227     .write = sht4x_hwmon_write,
0228 };
0229 
0230 static const struct hwmon_chip_info sht4x_chip_info = {
0231     .ops = &sht4x_hwmon_ops,
0232     .info = sht4x_info,
0233 };
0234 
0235 static int sht4x_probe(struct i2c_client *client,
0236                const struct i2c_device_id *sht4x_id)
0237 {
0238     struct device *device = &client->dev;
0239     struct device *hwmon_dev;
0240     struct sht4x_data *data;
0241     u8 cmd[] = {SHT4X_CMD_RESET};
0242     int ret;
0243 
0244     /*
0245      * we require full i2c support since the sht4x uses multi-byte read and
0246      * writes as well as multi-byte commands which are not supported by
0247      * the smbus protocol
0248      */
0249     if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
0250         return -EOPNOTSUPP;
0251 
0252     data = devm_kzalloc(device, sizeof(*data), GFP_KERNEL);
0253     if (!data)
0254         return -ENOMEM;
0255 
0256     data->update_interval = SHT4X_MIN_POLL_INTERVAL;
0257     data->client = client;
0258 
0259     mutex_init(&data->lock);
0260 
0261     crc8_populate_msb(sht4x_crc8_table, SHT4X_CRC8_POLYNOMIAL);
0262 
0263     ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN);
0264     if (ret < 0)
0265         return ret;
0266     if (ret != SHT4X_CMD_LEN)
0267         return -EIO;
0268 
0269     hwmon_dev = devm_hwmon_device_register_with_info(device,
0270                              client->name,
0271                              data,
0272                              &sht4x_chip_info,
0273                              NULL);
0274 
0275     return PTR_ERR_OR_ZERO(hwmon_dev);
0276 }
0277 
0278 static const struct i2c_device_id sht4x_id[] = {
0279     { "sht4x", 0 },
0280     { },
0281 };
0282 MODULE_DEVICE_TABLE(i2c, sht4x_id);
0283 
0284 static const struct of_device_id sht4x_of_match[] = {
0285     { .compatible = "sensirion,sht4x" },
0286     { }
0287 };
0288 MODULE_DEVICE_TABLE(of, sht4x_of_match);
0289 
0290 static struct i2c_driver sht4x_driver = {
0291     .driver = {
0292         .name = "sht4x",
0293         .of_match_table = sht4x_of_match,
0294     },
0295     .probe      = sht4x_probe,
0296     .id_table   = sht4x_id,
0297 };
0298 
0299 module_i2c_driver(sht4x_driver);
0300 
0301 MODULE_AUTHOR("Navin Sankar Velliangiri <navin@linumiz.com>");
0302 MODULE_DESCRIPTION("Sensirion SHT4x humidity and temperature sensor driver");
0303 MODULE_LICENSE("GPL v2");