127f8b135SIain Paton /* Honeywell HIH-6130/HIH-6131 humidity and temperature sensor driver 227f8b135SIain Paton * 327f8b135SIain Paton * Copyright (C) 2012 Iain Paton <ipaton0@gmail.com> 427f8b135SIain Paton * 527f8b135SIain Paton * heavily based on the sht21 driver 627f8b135SIain Paton * Copyright (C) 2010 Urs Fleisch <urs.fleisch@sensirion.com> 727f8b135SIain Paton * 827f8b135SIain Paton * This program is free software; you can redistribute it and/or modify 927f8b135SIain Paton * it under the terms of the GNU General Public License as published by 1027f8b135SIain Paton * the Free Software Foundation; either version 2 of the License, or 1127f8b135SIain Paton * (at your option) any later version. 1227f8b135SIain Paton * 1327f8b135SIain Paton * This program is distributed in the hope that it will be useful, 1427f8b135SIain Paton * but WITHOUT ANY WARRANTY; without even the implied warranty of 1527f8b135SIain Paton * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1627f8b135SIain Paton * GNU General Public License for more details. 1727f8b135SIain Paton * 1827f8b135SIain Paton * You should have received a copy of the GNU General Public License 1927f8b135SIain Paton * along with this program; if not, write to the Free Software 2027f8b135SIain Paton * Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA 2127f8b135SIain Paton * 2227f8b135SIain Paton * Data sheets available (2012-06-22) at 2327f8b135SIain Paton * http://sensing.honeywell.com/index.php?ci_id=3106&la_id=1&defId=44872 2427f8b135SIain Paton */ 2527f8b135SIain Paton 2627f8b135SIain Paton #include <linux/module.h> 2727f8b135SIain Paton #include <linux/init.h> 2827f8b135SIain Paton #include <linux/slab.h> 2927f8b135SIain Paton #include <linux/i2c.h> 3027f8b135SIain Paton #include <linux/hwmon.h> 3127f8b135SIain Paton #include <linux/hwmon-sysfs.h> 3227f8b135SIain Paton #include <linux/err.h> 3327f8b135SIain Paton #include <linux/mutex.h> 3427f8b135SIain Paton #include <linux/device.h> 3527f8b135SIain Paton #include <linux/delay.h> 36dcd8f392SJean Delvare #include <linux/jiffies.h> 3727f8b135SIain Paton 3827f8b135SIain Paton /** 3927f8b135SIain Paton * struct hih6130 - HIH-6130 device specific data 4027f8b135SIain Paton * @hwmon_dev: device registered with hwmon 4127f8b135SIain Paton * @lock: mutex to protect measurement values 4227f8b135SIain Paton * @valid: only false before first measurement is taken 4327f8b135SIain Paton * @last_update: time of last update (jiffies) 4427f8b135SIain Paton * @temperature: cached temperature measurement value 4527f8b135SIain Paton * @humidity: cached humidity measurement value 46efabcc21SJosé Miguel Gonçalves * @write_length: length for I2C measurement request 4727f8b135SIain Paton */ 4827f8b135SIain Paton struct hih6130 { 49a5afc18cSAxel Lin struct i2c_client *client; 5027f8b135SIain Paton struct mutex lock; 5127f8b135SIain Paton bool valid; 5227f8b135SIain Paton unsigned long last_update; 5327f8b135SIain Paton int temperature; 5427f8b135SIain Paton int humidity; 55efabcc21SJosé Miguel Gonçalves size_t write_length; 5627f8b135SIain Paton }; 5727f8b135SIain Paton 5827f8b135SIain Paton /** 5927f8b135SIain Paton * hih6130_temp_ticks_to_millicelsius() - convert raw temperature ticks to 6027f8b135SIain Paton * milli celsius 6127f8b135SIain Paton * @ticks: temperature ticks value received from sensor 6227f8b135SIain Paton */ 6327f8b135SIain Paton static inline int hih6130_temp_ticks_to_millicelsius(int ticks) 6427f8b135SIain Paton { 6527f8b135SIain Paton ticks = ticks >> 2; 6627f8b135SIain Paton /* 6727f8b135SIain Paton * from data sheet section 5.0 6827f8b135SIain Paton * Formula T = ( ticks / ( 2^14 - 2 ) ) * 165 -40 6927f8b135SIain Paton */ 7027f8b135SIain Paton return (DIV_ROUND_CLOSEST(ticks * 1650, 16382) - 400) * 100; 7127f8b135SIain Paton } 7227f8b135SIain Paton 7327f8b135SIain Paton /** 7427f8b135SIain Paton * hih6130_rh_ticks_to_per_cent_mille() - convert raw humidity ticks to 7527f8b135SIain Paton * one-thousandths of a percent relative humidity 7627f8b135SIain Paton * @ticks: humidity ticks value received from sensor 7727f8b135SIain Paton */ 7827f8b135SIain Paton static inline int hih6130_rh_ticks_to_per_cent_mille(int ticks) 7927f8b135SIain Paton { 8027f8b135SIain Paton ticks &= ~0xC000; /* clear status bits */ 8127f8b135SIain Paton /* 8227f8b135SIain Paton * from data sheet section 4.0 8327f8b135SIain Paton * Formula RH = ( ticks / ( 2^14 -2 ) ) * 100 8427f8b135SIain Paton */ 8527f8b135SIain Paton return DIV_ROUND_CLOSEST(ticks * 1000, 16382) * 100; 8627f8b135SIain Paton } 8727f8b135SIain Paton 8827f8b135SIain Paton /** 8927f8b135SIain Paton * hih6130_update_measurements() - get updated measurements from device 90a5afc18cSAxel Lin * @dev: device 9127f8b135SIain Paton * 9227f8b135SIain Paton * Returns 0 on success, else negative errno. 9327f8b135SIain Paton */ 94a5afc18cSAxel Lin static int hih6130_update_measurements(struct device *dev) 9527f8b135SIain Paton { 96a5afc18cSAxel Lin struct hih6130 *hih6130 = dev_get_drvdata(dev); 97a5afc18cSAxel Lin struct i2c_client *client = hih6130->client; 9827f8b135SIain Paton int ret = 0; 9927f8b135SIain Paton int t; 10027f8b135SIain Paton unsigned char tmp[4]; 10127f8b135SIain Paton struct i2c_msg msgs[1] = { 10227f8b135SIain Paton { 10327f8b135SIain Paton .addr = client->addr, 10427f8b135SIain Paton .flags = I2C_M_RD, 10527f8b135SIain Paton .len = 4, 10627f8b135SIain Paton .buf = tmp, 10727f8b135SIain Paton } 10827f8b135SIain Paton }; 10927f8b135SIain Paton 11027f8b135SIain Paton mutex_lock(&hih6130->lock); 11127f8b135SIain Paton 11227f8b135SIain Paton /* 11327f8b135SIain Paton * While the measurement can be completed in ~40ms the sensor takes 11427f8b135SIain Paton * much longer to react to a change in external conditions. How quickly 11527f8b135SIain Paton * it reacts depends on airflow and other factors outwith our control. 11627f8b135SIain Paton * The datasheet specifies maximum 'Response time' for humidity at 8s 11727f8b135SIain Paton * and temperature at 30s under specified conditions. 11827f8b135SIain Paton * We therefore choose to only read the sensor at most once per second. 11927f8b135SIain Paton * This trades off pointless activity polling the sensor much faster 12027f8b135SIain Paton * than it can react against better response times in conditions more 12127f8b135SIain Paton * favourable than specified in the datasheet. 12227f8b135SIain Paton */ 12327f8b135SIain Paton if (time_after(jiffies, hih6130->last_update + HZ) || !hih6130->valid) { 12427f8b135SIain Paton 125efabcc21SJosé Miguel Gonçalves /* 126efabcc21SJosé Miguel Gonçalves * Write to slave address to request a measurement. 127efabcc21SJosé Miguel Gonçalves * According with the datasheet it should be with no data, but 128efabcc21SJosé Miguel Gonçalves * for systems with I2C bus drivers that do not allow zero 129efabcc21SJosé Miguel Gonçalves * length packets we write one dummy byte to allow sensor 130efabcc21SJosé Miguel Gonçalves * measurements on them. 131efabcc21SJosé Miguel Gonçalves */ 132efabcc21SJosé Miguel Gonçalves tmp[0] = 0; 133efabcc21SJosé Miguel Gonçalves ret = i2c_master_send(client, tmp, hih6130->write_length); 13427f8b135SIain Paton if (ret < 0) 13527f8b135SIain Paton goto out; 13627f8b135SIain Paton 13727f8b135SIain Paton /* measurement cycle time is ~36.65msec */ 13827f8b135SIain Paton msleep(40); 13927f8b135SIain Paton 14027f8b135SIain Paton ret = i2c_transfer(client->adapter, msgs, 1); 14127f8b135SIain Paton if (ret < 0) 14227f8b135SIain Paton goto out; 14327f8b135SIain Paton 14427f8b135SIain Paton if ((tmp[0] & 0xC0) != 0) { 14527f8b135SIain Paton dev_err(&client->dev, "Error while reading measurement result\n"); 14627f8b135SIain Paton ret = -EIO; 14727f8b135SIain Paton goto out; 14827f8b135SIain Paton } 14927f8b135SIain Paton 15027f8b135SIain Paton t = (tmp[0] << 8) + tmp[1]; 15127f8b135SIain Paton hih6130->humidity = hih6130_rh_ticks_to_per_cent_mille(t); 15227f8b135SIain Paton 15327f8b135SIain Paton t = (tmp[2] << 8) + tmp[3]; 15427f8b135SIain Paton hih6130->temperature = hih6130_temp_ticks_to_millicelsius(t); 15527f8b135SIain Paton 15627f8b135SIain Paton hih6130->last_update = jiffies; 15727f8b135SIain Paton hih6130->valid = true; 15827f8b135SIain Paton } 15927f8b135SIain Paton out: 16027f8b135SIain Paton mutex_unlock(&hih6130->lock); 16127f8b135SIain Paton 16227f8b135SIain Paton return ret >= 0 ? 0 : ret; 16327f8b135SIain Paton } 16427f8b135SIain Paton 16527f8b135SIain Paton /** 16627f8b135SIain Paton * hih6130_show_temperature() - show temperature measurement value in sysfs 16727f8b135SIain Paton * @dev: device 16827f8b135SIain Paton * @attr: device attribute 16927f8b135SIain Paton * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to 17027f8b135SIain Paton * 17127f8b135SIain Paton * Will be called on read access to temp1_input sysfs attribute. 17227f8b135SIain Paton * Returns number of bytes written into buffer, negative errno on error. 17327f8b135SIain Paton */ 17427f8b135SIain Paton static ssize_t hih6130_show_temperature(struct device *dev, 17527f8b135SIain Paton struct device_attribute *attr, 17627f8b135SIain Paton char *buf) 17727f8b135SIain Paton { 178a5afc18cSAxel Lin struct hih6130 *hih6130 = dev_get_drvdata(dev); 179a5afc18cSAxel Lin int ret; 180a5afc18cSAxel Lin 181a5afc18cSAxel Lin ret = hih6130_update_measurements(dev); 18227f8b135SIain Paton if (ret < 0) 18327f8b135SIain Paton return ret; 18427f8b135SIain Paton return sprintf(buf, "%d\n", hih6130->temperature); 18527f8b135SIain Paton } 18627f8b135SIain Paton 18727f8b135SIain Paton /** 18827f8b135SIain Paton * hih6130_show_humidity() - show humidity measurement value in sysfs 18927f8b135SIain Paton * @dev: device 19027f8b135SIain Paton * @attr: device attribute 19127f8b135SIain Paton * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to 19227f8b135SIain Paton * 19327f8b135SIain Paton * Will be called on read access to humidity1_input sysfs attribute. 19427f8b135SIain Paton * Returns number of bytes written into buffer, negative errno on error. 19527f8b135SIain Paton */ 19627f8b135SIain Paton static ssize_t hih6130_show_humidity(struct device *dev, 19727f8b135SIain Paton struct device_attribute *attr, char *buf) 19827f8b135SIain Paton { 199a5afc18cSAxel Lin struct hih6130 *hih6130 = dev_get_drvdata(dev); 200a5afc18cSAxel Lin int ret; 201a5afc18cSAxel Lin 202a5afc18cSAxel Lin ret = hih6130_update_measurements(dev); 20327f8b135SIain Paton if (ret < 0) 20427f8b135SIain Paton return ret; 20527f8b135SIain Paton return sprintf(buf, "%d\n", hih6130->humidity); 20627f8b135SIain Paton } 20727f8b135SIain Paton 20827f8b135SIain Paton /* sysfs attributes */ 20927f8b135SIain Paton static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, hih6130_show_temperature, 21027f8b135SIain Paton NULL, 0); 21127f8b135SIain Paton static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO, hih6130_show_humidity, 21227f8b135SIain Paton NULL, 0); 21327f8b135SIain Paton 214a5afc18cSAxel Lin static struct attribute *hih6130_attrs[] = { 21527f8b135SIain Paton &sensor_dev_attr_temp1_input.dev_attr.attr, 21627f8b135SIain Paton &sensor_dev_attr_humidity1_input.dev_attr.attr, 21727f8b135SIain Paton NULL 21827f8b135SIain Paton }; 21927f8b135SIain Paton 220a5afc18cSAxel Lin ATTRIBUTE_GROUPS(hih6130); 22127f8b135SIain Paton 2226c931ae1SBill Pemberton static int hih6130_probe(struct i2c_client *client, 22327f8b135SIain Paton const struct i2c_device_id *id) 22427f8b135SIain Paton { 225a5afc18cSAxel Lin struct device *dev = &client->dev; 22627f8b135SIain Paton struct hih6130 *hih6130; 227a5afc18cSAxel Lin struct device *hwmon_dev; 22827f8b135SIain Paton 22927f8b135SIain Paton if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { 23027f8b135SIain Paton dev_err(&client->dev, "adapter does not support true I2C\n"); 23127f8b135SIain Paton return -ENODEV; 23227f8b135SIain Paton } 23327f8b135SIain Paton 234a5afc18cSAxel Lin hih6130 = devm_kzalloc(dev, sizeof(*hih6130), GFP_KERNEL); 23527f8b135SIain Paton if (!hih6130) 23627f8b135SIain Paton return -ENOMEM; 23727f8b135SIain Paton 238a5afc18cSAxel Lin hih6130->client = client; 23927f8b135SIain Paton mutex_init(&hih6130->lock); 24027f8b135SIain Paton 241eeeafd38SAxel Lin if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_QUICK)) 242eeeafd38SAxel Lin hih6130->write_length = 1; 243eeeafd38SAxel Lin 244a5afc18cSAxel Lin hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, 245a5afc18cSAxel Lin hih6130, 246a5afc18cSAxel Lin hih6130_groups); 247a5afc18cSAxel Lin return PTR_ERR_OR_ZERO(hwmon_dev); 24827f8b135SIain Paton } 24927f8b135SIain Paton 25027f8b135SIain Paton /* Device ID table */ 25127f8b135SIain Paton static const struct i2c_device_id hih6130_id[] = { 25227f8b135SIain Paton { "hih6130", 0 }, 25327f8b135SIain Paton { } 25427f8b135SIain Paton }; 25527f8b135SIain Paton MODULE_DEVICE_TABLE(i2c, hih6130_id); 25627f8b135SIain Paton 25727f8b135SIain Paton static struct i2c_driver hih6130_driver = { 25827f8b135SIain Paton .driver.name = "hih6130", 25927f8b135SIain Paton .probe = hih6130_probe, 26027f8b135SIain Paton .id_table = hih6130_id, 26127f8b135SIain Paton }; 26227f8b135SIain Paton 26327f8b135SIain Paton module_i2c_driver(hih6130_driver); 26427f8b135SIain Paton 26527f8b135SIain Paton MODULE_AUTHOR("Iain Paton <ipaton0@gmail.com>"); 26627f8b135SIain Paton MODULE_DESCRIPTION("Honeywell HIH-6130 humidity and temperature sensor driver"); 26727f8b135SIain Paton MODULE_LICENSE("GPL"); 268