linux/drivers/hwmon/sht4x.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2
   3/*
   4 * Copyright (c) Linumiz 2021
   5 *
   6 * sht4x.c - Linux hwmon driver for SHT4x Temperature and Humidity sensor
   7 *
   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 2000
  22
  23/*
  24 * I2C command delays (in microseconds)
  25 */
  26#define SHT4X_MEAS_DELAY        1000
  27#define SHT4X_DELAY_EXTRA       10000
  28
  29/*
  30 * Command Bytes
  31 */
  32#define SHT4X_CMD_MEASURE_HPM   0b11111101
  33#define SHT4X_CMD_RESET         0b10010100
  34
  35#define SHT4X_CMD_LEN           1
  36#define SHT4X_CRC8_LEN          1
  37#define SHT4X_WORD_LEN          2
  38#define SHT4X_RESPONSE_LENGTH   6
  39#define SHT4X_CRC8_POLYNOMIAL   0x31
  40#define SHT4X_CRC8_INIT         0xff
  41#define SHT4X_MIN_TEMPERATURE   -45000
  42#define SHT4X_MAX_TEMPERATURE   125000
  43#define SHT4X_MIN_HUMIDITY      0
  44#define SHT4X_MAX_HUMIDITY      100000
  45
  46DECLARE_CRC8_TABLE(sht4x_crc8_table);
  47
  48/**
  49 * struct sht4x_data - All the data required to operate an SHT4X chip
  50 * @client: the i2c client associated with the SHT4X
  51 * @lock: a mutex that is used to prevent parallel access to the i2c client
  52 * @update_interval: the minimum poll interval
  53 * @last_updated: the previous time that the SHT4X was polled
  54 * @temperature: the latest temperature value received from the SHT4X
  55 * @humidity: the latest humidity value received from the SHT4X
  56 */
  57struct sht4x_data {
  58        struct i2c_client       *client;
  59        struct mutex            lock;   /* atomic read data updates */
  60        bool                    valid;  /* validity of fields below */
  61        long                    update_interval;        /* in milli-seconds */
  62        long                    last_updated;   /* in jiffies */
  63        s32                     temperature;
  64        s32                     humidity;
  65};
  66
  67/**
  68 * sht4x_read_values() - read and parse the raw data from the SHT4X
  69 * @sht4x_data: the struct sht4x_data to use for the lock
  70 * Return: 0 if successful, -ERRNO if not
  71 */
  72static int sht4x_read_values(struct sht4x_data *data)
  73{
  74        int ret = 0;
  75        u16 t_ticks, rh_ticks;
  76        unsigned long next_update;
  77        struct i2c_client *client = data->client;
  78        u8 crc;
  79        u8 cmd[SHT4X_CMD_LEN] = {SHT4X_CMD_MEASURE_HPM};
  80        u8 raw_data[SHT4X_RESPONSE_LENGTH];
  81
  82        mutex_lock(&data->lock);
  83        next_update = data->last_updated +
  84                      msecs_to_jiffies(data->update_interval);
  85
  86        if (data->valid && time_before_eq(jiffies, next_update))
  87                goto unlock;
  88
  89        ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN);
  90        if (ret < 0)
  91                goto unlock;
  92
  93        usleep_range(SHT4X_MEAS_DELAY, SHT4X_MEAS_DELAY + SHT4X_DELAY_EXTRA);
  94
  95        ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH);
  96        if (ret != SHT4X_RESPONSE_LENGTH) {
  97                if (ret >= 0)
  98                        ret = -ENODATA;
  99                goto unlock;
 100        }
 101
 102        t_ticks = raw_data[0] << 8 | raw_data[1];
 103        rh_ticks = raw_data[3] << 8 | raw_data[4];
 104
 105        crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
 106        if (crc != raw_data[2]) {
 107                dev_err(&client->dev, "data integrity check failed\n");
 108                ret = -EIO;
 109                goto unlock;
 110        }
 111
 112        crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE);
 113        if (crc != raw_data[5]) {
 114                dev_err(&client->dev, "data integrity check failed\n");
 115                ret = -EIO;
 116                goto unlock;
 117        }
 118
 119        data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000;
 120        data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000;
 121        data->last_updated = jiffies;
 122        data->valid = true;
 123        ret = 0;
 124
 125unlock:
 126        mutex_unlock(&data->lock);
 127        return ret;
 128}
 129
 130static ssize_t sht4x_interval_write(struct sht4x_data *data, long val)
 131{
 132        data->update_interval = clamp_val(val, SHT4X_MIN_POLL_INTERVAL, UINT_MAX);
 133
 134        return 0;
 135}
 136
 137/* sht4x_interval_read() - read the minimum poll interval in milliseconds */
 138static size_t sht4x_interval_read(struct sht4x_data *data, long *val)
 139{
 140        *val = data->update_interval;
 141        return 0;
 142}
 143
 144/* sht4x_temperature1_read() - read the temperature in millidegrees */
 145static int sht4x_temperature1_read(struct sht4x_data *data, long *val)
 146{
 147        int ret;
 148
 149        ret = sht4x_read_values(data);
 150        if (ret < 0)
 151                return ret;
 152
 153        *val = data->temperature;
 154
 155        return 0;
 156}
 157
 158/* sht4x_humidity1_read() - read a relative humidity in millipercent */
 159static int sht4x_humidity1_read(struct sht4x_data *data, long *val)
 160{
 161        int ret;
 162
 163        ret = sht4x_read_values(data);
 164        if (ret < 0)
 165                return ret;
 166
 167        *val = data->humidity;
 168
 169        return 0;
 170}
 171
 172static umode_t sht4x_hwmon_visible(const void *data,
 173                                   enum hwmon_sensor_types type,
 174                                   u32 attr, int channel)
 175{
 176        switch (type) {
 177        case hwmon_temp:
 178        case hwmon_humidity:
 179                return 0444;
 180        case hwmon_chip:
 181                return 0644;
 182        default:
 183                return 0;
 184        }
 185}
 186
 187static int sht4x_hwmon_read(struct device *dev, enum hwmon_sensor_types type,
 188                            u32 attr, int channel, long *val)
 189{
 190        struct sht4x_data *data = dev_get_drvdata(dev);
 191
 192        switch (type) {
 193        case hwmon_temp:
 194                return sht4x_temperature1_read(data, val);
 195        case hwmon_humidity:
 196                return sht4x_humidity1_read(data, val);
 197        case hwmon_chip:
 198                return sht4x_interval_read(data, val);
 199        default:
 200                return -EOPNOTSUPP;
 201        }
 202}
 203
 204static int sht4x_hwmon_write(struct device *dev, enum hwmon_sensor_types type,
 205                             u32 attr, int channel, long val)
 206{
 207        struct sht4x_data *data = dev_get_drvdata(dev);
 208
 209        switch (type) {
 210        case hwmon_chip:
 211                return sht4x_interval_write(data, val);
 212        default:
 213                return -EOPNOTSUPP;
 214        }
 215}
 216
 217static const struct hwmon_channel_info *sht4x_info[] = {
 218        HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL),
 219        HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),
 220        HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT),
 221        NULL,
 222};
 223
 224static const struct hwmon_ops sht4x_hwmon_ops = {
 225        .is_visible = sht4x_hwmon_visible,
 226        .read = sht4x_hwmon_read,
 227        .write = sht4x_hwmon_write,
 228};
 229
 230static const struct hwmon_chip_info sht4x_chip_info = {
 231        .ops = &sht4x_hwmon_ops,
 232        .info = sht4x_info,
 233};
 234
 235static int sht4x_probe(struct i2c_client *client,
 236                       const struct i2c_device_id *sht4x_id)
 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 and
 246         * writes as well as multi-byte commands which are not supported by
 247         * the smbus protocol
 248         */
 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", 0 },
 280        { },
 281};
 282MODULE_DEVICE_TABLE(i2c, sht4x_id);
 283
 284static struct i2c_driver sht4x_driver = {
 285        .driver = {
 286                .name = "sht4x",
 287        },
 288        .probe          = sht4x_probe,
 289        .id_table       = sht4x_id,
 290};
 291
 292module_i2c_driver(sht4x_driver);
 293
 294MODULE_AUTHOR("Navin Sankar Velliangiri <navin@linumiz.com>");
 295MODULE_DESCRIPTION("Sensirion SHT4x humidity and temperature sensor driver");
 296MODULE_LICENSE("GPL v2");
 297