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 33struct htu21 { 34 struct i2c_client *client; 35 struct mutex lock; 36 bool valid; 37 unsigned long last_update; 38 int temperature; 39 int humidity; 40}; 41 42static 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 52static 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 62static int htu21_update_measurements(struct device *dev) 63{ 64 struct htu21 *htu21 = dev_get_drvdata(dev); 65 struct i2c_client *client = htu21->client; 66 int ret = 0; 67 68 mutex_lock(&htu21->lock); 69 70 if (time_after(jiffies, htu21->last_update + HZ / 2) || 71 !htu21->valid) { 72 ret = i2c_smbus_read_word_swapped(client, 73 HTU21_T_MEASUREMENT_HM); 74 if (ret < 0) 75 goto out; 76 htu21->temperature = htu21_temp_ticks_to_millicelsius(ret); 77 ret = i2c_smbus_read_word_swapped(client, 78 HTU21_RH_MEASUREMENT_HM); 79 if (ret < 0) 80 goto out; 81 htu21->humidity = htu21_rh_ticks_to_per_cent_mille(ret); 82 htu21->last_update = jiffies; 83 htu21->valid = true; 84 } 85out: 86 mutex_unlock(&htu21->lock); 87 88 return ret >= 0 ? 0 : ret; 89} 90 91static ssize_t htu21_show_temperature(struct device *dev, 92 struct device_attribute *attr, char *buf) 93{ 94 struct htu21 *htu21 = dev_get_drvdata(dev); 95 int ret; 96 97 ret = htu21_update_measurements(dev); 98 if (ret < 0) 99 return ret; 100 return sprintf(buf, "%d\n", htu21->temperature); 101} 102 103static ssize_t htu21_show_humidity(struct device *dev, 104 struct device_attribute *attr, char *buf) 105{ 106 struct htu21 *htu21 = dev_get_drvdata(dev); 107 int ret; 108 109 ret = htu21_update_measurements(dev); 110 if (ret < 0) 111 return ret; 112 return sprintf(buf, "%d\n", htu21->humidity); 113} 114 115static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, 116 htu21_show_temperature, NULL, 0); 117static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO, 118 htu21_show_humidity, NULL, 0); 119 120static struct attribute *htu21_attrs[] = { 121 &sensor_dev_attr_temp1_input.dev_attr.attr, 122 &sensor_dev_attr_humidity1_input.dev_attr.attr, 123 NULL 124}; 125 126ATTRIBUTE_GROUPS(htu21); 127 128static int htu21_probe(struct i2c_client *client, 129 const struct i2c_device_id *id) 130{ 131 struct device *dev = &client->dev; 132 struct htu21 *htu21; 133 struct device *hwmon_dev; 134 135 if (!i2c_check_functionality(client->adapter, 136 I2C_FUNC_SMBUS_READ_WORD_DATA)) { 137 dev_err(&client->dev, 138 "adapter does not support SMBus word transactions\n"); 139 return -ENODEV; 140 } 141 142 htu21 = devm_kzalloc(dev, sizeof(*htu21), GFP_KERNEL); 143 if (!htu21) 144 return -ENOMEM; 145 146 htu21->client = client; 147 mutex_init(&htu21->lock); 148 149 hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 150 htu21, 151 htu21_groups); 152 return PTR_ERR_OR_ZERO(hwmon_dev); 153} 154 155static const struct i2c_device_id htu21_id[] = { 156 { "htu21", 0 }, 157 { } 158}; 159MODULE_DEVICE_TABLE(i2c, htu21_id); 160 161static struct i2c_driver htu21_driver = { 162 .class = I2C_CLASS_HWMON, 163 .driver = { 164 .name = "htu21", 165 }, 166 .probe = htu21_probe, 167 .id_table = htu21_id, 168}; 169 170module_i2c_driver(htu21_driver); 171 172MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>"); 173MODULE_DESCRIPTION("MEAS HTU21D humidity and temperature sensor driver"); 174MODULE_LICENSE("GPL"); 175