Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /* Sensirion SHT21 humidity and temperature sensor driver
0003  *
0004  * Copyright (C) 2010 Urs Fleisch <urs.fleisch@sensirion.com>
0005  *
0006  * Data sheet available at https://www.sensirion.com/file/datasheet_sht21
0007  */
0008 
0009 #include <linux/module.h>
0010 #include <linux/init.h>
0011 #include <linux/slab.h>
0012 #include <linux/i2c.h>
0013 #include <linux/hwmon.h>
0014 #include <linux/hwmon-sysfs.h>
0015 #include <linux/err.h>
0016 #include <linux/mutex.h>
0017 #include <linux/device.h>
0018 #include <linux/jiffies.h>
0019 
0020 /* I2C command bytes */
0021 #define SHT21_TRIG_T_MEASUREMENT_HM  0xe3
0022 #define SHT21_TRIG_RH_MEASUREMENT_HM 0xe5
0023 #define SHT21_READ_SNB_CMD1 0xFA
0024 #define SHT21_READ_SNB_CMD2 0x0F
0025 #define SHT21_READ_SNAC_CMD1 0xFC
0026 #define SHT21_READ_SNAC_CMD2 0xC9
0027 
0028 /**
0029  * struct sht21 - SHT21 device specific data
0030  * @client: I2C client device
0031  * @lock: mutex to protect measurement values
0032  * @last_update: time of last update (jiffies)
0033  * @temperature: cached temperature measurement value
0034  * @humidity: cached humidity measurement value
0035  * @valid: only 0 before first measurement is taken
0036  * @eic: cached electronic identification code text
0037  */
0038 struct sht21 {
0039     struct i2c_client *client;
0040     struct mutex lock;
0041     unsigned long last_update;
0042     int temperature;
0043     int humidity;
0044     bool valid;
0045     char eic[18];
0046 };
0047 
0048 /**
0049  * sht21_temp_ticks_to_millicelsius() - convert raw temperature ticks to
0050  * milli celsius
0051  * @ticks: temperature ticks value received from sensor
0052  */
0053 static inline int sht21_temp_ticks_to_millicelsius(int ticks)
0054 {
0055     ticks &= ~0x0003; /* clear status bits */
0056     /*
0057      * Formula T = -46.85 + 175.72 * ST / 2^16 from data sheet 6.2,
0058      * optimized for integer fixed point (3 digits) arithmetic
0059      */
0060     return ((21965 * ticks) >> 13) - 46850;
0061 }
0062 
0063 /**
0064  * sht21_rh_ticks_to_per_cent_mille() - convert raw humidity ticks to
0065  * one-thousandths of a percent relative humidity
0066  * @ticks: humidity ticks value received from sensor
0067  */
0068 static inline int sht21_rh_ticks_to_per_cent_mille(int ticks)
0069 {
0070     ticks &= ~0x0003; /* clear status bits */
0071     /*
0072      * Formula RH = -6 + 125 * SRH / 2^16 from data sheet 6.1,
0073      * optimized for integer fixed point (3 digits) arithmetic
0074      */
0075     return ((15625 * ticks) >> 13) - 6000;
0076 }
0077 
0078 /**
0079  * sht21_update_measurements() - get updated measurements from device
0080  * @dev: device
0081  *
0082  * Returns 0 on success, else negative errno.
0083  */
0084 static int sht21_update_measurements(struct device *dev)
0085 {
0086     int ret = 0;
0087     struct sht21 *sht21 = dev_get_drvdata(dev);
0088     struct i2c_client *client = sht21->client;
0089 
0090     mutex_lock(&sht21->lock);
0091     /*
0092      * Data sheet 2.4:
0093      * SHT2x should not be active for more than 10% of the time - e.g.
0094      * maximum two measurements per second at 12bit accuracy shall be made.
0095      */
0096     if (time_after(jiffies, sht21->last_update + HZ / 2) || !sht21->valid) {
0097         ret = i2c_smbus_read_word_swapped(client,
0098                           SHT21_TRIG_T_MEASUREMENT_HM);
0099         if (ret < 0)
0100             goto out;
0101         sht21->temperature = sht21_temp_ticks_to_millicelsius(ret);
0102         ret = i2c_smbus_read_word_swapped(client,
0103                           SHT21_TRIG_RH_MEASUREMENT_HM);
0104         if (ret < 0)
0105             goto out;
0106         sht21->humidity = sht21_rh_ticks_to_per_cent_mille(ret);
0107         sht21->last_update = jiffies;
0108         sht21->valid = true;
0109     }
0110 out:
0111     mutex_unlock(&sht21->lock);
0112 
0113     return ret >= 0 ? 0 : ret;
0114 }
0115 
0116 /**
0117  * sht21_show_temperature() - show temperature measurement value in sysfs
0118  * @dev: device
0119  * @attr: device attribute
0120  * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to
0121  *
0122  * Will be called on read access to temp1_input sysfs attribute.
0123  * Returns number of bytes written into buffer, negative errno on error.
0124  */
0125 static ssize_t sht21_temperature_show(struct device *dev,
0126                       struct device_attribute *attr,
0127                       char *buf)
0128 {
0129     struct sht21 *sht21 = dev_get_drvdata(dev);
0130     int ret;
0131 
0132     ret = sht21_update_measurements(dev);
0133     if (ret < 0)
0134         return ret;
0135     return sprintf(buf, "%d\n", sht21->temperature);
0136 }
0137 
0138 /**
0139  * sht21_show_humidity() - show humidity measurement value in sysfs
0140  * @dev: device
0141  * @attr: device attribute
0142  * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to
0143  *
0144  * Will be called on read access to humidity1_input sysfs attribute.
0145  * Returns number of bytes written into buffer, negative errno on error.
0146  */
0147 static ssize_t sht21_humidity_show(struct device *dev,
0148                    struct device_attribute *attr, char *buf)
0149 {
0150     struct sht21 *sht21 = dev_get_drvdata(dev);
0151     int ret;
0152 
0153     ret = sht21_update_measurements(dev);
0154     if (ret < 0)
0155         return ret;
0156     return sprintf(buf, "%d\n", sht21->humidity);
0157 }
0158 
0159 static ssize_t eic_read(struct sht21 *sht21)
0160 {
0161     struct i2c_client *client = sht21->client;
0162     u8 tx[2];
0163     u8 rx[8];
0164     u8 eic[8];
0165     struct i2c_msg msgs[2] = {
0166         {
0167             .addr = client->addr,
0168             .flags = 0,
0169             .len = 2,
0170             .buf = tx,
0171         },
0172         {
0173             .addr = client->addr,
0174             .flags = I2C_M_RD,
0175             .len = 8,
0176             .buf = rx,
0177         },
0178     };
0179     int ret;
0180 
0181     tx[0] = SHT21_READ_SNB_CMD1;
0182     tx[1] = SHT21_READ_SNB_CMD2;
0183     ret = i2c_transfer(client->adapter, msgs, 2);
0184     if (ret < 0)
0185         goto out;
0186     eic[2] = rx[0];
0187     eic[3] = rx[2];
0188     eic[4] = rx[4];
0189     eic[5] = rx[6];
0190 
0191     tx[0] = SHT21_READ_SNAC_CMD1;
0192     tx[1] = SHT21_READ_SNAC_CMD2;
0193     msgs[1].len = 6;
0194     ret = i2c_transfer(client->adapter, msgs, 2);
0195     if (ret < 0)
0196         goto out;
0197     eic[0] = rx[3];
0198     eic[1] = rx[4];
0199     eic[6] = rx[0];
0200     eic[7] = rx[1];
0201 
0202     ret = snprintf(sht21->eic, sizeof(sht21->eic),
0203                "%02x%02x%02x%02x%02x%02x%02x%02x\n",
0204                eic[0], eic[1], eic[2], eic[3],
0205                eic[4], eic[5], eic[6], eic[7]);
0206 out:
0207     if (ret < 0)
0208         sht21->eic[0] = 0;
0209 
0210     return ret;
0211 }
0212 
0213 /**
0214  * eic_show() - show Electronic Identification Code in sysfs
0215  * @dev: device
0216  * @attr: device attribute
0217  * @buf: sysfs buffer (PAGE_SIZE) where EIC is written
0218  *
0219  * Will be called on read access to eic sysfs attribute.
0220  * Returns number of bytes written into buffer, negative errno on error.
0221  */
0222 static ssize_t eic_show(struct device *dev,
0223     struct device_attribute *attr,
0224     char *buf)
0225 {
0226     struct sht21 *sht21 = dev_get_drvdata(dev);
0227     int ret;
0228 
0229     ret = sizeof(sht21->eic) - 1;
0230     mutex_lock(&sht21->lock);
0231     if (!sht21->eic[0])
0232         ret = eic_read(sht21);
0233     if (ret > 0)
0234         memcpy(buf, sht21->eic, ret);
0235     mutex_unlock(&sht21->lock);
0236     return ret;
0237 }
0238 
0239 /* sysfs attributes */
0240 static SENSOR_DEVICE_ATTR_RO(temp1_input, sht21_temperature, 0);
0241 static SENSOR_DEVICE_ATTR_RO(humidity1_input, sht21_humidity, 0);
0242 static DEVICE_ATTR_RO(eic);
0243 
0244 static struct attribute *sht21_attrs[] = {
0245     &sensor_dev_attr_temp1_input.dev_attr.attr,
0246     &sensor_dev_attr_humidity1_input.dev_attr.attr,
0247     &dev_attr_eic.attr,
0248     NULL
0249 };
0250 
0251 ATTRIBUTE_GROUPS(sht21);
0252 
0253 static int sht21_probe(struct i2c_client *client)
0254 {
0255     struct device *dev = &client->dev;
0256     struct device *hwmon_dev;
0257     struct sht21 *sht21;
0258 
0259     if (!i2c_check_functionality(client->adapter,
0260                      I2C_FUNC_SMBUS_WORD_DATA)) {
0261         dev_err(&client->dev,
0262             "adapter does not support SMBus word transactions\n");
0263         return -ENODEV;
0264     }
0265 
0266     sht21 = devm_kzalloc(dev, sizeof(*sht21), GFP_KERNEL);
0267     if (!sht21)
0268         return -ENOMEM;
0269 
0270     sht21->client = client;
0271 
0272     mutex_init(&sht21->lock);
0273 
0274     hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
0275                                sht21, sht21_groups);
0276     return PTR_ERR_OR_ZERO(hwmon_dev);
0277 }
0278 
0279 /* Device ID table */
0280 static const struct i2c_device_id sht21_id[] = {
0281     { "sht21", 0 },
0282     { }
0283 };
0284 MODULE_DEVICE_TABLE(i2c, sht21_id);
0285 
0286 static struct i2c_driver sht21_driver = {
0287     .driver.name = "sht21",
0288     .probe_new   = sht21_probe,
0289     .id_table    = sht21_id,
0290 };
0291 
0292 module_i2c_driver(sht21_driver);
0293 
0294 MODULE_AUTHOR("Urs Fleisch <urs.fleisch@sensirion.com>");
0295 MODULE_DESCRIPTION("Sensirion SHT21 humidity and temperature sensor driver");
0296 MODULE_LICENSE("GPL");