Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwmon: (htu21) Add Measurement Specialties HTU21D support

Signed-off-by: William Markezana <william.markezana@meas-spec.com>
[Guenter Roeck - minor formatting changes]
Signed-off-by: Guenter Roeck <linux@roeck-us.net>

authored by

William Markezana and committed by
Guenter Roeck
f060c658 5407e051

+256
+46
Documentation/hwmon/htu21
··· 1 + Kernel driver htu21 2 + =================== 3 + 4 + Supported chips: 5 + * Measurement Specialties HTU21D 6 + Prefix: 'htu21' 7 + Addresses scanned: none 8 + Datasheet: Publicly available at the Measurement Specialties website 9 + http://www.meas-spec.com/downloads/HTU21D.pdf 10 + 11 + 12 + Author: 13 + William Markezana <william.markezana@meas-spec.com> 14 + 15 + Description 16 + ----------- 17 + 18 + The HTU21D is a humidity and temperature sensor in a DFN package of 19 + only 3 x 3 mm footprint and 0.9 mm height. 20 + 21 + The devices communicate with the I2C protocol. All sensors are set to the 22 + same I2C address 0x40, so an entry with I2C_BOARD_INFO("htu21", 0x40) can 23 + be used in the board setup code. 24 + 25 + This driver does not auto-detect devices. You will have to instantiate the 26 + devices explicitly. Please see Documentation/i2c/instantiating-devices 27 + for details. 28 + 29 + sysfs-Interface 30 + --------------- 31 + 32 + temp1_input - temperature input 33 + humidity1_input - humidity input 34 + 35 + Notes 36 + ----- 37 + 38 + The driver uses the default resolution settings of 12 bit for humidity and 14 39 + bit for temperature, which results in typical measurement times of 11 ms for 40 + humidity and 44 ms for temperature. To keep self heating below 0.1 degree 41 + Celsius, the device should not be active for more than 10% of the time. For 42 + this reason, the driver performs no more than two measurements per second and 43 + reports cached information if polled more frequently. 44 + 45 + Different resolutions, the on-chip heater, using the CRC checksum and reading 46 + the serial number are not supported yet.
+10
drivers/hwmon/Kconfig
··· 511 511 This driver can also be built as a module. If so, the module 512 512 will be called hih6130. 513 513 514 + config SENSORS_HTU21 515 + tristate "Measurement Specialties HTU21D humidity/temperature sensors" 516 + depends on I2C 517 + help 518 + If you say yes here you get support for the Measurement Specialties 519 + HTU21D humidity and temperature sensors. 520 + 521 + This driver can also be built as a module. If so, the module 522 + will be called htu21. 523 + 514 524 config SENSORS_CORETEMP 515 525 tristate "Intel Core/Core2/Atom temperature sensor" 516 526 depends on X86
+1
drivers/hwmon/Makefile
··· 65 65 obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o 66 66 obj-$(CONFIG_SENSORS_GPIO_FAN) += gpio-fan.o 67 67 obj-$(CONFIG_SENSORS_HIH6130) += hih6130.o 68 + obj-$(CONFIG_SENSORS_HTU21) += htu21.o 68 69 obj-$(CONFIG_SENSORS_ULTRA45) += ultra45_env.o 69 70 obj-$(CONFIG_SENSORS_I5K_AMB) += i5k_amb.o 70 71 obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o
+199
drivers/hwmon/htu21.c
··· 1 + /* 2 + * Measurement Specialties HTU21D humidity and temperature sensor driver 3 + * 4 + * Copyright (C) 2013 William Markezana <william.markezana@meas-spec.com> 5 + * 6 + * This program is free software; you can redistribute it and/or modify 7 + * it under the terms of the GNU General Public License as published by 8 + * the Free Software Foundation; either version 2 of the License, or 9 + * (at your option) any later version. 10 + * 11 + * This program is distributed in the hope that it will be useful, 12 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 + * GNU General Public License for more details. 15 + * 16 + */ 17 + 18 + #include <linux/module.h> 19 + #include <linux/init.h> 20 + #include <linux/slab.h> 21 + #include <linux/i2c.h> 22 + #include <linux/hwmon.h> 23 + #include <linux/hwmon-sysfs.h> 24 + #include <linux/err.h> 25 + #include <linux/mutex.h> 26 + #include <linux/device.h> 27 + #include <linux/jiffies.h> 28 + 29 + /* HTU21 Commands */ 30 + #define HTU21_T_MEASUREMENT_HM 0xE3 31 + #define HTU21_RH_MEASUREMENT_HM 0xE5 32 + 33 + struct htu21 { 34 + struct device *hwmon_dev; 35 + struct mutex lock; 36 + bool valid; 37 + unsigned long last_update; 38 + int temperature; 39 + int humidity; 40 + }; 41 + 42 + static inline int htu21_temp_ticks_to_millicelsius(int ticks) 43 + { 44 + ticks &= ~0x0003; /* clear status bits */ 45 + /* 46 + * Formula T = -46.85 + 175.72 * ST / 2^16 from datasheet p14, 47 + * optimized for integer fixed point (3 digits) arithmetic 48 + */ 49 + return ((21965 * ticks) >> 13) - 46850; 50 + } 51 + 52 + static inline int htu21_rh_ticks_to_per_cent_mille(int ticks) 53 + { 54 + ticks &= ~0x0003; /* clear status bits */ 55 + /* 56 + * Formula RH = -6 + 125 * SRH / 2^16 from datasheet p14, 57 + * optimized for integer fixed point (3 digits) arithmetic 58 + */ 59 + return ((15625 * ticks) >> 13) - 6000; 60 + } 61 + 62 + static int htu21_update_measurements(struct i2c_client *client) 63 + { 64 + int ret = 0; 65 + struct htu21 *htu21 = i2c_get_clientdata(client); 66 + 67 + mutex_lock(&htu21->lock); 68 + 69 + if (time_after(jiffies, htu21->last_update + HZ / 2) || 70 + !htu21->valid) { 71 + ret = i2c_smbus_read_word_swapped(client, 72 + HTU21_T_MEASUREMENT_HM); 73 + if (ret < 0) 74 + goto out; 75 + htu21->temperature = htu21_temp_ticks_to_millicelsius(ret); 76 + ret = i2c_smbus_read_word_swapped(client, 77 + HTU21_RH_MEASUREMENT_HM); 78 + if (ret < 0) 79 + goto out; 80 + htu21->humidity = htu21_rh_ticks_to_per_cent_mille(ret); 81 + htu21->last_update = jiffies; 82 + htu21->valid = true; 83 + } 84 + out: 85 + mutex_unlock(&htu21->lock); 86 + 87 + return ret >= 0 ? 0 : ret; 88 + } 89 + 90 + static ssize_t htu21_show_temperature(struct device *dev, 91 + struct device_attribute *attr, char *buf) 92 + { 93 + struct i2c_client *client = to_i2c_client(dev); 94 + struct htu21 *htu21 = i2c_get_clientdata(client); 95 + int ret = htu21_update_measurements(client); 96 + if (ret < 0) 97 + return ret; 98 + return sprintf(buf, "%d\n", htu21->temperature); 99 + } 100 + 101 + static ssize_t htu21_show_humidity(struct device *dev, 102 + struct device_attribute *attr, char *buf) 103 + { 104 + struct i2c_client *client = to_i2c_client(dev); 105 + struct htu21 *htu21 = i2c_get_clientdata(client); 106 + int ret = htu21_update_measurements(client); 107 + if (ret < 0) 108 + return ret; 109 + return sprintf(buf, "%d\n", htu21->humidity); 110 + } 111 + 112 + static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, 113 + htu21_show_temperature, NULL, 0); 114 + static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO, 115 + htu21_show_humidity, NULL, 0); 116 + 117 + static struct attribute *htu21_attributes[] = { 118 + &sensor_dev_attr_temp1_input.dev_attr.attr, 119 + &sensor_dev_attr_humidity1_input.dev_attr.attr, 120 + NULL 121 + }; 122 + 123 + static const struct attribute_group htu21_group = { 124 + .attrs = htu21_attributes, 125 + }; 126 + 127 + static int htu21_probe(struct i2c_client *client, 128 + const struct i2c_device_id *id) 129 + { 130 + struct htu21 *htu21; 131 + int err; 132 + 133 + if (!i2c_check_functionality(client->adapter, 134 + I2C_FUNC_SMBUS_READ_WORD_DATA)) { 135 + dev_err(&client->dev, 136 + "adapter does not support SMBus word transactions\n"); 137 + return -ENODEV; 138 + } 139 + 140 + htu21 = devm_kzalloc(&client->dev, sizeof(*htu21), GFP_KERNEL); 141 + if (!htu21) 142 + return -ENOMEM; 143 + 144 + i2c_set_clientdata(client, htu21); 145 + 146 + mutex_init(&htu21->lock); 147 + 148 + err = sysfs_create_group(&client->dev.kobj, &htu21_group); 149 + if (err) { 150 + dev_dbg(&client->dev, "could not create sysfs files\n"); 151 + return err; 152 + } 153 + htu21->hwmon_dev = hwmon_device_register(&client->dev); 154 + if (IS_ERR(htu21->hwmon_dev)) { 155 + dev_dbg(&client->dev, "unable to register hwmon device\n"); 156 + err = PTR_ERR(htu21->hwmon_dev); 157 + goto error; 158 + } 159 + 160 + dev_info(&client->dev, "initialized\n"); 161 + 162 + return 0; 163 + 164 + error: 165 + sysfs_remove_group(&client->dev.kobj, &htu21_group); 166 + return err; 167 + } 168 + 169 + static int htu21_remove(struct i2c_client *client) 170 + { 171 + struct htu21 *htu21 = i2c_get_clientdata(client); 172 + 173 + hwmon_device_unregister(htu21->hwmon_dev); 174 + sysfs_remove_group(&client->dev.kobj, &htu21_group); 175 + 176 + return 0; 177 + } 178 + 179 + static const struct i2c_device_id htu21_id[] = { 180 + { "htu21", 0 }, 181 + { } 182 + }; 183 + MODULE_DEVICE_TABLE(i2c, htu21_id); 184 + 185 + static struct i2c_driver htu21_driver = { 186 + .class = I2C_CLASS_HWMON, 187 + .driver = { 188 + .name = "htu21", 189 + }, 190 + .probe = htu21_probe, 191 + .remove = htu21_remove, 192 + .id_table = htu21_id, 193 + }; 194 + 195 + module_i2c_driver(htu21_driver); 196 + 197 + MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>"); 198 + MODULE_DESCRIPTION("MEAS HTU21D humidity and temperature sensor driver"); 199 + MODULE_LICENSE("GPL");