18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* Sensirion SHT21 humidity and temperature sensor driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2010 Urs Fleisch <urs.fleisch@sensirion.com> 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Data sheet available at https://www.sensirion.com/file/datasheet_sht21 78c2ecf20Sopenharmony_ci */ 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/module.h> 108c2ecf20Sopenharmony_ci#include <linux/init.h> 118c2ecf20Sopenharmony_ci#include <linux/slab.h> 128c2ecf20Sopenharmony_ci#include <linux/i2c.h> 138c2ecf20Sopenharmony_ci#include <linux/hwmon.h> 148c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h> 158c2ecf20Sopenharmony_ci#include <linux/err.h> 168c2ecf20Sopenharmony_ci#include <linux/mutex.h> 178c2ecf20Sopenharmony_ci#include <linux/device.h> 188c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* I2C command bytes */ 218c2ecf20Sopenharmony_ci#define SHT21_TRIG_T_MEASUREMENT_HM 0xe3 228c2ecf20Sopenharmony_ci#define SHT21_TRIG_RH_MEASUREMENT_HM 0xe5 238c2ecf20Sopenharmony_ci#define SHT21_READ_SNB_CMD1 0xFA 248c2ecf20Sopenharmony_ci#define SHT21_READ_SNB_CMD2 0x0F 258c2ecf20Sopenharmony_ci#define SHT21_READ_SNAC_CMD1 0xFC 268c2ecf20Sopenharmony_ci#define SHT21_READ_SNAC_CMD2 0xC9 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/** 298c2ecf20Sopenharmony_ci * struct sht21 - SHT21 device specific data 308c2ecf20Sopenharmony_ci * @client: I2C client device 318c2ecf20Sopenharmony_ci * @lock: mutex to protect measurement values 328c2ecf20Sopenharmony_ci * @last_update: time of last update (jiffies) 338c2ecf20Sopenharmony_ci * @temperature: cached temperature measurement value 348c2ecf20Sopenharmony_ci * @humidity: cached humidity measurement value 358c2ecf20Sopenharmony_ci * @valid: only 0 before first measurement is taken 368c2ecf20Sopenharmony_ci * @eic: cached electronic identification code text 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_cistruct sht21 { 398c2ecf20Sopenharmony_ci struct i2c_client *client; 408c2ecf20Sopenharmony_ci struct mutex lock; 418c2ecf20Sopenharmony_ci unsigned long last_update; 428c2ecf20Sopenharmony_ci int temperature; 438c2ecf20Sopenharmony_ci int humidity; 448c2ecf20Sopenharmony_ci char valid; 458c2ecf20Sopenharmony_ci char eic[18]; 468c2ecf20Sopenharmony_ci}; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci/** 498c2ecf20Sopenharmony_ci * sht21_temp_ticks_to_millicelsius() - convert raw temperature ticks to 508c2ecf20Sopenharmony_ci * milli celsius 518c2ecf20Sopenharmony_ci * @ticks: temperature ticks value received from sensor 528c2ecf20Sopenharmony_ci */ 538c2ecf20Sopenharmony_cistatic inline int sht21_temp_ticks_to_millicelsius(int ticks) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci ticks &= ~0x0003; /* clear status bits */ 568c2ecf20Sopenharmony_ci /* 578c2ecf20Sopenharmony_ci * Formula T = -46.85 + 175.72 * ST / 2^16 from data sheet 6.2, 588c2ecf20Sopenharmony_ci * optimized for integer fixed point (3 digits) arithmetic 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci return ((21965 * ticks) >> 13) - 46850; 618c2ecf20Sopenharmony_ci} 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci/** 648c2ecf20Sopenharmony_ci * sht21_rh_ticks_to_per_cent_mille() - convert raw humidity ticks to 658c2ecf20Sopenharmony_ci * one-thousandths of a percent relative humidity 668c2ecf20Sopenharmony_ci * @ticks: humidity ticks value received from sensor 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_cistatic inline int sht21_rh_ticks_to_per_cent_mille(int ticks) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci ticks &= ~0x0003; /* clear status bits */ 718c2ecf20Sopenharmony_ci /* 728c2ecf20Sopenharmony_ci * Formula RH = -6 + 125 * SRH / 2^16 from data sheet 6.1, 738c2ecf20Sopenharmony_ci * optimized for integer fixed point (3 digits) arithmetic 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci return ((15625 * ticks) >> 13) - 6000; 768c2ecf20Sopenharmony_ci} 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_ci/** 798c2ecf20Sopenharmony_ci * sht21_update_measurements() - get updated measurements from device 808c2ecf20Sopenharmony_ci * @dev: device 818c2ecf20Sopenharmony_ci * 828c2ecf20Sopenharmony_ci * Returns 0 on success, else negative errno. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_cistatic int sht21_update_measurements(struct device *dev) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci int ret = 0; 878c2ecf20Sopenharmony_ci struct sht21 *sht21 = dev_get_drvdata(dev); 888c2ecf20Sopenharmony_ci struct i2c_client *client = sht21->client; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci mutex_lock(&sht21->lock); 918c2ecf20Sopenharmony_ci /* 928c2ecf20Sopenharmony_ci * Data sheet 2.4: 938c2ecf20Sopenharmony_ci * SHT2x should not be active for more than 10% of the time - e.g. 948c2ecf20Sopenharmony_ci * maximum two measurements per second at 12bit accuracy shall be made. 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci if (time_after(jiffies, sht21->last_update + HZ / 2) || !sht21->valid) { 978c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_swapped(client, 988c2ecf20Sopenharmony_ci SHT21_TRIG_T_MEASUREMENT_HM); 998c2ecf20Sopenharmony_ci if (ret < 0) 1008c2ecf20Sopenharmony_ci goto out; 1018c2ecf20Sopenharmony_ci sht21->temperature = sht21_temp_ticks_to_millicelsius(ret); 1028c2ecf20Sopenharmony_ci ret = i2c_smbus_read_word_swapped(client, 1038c2ecf20Sopenharmony_ci SHT21_TRIG_RH_MEASUREMENT_HM); 1048c2ecf20Sopenharmony_ci if (ret < 0) 1058c2ecf20Sopenharmony_ci goto out; 1068c2ecf20Sopenharmony_ci sht21->humidity = sht21_rh_ticks_to_per_cent_mille(ret); 1078c2ecf20Sopenharmony_ci sht21->last_update = jiffies; 1088c2ecf20Sopenharmony_ci sht21->valid = 1; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ciout: 1118c2ecf20Sopenharmony_ci mutex_unlock(&sht21->lock); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci return ret >= 0 ? 0 : ret; 1148c2ecf20Sopenharmony_ci} 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci/** 1178c2ecf20Sopenharmony_ci * sht21_show_temperature() - show temperature measurement value in sysfs 1188c2ecf20Sopenharmony_ci * @dev: device 1198c2ecf20Sopenharmony_ci * @attr: device attribute 1208c2ecf20Sopenharmony_ci * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to 1218c2ecf20Sopenharmony_ci * 1228c2ecf20Sopenharmony_ci * Will be called on read access to temp1_input sysfs attribute. 1238c2ecf20Sopenharmony_ci * Returns number of bytes written into buffer, negative errno on error. 1248c2ecf20Sopenharmony_ci */ 1258c2ecf20Sopenharmony_cistatic ssize_t sht21_temperature_show(struct device *dev, 1268c2ecf20Sopenharmony_ci struct device_attribute *attr, 1278c2ecf20Sopenharmony_ci char *buf) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci struct sht21 *sht21 = dev_get_drvdata(dev); 1308c2ecf20Sopenharmony_ci int ret; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci ret = sht21_update_measurements(dev); 1338c2ecf20Sopenharmony_ci if (ret < 0) 1348c2ecf20Sopenharmony_ci return ret; 1358c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", sht21->temperature); 1368c2ecf20Sopenharmony_ci} 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci/** 1398c2ecf20Sopenharmony_ci * sht21_show_humidity() - show humidity measurement value in sysfs 1408c2ecf20Sopenharmony_ci * @dev: device 1418c2ecf20Sopenharmony_ci * @attr: device attribute 1428c2ecf20Sopenharmony_ci * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to 1438c2ecf20Sopenharmony_ci * 1448c2ecf20Sopenharmony_ci * Will be called on read access to humidity1_input sysfs attribute. 1458c2ecf20Sopenharmony_ci * Returns number of bytes written into buffer, negative errno on error. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_cistatic ssize_t sht21_humidity_show(struct device *dev, 1488c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1498c2ecf20Sopenharmony_ci{ 1508c2ecf20Sopenharmony_ci struct sht21 *sht21 = dev_get_drvdata(dev); 1518c2ecf20Sopenharmony_ci int ret; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci ret = sht21_update_measurements(dev); 1548c2ecf20Sopenharmony_ci if (ret < 0) 1558c2ecf20Sopenharmony_ci return ret; 1568c2ecf20Sopenharmony_ci return sprintf(buf, "%d\n", sht21->humidity); 1578c2ecf20Sopenharmony_ci} 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_cistatic ssize_t eic_read(struct sht21 *sht21) 1608c2ecf20Sopenharmony_ci{ 1618c2ecf20Sopenharmony_ci struct i2c_client *client = sht21->client; 1628c2ecf20Sopenharmony_ci u8 tx[2]; 1638c2ecf20Sopenharmony_ci u8 rx[8]; 1648c2ecf20Sopenharmony_ci u8 eic[8]; 1658c2ecf20Sopenharmony_ci struct i2c_msg msgs[2] = { 1668c2ecf20Sopenharmony_ci { 1678c2ecf20Sopenharmony_ci .addr = client->addr, 1688c2ecf20Sopenharmony_ci .flags = 0, 1698c2ecf20Sopenharmony_ci .len = 2, 1708c2ecf20Sopenharmony_ci .buf = tx, 1718c2ecf20Sopenharmony_ci }, 1728c2ecf20Sopenharmony_ci { 1738c2ecf20Sopenharmony_ci .addr = client->addr, 1748c2ecf20Sopenharmony_ci .flags = I2C_M_RD, 1758c2ecf20Sopenharmony_ci .len = 8, 1768c2ecf20Sopenharmony_ci .buf = rx, 1778c2ecf20Sopenharmony_ci }, 1788c2ecf20Sopenharmony_ci }; 1798c2ecf20Sopenharmony_ci int ret; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci tx[0] = SHT21_READ_SNB_CMD1; 1828c2ecf20Sopenharmony_ci tx[1] = SHT21_READ_SNB_CMD2; 1838c2ecf20Sopenharmony_ci ret = i2c_transfer(client->adapter, msgs, 2); 1848c2ecf20Sopenharmony_ci if (ret < 0) 1858c2ecf20Sopenharmony_ci goto out; 1868c2ecf20Sopenharmony_ci eic[2] = rx[0]; 1878c2ecf20Sopenharmony_ci eic[3] = rx[2]; 1888c2ecf20Sopenharmony_ci eic[4] = rx[4]; 1898c2ecf20Sopenharmony_ci eic[5] = rx[6]; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci tx[0] = SHT21_READ_SNAC_CMD1; 1928c2ecf20Sopenharmony_ci tx[1] = SHT21_READ_SNAC_CMD2; 1938c2ecf20Sopenharmony_ci msgs[1].len = 6; 1948c2ecf20Sopenharmony_ci ret = i2c_transfer(client->adapter, msgs, 2); 1958c2ecf20Sopenharmony_ci if (ret < 0) 1968c2ecf20Sopenharmony_ci goto out; 1978c2ecf20Sopenharmony_ci eic[0] = rx[3]; 1988c2ecf20Sopenharmony_ci eic[1] = rx[4]; 1998c2ecf20Sopenharmony_ci eic[6] = rx[0]; 2008c2ecf20Sopenharmony_ci eic[7] = rx[1]; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci ret = snprintf(sht21->eic, sizeof(sht21->eic), 2038c2ecf20Sopenharmony_ci "%02x%02x%02x%02x%02x%02x%02x%02x\n", 2048c2ecf20Sopenharmony_ci eic[0], eic[1], eic[2], eic[3], 2058c2ecf20Sopenharmony_ci eic[4], eic[5], eic[6], eic[7]); 2068c2ecf20Sopenharmony_ciout: 2078c2ecf20Sopenharmony_ci if (ret < 0) 2088c2ecf20Sopenharmony_ci sht21->eic[0] = 0; 2098c2ecf20Sopenharmony_ci 2108c2ecf20Sopenharmony_ci return ret; 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci/** 2148c2ecf20Sopenharmony_ci * eic_show() - show Electronic Identification Code in sysfs 2158c2ecf20Sopenharmony_ci * @dev: device 2168c2ecf20Sopenharmony_ci * @attr: device attribute 2178c2ecf20Sopenharmony_ci * @buf: sysfs buffer (PAGE_SIZE) where EIC is written 2188c2ecf20Sopenharmony_ci * 2198c2ecf20Sopenharmony_ci * Will be called on read access to eic sysfs attribute. 2208c2ecf20Sopenharmony_ci * Returns number of bytes written into buffer, negative errno on error. 2218c2ecf20Sopenharmony_ci */ 2228c2ecf20Sopenharmony_cistatic ssize_t eic_show(struct device *dev, 2238c2ecf20Sopenharmony_ci struct device_attribute *attr, 2248c2ecf20Sopenharmony_ci char *buf) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci struct sht21 *sht21 = dev_get_drvdata(dev); 2278c2ecf20Sopenharmony_ci int ret; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci ret = sizeof(sht21->eic) - 1; 2308c2ecf20Sopenharmony_ci mutex_lock(&sht21->lock); 2318c2ecf20Sopenharmony_ci if (!sht21->eic[0]) 2328c2ecf20Sopenharmony_ci ret = eic_read(sht21); 2338c2ecf20Sopenharmony_ci if (ret > 0) 2348c2ecf20Sopenharmony_ci memcpy(buf, sht21->eic, ret); 2358c2ecf20Sopenharmony_ci mutex_unlock(&sht21->lock); 2368c2ecf20Sopenharmony_ci return ret; 2378c2ecf20Sopenharmony_ci} 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci/* sysfs attributes */ 2408c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(temp1_input, sht21_temperature, 0); 2418c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR_RO(humidity1_input, sht21_humidity, 0); 2428c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(eic); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic struct attribute *sht21_attrs[] = { 2458c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 2468c2ecf20Sopenharmony_ci &sensor_dev_attr_humidity1_input.dev_attr.attr, 2478c2ecf20Sopenharmony_ci &dev_attr_eic.attr, 2488c2ecf20Sopenharmony_ci NULL 2498c2ecf20Sopenharmony_ci}; 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(sht21); 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_cistatic int sht21_probe(struct i2c_client *client) 2548c2ecf20Sopenharmony_ci{ 2558c2ecf20Sopenharmony_ci struct device *dev = &client->dev; 2568c2ecf20Sopenharmony_ci struct device *hwmon_dev; 2578c2ecf20Sopenharmony_ci struct sht21 *sht21; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, 2608c2ecf20Sopenharmony_ci I2C_FUNC_SMBUS_WORD_DATA)) { 2618c2ecf20Sopenharmony_ci dev_err(&client->dev, 2628c2ecf20Sopenharmony_ci "adapter does not support SMBus word transactions\n"); 2638c2ecf20Sopenharmony_ci return -ENODEV; 2648c2ecf20Sopenharmony_ci } 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci sht21 = devm_kzalloc(dev, sizeof(*sht21), GFP_KERNEL); 2678c2ecf20Sopenharmony_ci if (!sht21) 2688c2ecf20Sopenharmony_ci return -ENOMEM; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci sht21->client = client; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci mutex_init(&sht21->lock); 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 2758c2ecf20Sopenharmony_ci sht21, sht21_groups); 2768c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(hwmon_dev); 2778c2ecf20Sopenharmony_ci} 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci/* Device ID table */ 2808c2ecf20Sopenharmony_cistatic const struct i2c_device_id sht21_id[] = { 2818c2ecf20Sopenharmony_ci { "sht21", 0 }, 2828c2ecf20Sopenharmony_ci { } 2838c2ecf20Sopenharmony_ci}; 2848c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, sht21_id); 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_cistatic struct i2c_driver sht21_driver = { 2878c2ecf20Sopenharmony_ci .driver.name = "sht21", 2888c2ecf20Sopenharmony_ci .probe_new = sht21_probe, 2898c2ecf20Sopenharmony_ci .id_table = sht21_id, 2908c2ecf20Sopenharmony_ci}; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cimodule_i2c_driver(sht21_driver); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Urs Fleisch <urs.fleisch@sensirion.com>"); 2958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sensirion SHT21 humidity and temperature sensor driver"); 2968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 297