xref: /openbmc/linux/drivers/hwmon/hih6130.c (revision a5afc18c)
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 
241a5afc18cSAxel Lin 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
242a5afc18cSAxel Lin 							   hih6130,
243a5afc18cSAxel Lin 							   hih6130_groups);
244a5afc18cSAxel Lin 	return PTR_ERR_OR_ZERO(hwmon_dev);
24527f8b135SIain Paton }
24627f8b135SIain Paton 
24727f8b135SIain Paton /* Device ID table */
24827f8b135SIain Paton static const struct i2c_device_id hih6130_id[] = {
24927f8b135SIain Paton 	{ "hih6130", 0 },
25027f8b135SIain Paton 	{ }
25127f8b135SIain Paton };
25227f8b135SIain Paton MODULE_DEVICE_TABLE(i2c, hih6130_id);
25327f8b135SIain Paton 
25427f8b135SIain Paton static struct i2c_driver hih6130_driver = {
25527f8b135SIain Paton 	.driver.name = "hih6130",
25627f8b135SIain Paton 	.probe       = hih6130_probe,
25727f8b135SIain Paton 	.id_table    = hih6130_id,
25827f8b135SIain Paton };
25927f8b135SIain Paton 
26027f8b135SIain Paton module_i2c_driver(hih6130_driver);
26127f8b135SIain Paton 
26227f8b135SIain Paton MODULE_AUTHOR("Iain Paton <ipaton0@gmail.com>");
26327f8b135SIain Paton MODULE_DESCRIPTION("Honeywell HIH-6130 humidity and temperature sensor driver");
26427f8b135SIain Paton MODULE_LICENSE("GPL");
265