xref: /linux/drivers/hwmon/hih6130.c (revision 621cde16e49b3ecf7d59a8106a20aaebfb4a59a9)
1fd534e9bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
227f8b135SIain Paton /* Honeywell HIH-6130/HIH-6131 humidity and temperature sensor driver
327f8b135SIain Paton  *
427f8b135SIain Paton  * Copyright (C) 2012 Iain Paton <ipaton0@gmail.com>
527f8b135SIain Paton  *
627f8b135SIain Paton  * heavily based on the sht21 driver
727f8b135SIain Paton  * Copyright (C) 2010 Urs Fleisch <urs.fleisch@sensirion.com>
827f8b135SIain Paton  *
927f8b135SIain Paton  * Data sheets available (2012-06-22) at
1027f8b135SIain Paton  * http://sensing.honeywell.com/index.php?ci_id=3106&la_id=1&defId=44872
1127f8b135SIain Paton  */
1227f8b135SIain Paton 
1327f8b135SIain Paton #include <linux/module.h>
1427f8b135SIain Paton #include <linux/init.h>
1527f8b135SIain Paton #include <linux/slab.h>
1627f8b135SIain Paton #include <linux/i2c.h>
1727f8b135SIain Paton #include <linux/hwmon.h>
1827f8b135SIain Paton #include <linux/hwmon-sysfs.h>
1927f8b135SIain Paton #include <linux/err.h>
2027f8b135SIain Paton #include <linux/mutex.h>
2127f8b135SIain Paton #include <linux/device.h>
2227f8b135SIain Paton #include <linux/delay.h>
23dcd8f392SJean Delvare #include <linux/jiffies.h>
2427f8b135SIain Paton 
2527f8b135SIain Paton /**
2627f8b135SIain Paton  * struct hih6130 - HIH-6130 device specific data
27a7a9b15aSGuenter Roeck  * @client: pointer to I2C client device
2827f8b135SIain Paton  * @lock: mutex to protect measurement values
2927f8b135SIain Paton  * @valid: only false before first measurement is taken
3027f8b135SIain Paton  * @last_update: time of last update (jiffies)
3127f8b135SIain Paton  * @temperature: cached temperature measurement value
3227f8b135SIain Paton  * @humidity: cached humidity measurement value
33efabcc21SJosé Miguel Gonçalves  * @write_length: length for I2C measurement request
3427f8b135SIain Paton  */
3527f8b135SIain Paton struct hih6130 {
36a5afc18cSAxel Lin 	struct i2c_client *client;
3727f8b135SIain Paton 	struct mutex lock;
3827f8b135SIain Paton 	bool valid;
3927f8b135SIain Paton 	unsigned long last_update;
4027f8b135SIain Paton 	int temperature;
4127f8b135SIain Paton 	int humidity;
42efabcc21SJosé Miguel Gonçalves 	size_t write_length;
4327f8b135SIain Paton };
4427f8b135SIain Paton 
4527f8b135SIain Paton /**
4627f8b135SIain Paton  * hih6130_temp_ticks_to_millicelsius() - convert raw temperature ticks to
4727f8b135SIain Paton  * milli celsius
4827f8b135SIain Paton  * @ticks: temperature ticks value received from sensor
4927f8b135SIain Paton  */
hih6130_temp_ticks_to_millicelsius(int ticks)5027f8b135SIain Paton static inline int hih6130_temp_ticks_to_millicelsius(int ticks)
5127f8b135SIain Paton {
5227f8b135SIain Paton 	ticks = ticks >> 2;
5327f8b135SIain Paton 	/*
5427f8b135SIain Paton 	 * from data sheet section 5.0
5527f8b135SIain Paton 	 * Formula T = ( ticks / ( 2^14 - 2 ) ) * 165 -40
5627f8b135SIain Paton 	 */
5727f8b135SIain Paton 	return (DIV_ROUND_CLOSEST(ticks * 1650, 16382) - 400) * 100;
5827f8b135SIain Paton }
5927f8b135SIain Paton 
6027f8b135SIain Paton /**
6127f8b135SIain Paton  * hih6130_rh_ticks_to_per_cent_mille() - convert raw humidity ticks to
6227f8b135SIain Paton  * one-thousandths of a percent relative humidity
6327f8b135SIain Paton  * @ticks: humidity ticks value received from sensor
6427f8b135SIain Paton  */
hih6130_rh_ticks_to_per_cent_mille(int ticks)6527f8b135SIain Paton static inline int hih6130_rh_ticks_to_per_cent_mille(int ticks)
6627f8b135SIain Paton {
6727f8b135SIain Paton 	ticks &= ~0xC000; /* clear status bits */
6827f8b135SIain Paton 	/*
6927f8b135SIain Paton 	 * from data sheet section 4.0
7027f8b135SIain Paton 	 * Formula RH = ( ticks / ( 2^14 -2 ) ) * 100
7127f8b135SIain Paton 	 */
7227f8b135SIain Paton 	return DIV_ROUND_CLOSEST(ticks * 1000, 16382) * 100;
7327f8b135SIain Paton }
7427f8b135SIain Paton 
7527f8b135SIain Paton /**
7627f8b135SIain Paton  * hih6130_update_measurements() - get updated measurements from device
77a5afc18cSAxel Lin  * @dev: device
7827f8b135SIain Paton  *
7927f8b135SIain Paton  * Returns 0 on success, else negative errno.
8027f8b135SIain Paton  */
hih6130_update_measurements(struct device * dev)81a5afc18cSAxel Lin static int hih6130_update_measurements(struct device *dev)
8227f8b135SIain Paton {
83a5afc18cSAxel Lin 	struct hih6130 *hih6130 = dev_get_drvdata(dev);
84a5afc18cSAxel Lin 	struct i2c_client *client = hih6130->client;
8527f8b135SIain Paton 	int ret = 0;
8627f8b135SIain Paton 	int t;
8727f8b135SIain Paton 	unsigned char tmp[4];
8827f8b135SIain Paton 	struct i2c_msg msgs[1] = {
8927f8b135SIain Paton 		{
9027f8b135SIain Paton 			.addr = client->addr,
9127f8b135SIain Paton 			.flags = I2C_M_RD,
9227f8b135SIain Paton 			.len = 4,
9327f8b135SIain Paton 			.buf = tmp,
9427f8b135SIain Paton 		}
9527f8b135SIain Paton 	};
9627f8b135SIain Paton 
9727f8b135SIain Paton 	mutex_lock(&hih6130->lock);
9827f8b135SIain Paton 
9927f8b135SIain Paton 	/*
10027f8b135SIain Paton 	 * While the measurement can be completed in ~40ms the sensor takes
10127f8b135SIain Paton 	 * much longer to react to a change in external conditions. How quickly
10227f8b135SIain Paton 	 * it reacts depends on airflow and other factors outwith our control.
10327f8b135SIain Paton 	 * The datasheet specifies maximum 'Response time' for humidity at 8s
10427f8b135SIain Paton 	 * and temperature at 30s under specified conditions.
10527f8b135SIain Paton 	 * We therefore choose to only read the sensor at most once per second.
10627f8b135SIain Paton 	 * This trades off pointless activity polling the sensor much faster
10727f8b135SIain Paton 	 * than it can react against better response times in conditions more
10827f8b135SIain Paton 	 * favourable than specified in the datasheet.
10927f8b135SIain Paton 	 */
11027f8b135SIain Paton 	if (time_after(jiffies, hih6130->last_update + HZ) || !hih6130->valid) {
11127f8b135SIain Paton 
112efabcc21SJosé Miguel Gonçalves 		/*
113efabcc21SJosé Miguel Gonçalves 		 * Write to slave address to request a measurement.
114efabcc21SJosé Miguel Gonçalves 		 * According with the datasheet it should be with no data, but
115efabcc21SJosé Miguel Gonçalves 		 * for systems with I2C bus drivers that do not allow zero
116efabcc21SJosé Miguel Gonçalves 		 * length packets we write one dummy byte to allow sensor
117efabcc21SJosé Miguel Gonçalves 		 * measurements on them.
118efabcc21SJosé Miguel Gonçalves 		 */
119efabcc21SJosé Miguel Gonçalves 		tmp[0] = 0;
120efabcc21SJosé Miguel Gonçalves 		ret = i2c_master_send(client, tmp, hih6130->write_length);
12127f8b135SIain Paton 		if (ret < 0)
12227f8b135SIain Paton 			goto out;
12327f8b135SIain Paton 
12427f8b135SIain Paton 		/* measurement cycle time is ~36.65msec */
12527f8b135SIain Paton 		msleep(40);
12627f8b135SIain Paton 
12727f8b135SIain Paton 		ret = i2c_transfer(client->adapter, msgs, 1);
12827f8b135SIain Paton 		if (ret < 0)
12927f8b135SIain Paton 			goto out;
13027f8b135SIain Paton 
13127f8b135SIain Paton 		if ((tmp[0] & 0xC0) != 0) {
13227f8b135SIain Paton 			dev_err(&client->dev, "Error while reading measurement result\n");
13327f8b135SIain Paton 			ret = -EIO;
13427f8b135SIain Paton 			goto out;
13527f8b135SIain Paton 		}
13627f8b135SIain Paton 
13727f8b135SIain Paton 		t = (tmp[0] << 8) + tmp[1];
13827f8b135SIain Paton 		hih6130->humidity = hih6130_rh_ticks_to_per_cent_mille(t);
13927f8b135SIain Paton 
14027f8b135SIain Paton 		t = (tmp[2] << 8) + tmp[3];
14127f8b135SIain Paton 		hih6130->temperature = hih6130_temp_ticks_to_millicelsius(t);
14227f8b135SIain Paton 
14327f8b135SIain Paton 		hih6130->last_update = jiffies;
14427f8b135SIain Paton 		hih6130->valid = true;
14527f8b135SIain Paton 	}
14627f8b135SIain Paton out:
14727f8b135SIain Paton 	mutex_unlock(&hih6130->lock);
14827f8b135SIain Paton 
14927f8b135SIain Paton 	return ret >= 0 ? 0 : ret;
15027f8b135SIain Paton }
15127f8b135SIain Paton 
15227f8b135SIain Paton /**
1530be688d0SRandy Dunlap  * hih6130_temperature_show() - show temperature measurement value in sysfs
15427f8b135SIain Paton  * @dev: device
15527f8b135SIain Paton  * @attr: device attribute
15627f8b135SIain Paton  * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to
15727f8b135SIain Paton  *
15827f8b135SIain Paton  * Will be called on read access to temp1_input sysfs attribute.
15927f8b135SIain Paton  * Returns number of bytes written into buffer, negative errno on error.
16027f8b135SIain Paton  */
hih6130_temperature_show(struct device * dev,struct device_attribute * attr,char * buf)1611640bb59SGuenter Roeck static ssize_t hih6130_temperature_show(struct device *dev,
16227f8b135SIain Paton 					struct device_attribute *attr,
16327f8b135SIain Paton 					char *buf)
16427f8b135SIain Paton {
165a5afc18cSAxel Lin 	struct hih6130 *hih6130 = dev_get_drvdata(dev);
166a5afc18cSAxel Lin 	int ret;
167a5afc18cSAxel Lin 
168a5afc18cSAxel Lin 	ret = hih6130_update_measurements(dev);
16927f8b135SIain Paton 	if (ret < 0)
17027f8b135SIain Paton 		return ret;
17127f8b135SIain Paton 	return sprintf(buf, "%d\n", hih6130->temperature);
17227f8b135SIain Paton }
17327f8b135SIain Paton 
17427f8b135SIain Paton /**
1750be688d0SRandy Dunlap  * hih6130_humidity_show() - show humidity measurement value in sysfs
17627f8b135SIain Paton  * @dev: device
17727f8b135SIain Paton  * @attr: device attribute
17827f8b135SIain Paton  * @buf: sysfs buffer (PAGE_SIZE) where measurement values are written to
17927f8b135SIain Paton  *
18027f8b135SIain Paton  * Will be called on read access to humidity1_input sysfs attribute.
18127f8b135SIain Paton  * Returns number of bytes written into buffer, negative errno on error.
18227f8b135SIain Paton  */
hih6130_humidity_show(struct device * dev,struct device_attribute * attr,char * buf)1831640bb59SGuenter Roeck static ssize_t hih6130_humidity_show(struct device *dev,
18427f8b135SIain Paton 				     struct device_attribute *attr, char *buf)
18527f8b135SIain Paton {
186a5afc18cSAxel Lin 	struct hih6130 *hih6130 = dev_get_drvdata(dev);
187a5afc18cSAxel Lin 	int ret;
188a5afc18cSAxel Lin 
189a5afc18cSAxel Lin 	ret = hih6130_update_measurements(dev);
19027f8b135SIain Paton 	if (ret < 0)
19127f8b135SIain Paton 		return ret;
19227f8b135SIain Paton 	return sprintf(buf, "%d\n", hih6130->humidity);
19327f8b135SIain Paton }
19427f8b135SIain Paton 
19527f8b135SIain Paton /* sysfs attributes */
1961640bb59SGuenter Roeck static SENSOR_DEVICE_ATTR_RO(temp1_input, hih6130_temperature, 0);
1971640bb59SGuenter Roeck static SENSOR_DEVICE_ATTR_RO(humidity1_input, hih6130_humidity, 0);
19827f8b135SIain Paton 
199a5afc18cSAxel Lin static struct attribute *hih6130_attrs[] = {
20027f8b135SIain Paton 	&sensor_dev_attr_temp1_input.dev_attr.attr,
20127f8b135SIain Paton 	&sensor_dev_attr_humidity1_input.dev_attr.attr,
20227f8b135SIain Paton 	NULL
20327f8b135SIain Paton };
20427f8b135SIain Paton 
205a5afc18cSAxel Lin ATTRIBUTE_GROUPS(hih6130);
20627f8b135SIain Paton 
hih6130_probe(struct i2c_client * client)20767487038SStephen Kitt static int hih6130_probe(struct i2c_client *client)
20827f8b135SIain Paton {
209a5afc18cSAxel Lin 	struct device *dev = &client->dev;
21027f8b135SIain Paton 	struct hih6130 *hih6130;
211a5afc18cSAxel Lin 	struct device *hwmon_dev;
21227f8b135SIain Paton 
21327f8b135SIain Paton 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
21427f8b135SIain Paton 		dev_err(&client->dev, "adapter does not support true I2C\n");
21527f8b135SIain Paton 		return -ENODEV;
21627f8b135SIain Paton 	}
21727f8b135SIain Paton 
218a5afc18cSAxel Lin 	hih6130 = devm_kzalloc(dev, sizeof(*hih6130), GFP_KERNEL);
21927f8b135SIain Paton 	if (!hih6130)
22027f8b135SIain Paton 		return -ENOMEM;
22127f8b135SIain Paton 
222a5afc18cSAxel Lin 	hih6130->client = client;
22327f8b135SIain Paton 	mutex_init(&hih6130->lock);
22427f8b135SIain Paton 
225eeeafd38SAxel Lin 	if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_QUICK))
226eeeafd38SAxel Lin 		hih6130->write_length = 1;
227eeeafd38SAxel Lin 
228a5afc18cSAxel Lin 	hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name,
229a5afc18cSAxel Lin 							   hih6130,
230a5afc18cSAxel Lin 							   hih6130_groups);
231a5afc18cSAxel Lin 	return PTR_ERR_OR_ZERO(hwmon_dev);
23227f8b135SIain Paton }
23327f8b135SIain Paton 
23427f8b135SIain Paton /* Device ID table */
23527f8b135SIain Paton static const struct i2c_device_id hih6130_id[] = {
236*d8a66f36SUwe Kleine-König 	{ "hih6130" },
23727f8b135SIain Paton 	{ }
23827f8b135SIain Paton };
23927f8b135SIain Paton MODULE_DEVICE_TABLE(i2c, hih6130_id);
24027f8b135SIain Paton 
241a62fe340SGuenter Roeck static const struct of_device_id __maybe_unused hih6130_of_match[] = {
24289bff8c2SAndreas Kemnade 	{ .compatible = "honeywell,hih6130", },
24389bff8c2SAndreas Kemnade 	{ }
24489bff8c2SAndreas Kemnade };
24589bff8c2SAndreas Kemnade MODULE_DEVICE_TABLE(of, hih6130_of_match);
24689bff8c2SAndreas Kemnade 
24727f8b135SIain Paton static struct i2c_driver hih6130_driver = {
24889bff8c2SAndreas Kemnade 	.driver = {
24989bff8c2SAndreas Kemnade 		.name = "hih6130",
25089bff8c2SAndreas Kemnade 		.of_match_table = of_match_ptr(hih6130_of_match),
25189bff8c2SAndreas Kemnade 	},
2521975d167SUwe Kleine-König 	.probe       = hih6130_probe,
25327f8b135SIain Paton 	.id_table    = hih6130_id,
25427f8b135SIain Paton };
25527f8b135SIain Paton 
25627f8b135SIain Paton module_i2c_driver(hih6130_driver);
25727f8b135SIain Paton 
25827f8b135SIain Paton MODULE_AUTHOR("Iain Paton <ipaton0@gmail.com>");
25927f8b135SIain Paton MODULE_DESCRIPTION("Honeywell HIH-6130 humidity and temperature sensor driver");
26027f8b135SIain Paton MODULE_LICENSE("GPL");
261