18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* Sensirion SHTC1 humidity and temperature sensor driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2014 Sensirion AG, Switzerland
58c2ecf20Sopenharmony_ci * Author: Johannes Winkelmann <johannes.winkelmann@sensirion.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/module.h>
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/i2c.h>
128c2ecf20Sopenharmony_ci#include <linux/hwmon.h>
138c2ecf20Sopenharmony_ci#include <linux/hwmon-sysfs.h>
148c2ecf20Sopenharmony_ci#include <linux/err.h>
158c2ecf20Sopenharmony_ci#include <linux/delay.h>
168c2ecf20Sopenharmony_ci#include <linux/platform_data/shtc1.h>
178c2ecf20Sopenharmony_ci#include <linux/of.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* commands (high precision mode) */
208c2ecf20Sopenharmony_cistatic const unsigned char shtc1_cmd_measure_blocking_hpm[]    = { 0x7C, 0xA2 };
218c2ecf20Sopenharmony_cistatic const unsigned char shtc1_cmd_measure_nonblocking_hpm[] = { 0x78, 0x66 };
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci/* commands (low precision mode) */
248c2ecf20Sopenharmony_cistatic const unsigned char shtc1_cmd_measure_blocking_lpm[]    = { 0x64, 0x58 };
258c2ecf20Sopenharmony_cistatic const unsigned char shtc1_cmd_measure_nonblocking_lpm[] = { 0x60, 0x9c };
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci/* command for reading the ID register */
288c2ecf20Sopenharmony_cistatic const unsigned char shtc1_cmd_read_id_reg[]             = { 0xef, 0xc8 };
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/*
318c2ecf20Sopenharmony_ci * constants for reading the ID register
328c2ecf20Sopenharmony_ci * SHTC1: 0x0007 with mask 0x003f
338c2ecf20Sopenharmony_ci * SHTW1: 0x0007 with mask 0x003f
348c2ecf20Sopenharmony_ci * SHTC3: 0x0807 with mask 0x083f
358c2ecf20Sopenharmony_ci */
368c2ecf20Sopenharmony_ci#define SHTC3_ID      0x0807
378c2ecf20Sopenharmony_ci#define SHTC3_ID_MASK 0x083f
388c2ecf20Sopenharmony_ci#define SHTC1_ID      0x0007
398c2ecf20Sopenharmony_ci#define SHTC1_ID_MASK 0x003f
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci/* delays for non-blocking i2c commands, both in us */
428c2ecf20Sopenharmony_ci#define SHTC1_NONBLOCKING_WAIT_TIME_HPM  14400
438c2ecf20Sopenharmony_ci#define SHTC1_NONBLOCKING_WAIT_TIME_LPM   1000
448c2ecf20Sopenharmony_ci#define SHTC3_NONBLOCKING_WAIT_TIME_HPM  12100
458c2ecf20Sopenharmony_ci#define SHTC3_NONBLOCKING_WAIT_TIME_LPM    800
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci#define SHTC1_CMD_LENGTH      2
488c2ecf20Sopenharmony_ci#define SHTC1_RESPONSE_LENGTH 6
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_cienum shtcx_chips {
518c2ecf20Sopenharmony_ci	shtc1,
528c2ecf20Sopenharmony_ci	shtc3,
538c2ecf20Sopenharmony_ci};
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistruct shtc1_data {
568c2ecf20Sopenharmony_ci	struct i2c_client *client;
578c2ecf20Sopenharmony_ci	struct mutex update_lock;
588c2ecf20Sopenharmony_ci	bool valid;
598c2ecf20Sopenharmony_ci	unsigned long last_updated; /* in jiffies */
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	const unsigned char *command;
628c2ecf20Sopenharmony_ci	unsigned int nonblocking_wait_time; /* in us */
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	struct shtc1_platform_data setup;
658c2ecf20Sopenharmony_ci	enum shtcx_chips chip;
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	int temperature; /* 1000 * temperature in dgr C */
688c2ecf20Sopenharmony_ci	int humidity; /* 1000 * relative humidity in %RH */
698c2ecf20Sopenharmony_ci};
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_cistatic int shtc1_update_values(struct i2c_client *client,
728c2ecf20Sopenharmony_ci			       struct shtc1_data *data,
738c2ecf20Sopenharmony_ci			       char *buf, int bufsize)
748c2ecf20Sopenharmony_ci{
758c2ecf20Sopenharmony_ci	int ret = i2c_master_send(client, data->command, SHTC1_CMD_LENGTH);
768c2ecf20Sopenharmony_ci	if (ret != SHTC1_CMD_LENGTH) {
778c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed to send command: %d\n", ret);
788c2ecf20Sopenharmony_ci		return ret < 0 ? ret : -EIO;
798c2ecf20Sopenharmony_ci	}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_ci	/*
828c2ecf20Sopenharmony_ci	 * In blocking mode (clock stretching mode) the I2C bus
838c2ecf20Sopenharmony_ci	 * is blocked for other traffic, thus the call to i2c_master_recv()
848c2ecf20Sopenharmony_ci	 * will wait until the data is ready. For non blocking mode, we
858c2ecf20Sopenharmony_ci	 * have to wait ourselves.
868c2ecf20Sopenharmony_ci	 */
878c2ecf20Sopenharmony_ci	if (!data->setup.blocking_io)
888c2ecf20Sopenharmony_ci		usleep_range(data->nonblocking_wait_time,
898c2ecf20Sopenharmony_ci			     data->nonblocking_wait_time + 1000);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	ret = i2c_master_recv(client, buf, bufsize);
928c2ecf20Sopenharmony_ci	if (ret != bufsize) {
938c2ecf20Sopenharmony_ci		dev_err(&client->dev, "failed to read values: %d\n", ret);
948c2ecf20Sopenharmony_ci		return ret < 0 ? ret : -EIO;
958c2ecf20Sopenharmony_ci	}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	return 0;
988c2ecf20Sopenharmony_ci}
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci/* sysfs attributes */
1018c2ecf20Sopenharmony_cistatic struct shtc1_data *shtc1_update_client(struct device *dev)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct shtc1_data *data = dev_get_drvdata(dev);
1048c2ecf20Sopenharmony_ci	struct i2c_client *client = data->client;
1058c2ecf20Sopenharmony_ci	unsigned char buf[SHTC1_RESPONSE_LENGTH];
1068c2ecf20Sopenharmony_ci	int val;
1078c2ecf20Sopenharmony_ci	int ret = 0;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	mutex_lock(&data->update_lock);
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci	if (time_after(jiffies, data->last_updated + HZ / 10) || !data->valid) {
1128c2ecf20Sopenharmony_ci		ret = shtc1_update_values(client, data, buf, sizeof(buf));
1138c2ecf20Sopenharmony_ci		if (ret)
1148c2ecf20Sopenharmony_ci			goto out;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci		/*
1178c2ecf20Sopenharmony_ci		 * From datasheet:
1188c2ecf20Sopenharmony_ci		 * T = -45 + 175 * ST / 2^16
1198c2ecf20Sopenharmony_ci		 * RH = 100 * SRH / 2^16
1208c2ecf20Sopenharmony_ci		 *
1218c2ecf20Sopenharmony_ci		 * Adapted for integer fixed point (3 digit) arithmetic.
1228c2ecf20Sopenharmony_ci		 */
1238c2ecf20Sopenharmony_ci		val = be16_to_cpup((__be16 *)buf);
1248c2ecf20Sopenharmony_ci		data->temperature = ((21875 * val) >> 13) - 45000;
1258c2ecf20Sopenharmony_ci		val = be16_to_cpup((__be16 *)(buf + 3));
1268c2ecf20Sopenharmony_ci		data->humidity = ((12500 * val) >> 13);
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		data->last_updated = jiffies;
1298c2ecf20Sopenharmony_ci		data->valid = true;
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ciout:
1338c2ecf20Sopenharmony_ci	mutex_unlock(&data->update_lock);
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	return ret == 0 ? data : ERR_PTR(ret);
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic ssize_t temp1_input_show(struct device *dev,
1398c2ecf20Sopenharmony_ci				struct device_attribute *attr,
1408c2ecf20Sopenharmony_ci				char *buf)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	struct shtc1_data *data = shtc1_update_client(dev);
1438c2ecf20Sopenharmony_ci	if (IS_ERR(data))
1448c2ecf20Sopenharmony_ci		return PTR_ERR(data);
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", data->temperature);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic ssize_t humidity1_input_show(struct device *dev,
1508c2ecf20Sopenharmony_ci				    struct device_attribute *attr, char *buf)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	struct shtc1_data *data = shtc1_update_client(dev);
1538c2ecf20Sopenharmony_ci	if (IS_ERR(data))
1548c2ecf20Sopenharmony_ci		return PTR_ERR(data);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	return sprintf(buf, "%d\n", data->humidity);
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(temp1_input);
1608c2ecf20Sopenharmony_cistatic DEVICE_ATTR_RO(humidity1_input);
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic struct attribute *shtc1_attrs[] = {
1638c2ecf20Sopenharmony_ci	&dev_attr_temp1_input.attr,
1648c2ecf20Sopenharmony_ci	&dev_attr_humidity1_input.attr,
1658c2ecf20Sopenharmony_ci	NULL
1668c2ecf20Sopenharmony_ci};
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(shtc1);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic void shtc1_select_command(struct shtc1_data *data)
1718c2ecf20Sopenharmony_ci{
1728c2ecf20Sopenharmony_ci	if (data->setup.high_precision) {
1738c2ecf20Sopenharmony_ci		data->command = data->setup.blocking_io ?
1748c2ecf20Sopenharmony_ci				shtc1_cmd_measure_blocking_hpm :
1758c2ecf20Sopenharmony_ci				shtc1_cmd_measure_nonblocking_hpm;
1768c2ecf20Sopenharmony_ci		data->nonblocking_wait_time = (data->chip == shtc1) ?
1778c2ecf20Sopenharmony_ci				SHTC1_NONBLOCKING_WAIT_TIME_HPM :
1788c2ecf20Sopenharmony_ci				SHTC3_NONBLOCKING_WAIT_TIME_HPM;
1798c2ecf20Sopenharmony_ci	} else {
1808c2ecf20Sopenharmony_ci		data->command = data->setup.blocking_io ?
1818c2ecf20Sopenharmony_ci				shtc1_cmd_measure_blocking_lpm :
1828c2ecf20Sopenharmony_ci				shtc1_cmd_measure_nonblocking_lpm;
1838c2ecf20Sopenharmony_ci		data->nonblocking_wait_time = (data->chip == shtc1) ?
1848c2ecf20Sopenharmony_ci				SHTC1_NONBLOCKING_WAIT_TIME_LPM :
1858c2ecf20Sopenharmony_ci				SHTC3_NONBLOCKING_WAIT_TIME_LPM;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic const struct i2c_device_id shtc1_id[];
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic int shtc1_probe(struct i2c_client *client)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	int ret;
1948c2ecf20Sopenharmony_ci	u16 id_reg;
1958c2ecf20Sopenharmony_ci	char id_reg_buf[2];
1968c2ecf20Sopenharmony_ci	struct shtc1_data *data;
1978c2ecf20Sopenharmony_ci	struct device *hwmon_dev;
1988c2ecf20Sopenharmony_ci	enum shtcx_chips chip = i2c_match_id(shtc1_id, client)->driver_data;
1998c2ecf20Sopenharmony_ci	struct i2c_adapter *adap = client->adapter;
2008c2ecf20Sopenharmony_ci	struct device *dev = &client->dev;
2018c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	if (!i2c_check_functionality(adap, I2C_FUNC_I2C)) {
2048c2ecf20Sopenharmony_ci		dev_err(dev, "plain i2c transactions not supported\n");
2058c2ecf20Sopenharmony_ci		return -ENODEV;
2068c2ecf20Sopenharmony_ci	}
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci	ret = i2c_master_send(client, shtc1_cmd_read_id_reg, SHTC1_CMD_LENGTH);
2098c2ecf20Sopenharmony_ci	if (ret != SHTC1_CMD_LENGTH) {
2108c2ecf20Sopenharmony_ci		dev_err(dev, "could not send read_id_reg command: %d\n", ret);
2118c2ecf20Sopenharmony_ci		return ret < 0 ? ret : -ENODEV;
2128c2ecf20Sopenharmony_ci	}
2138c2ecf20Sopenharmony_ci	ret = i2c_master_recv(client, id_reg_buf, sizeof(id_reg_buf));
2148c2ecf20Sopenharmony_ci	if (ret != sizeof(id_reg_buf)) {
2158c2ecf20Sopenharmony_ci		dev_err(dev, "could not read ID register: %d\n", ret);
2168c2ecf20Sopenharmony_ci		return -ENODEV;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	id_reg = be16_to_cpup((__be16 *)id_reg_buf);
2208c2ecf20Sopenharmony_ci	if (chip == shtc3) {
2218c2ecf20Sopenharmony_ci		if ((id_reg & SHTC3_ID_MASK) != SHTC3_ID) {
2228c2ecf20Sopenharmony_ci			dev_err(dev, "SHTC3 ID register does not match\n");
2238c2ecf20Sopenharmony_ci			return -ENODEV;
2248c2ecf20Sopenharmony_ci		}
2258c2ecf20Sopenharmony_ci	} else if ((id_reg & SHTC1_ID_MASK) != SHTC1_ID) {
2268c2ecf20Sopenharmony_ci		dev_err(dev, "SHTC1 ID register does not match\n");
2278c2ecf20Sopenharmony_ci		return -ENODEV;
2288c2ecf20Sopenharmony_ci	}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
2318c2ecf20Sopenharmony_ci	if (!data)
2328c2ecf20Sopenharmony_ci		return -ENOMEM;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	data->setup.blocking_io = false;
2358c2ecf20Sopenharmony_ci	data->setup.high_precision = true;
2368c2ecf20Sopenharmony_ci	data->client = client;
2378c2ecf20Sopenharmony_ci	data->chip = chip;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	if (np) {
2408c2ecf20Sopenharmony_ci		data->setup.blocking_io = of_property_read_bool(np, "sensirion,blocking-io");
2418c2ecf20Sopenharmony_ci		data->setup.high_precision = !of_property_read_bool(np, "sensicon,low-precision");
2428c2ecf20Sopenharmony_ci	} else {
2438c2ecf20Sopenharmony_ci		if (client->dev.platform_data)
2448c2ecf20Sopenharmony_ci			data->setup = *(struct shtc1_platform_data *)dev->platform_data;
2458c2ecf20Sopenharmony_ci	}
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_ci	shtc1_select_command(data);
2488c2ecf20Sopenharmony_ci	mutex_init(&data->update_lock);
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	hwmon_dev = devm_hwmon_device_register_with_groups(dev,
2518c2ecf20Sopenharmony_ci							   client->name,
2528c2ecf20Sopenharmony_ci							   data,
2538c2ecf20Sopenharmony_ci							   shtc1_groups);
2548c2ecf20Sopenharmony_ci	if (IS_ERR(hwmon_dev))
2558c2ecf20Sopenharmony_ci		dev_dbg(dev, "unable to register hwmon device\n");
2568c2ecf20Sopenharmony_ci
2578c2ecf20Sopenharmony_ci	return PTR_ERR_OR_ZERO(hwmon_dev);
2588c2ecf20Sopenharmony_ci}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci/* device ID table */
2618c2ecf20Sopenharmony_cistatic const struct i2c_device_id shtc1_id[] = {
2628c2ecf20Sopenharmony_ci	{ "shtc1", shtc1 },
2638c2ecf20Sopenharmony_ci	{ "shtw1", shtc1 },
2648c2ecf20Sopenharmony_ci	{ "shtc3", shtc3 },
2658c2ecf20Sopenharmony_ci	{ }
2668c2ecf20Sopenharmony_ci};
2678c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, shtc1_id);
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_cistatic const struct of_device_id shtc1_of_match[] = {
2708c2ecf20Sopenharmony_ci	{ .compatible = "sensirion,shtc1" },
2718c2ecf20Sopenharmony_ci	{ .compatible = "sensirion,shtw1" },
2728c2ecf20Sopenharmony_ci	{ .compatible = "sensirion,shtc3" },
2738c2ecf20Sopenharmony_ci	{ }
2748c2ecf20Sopenharmony_ci};
2758c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, shtc1_of_match);
2768c2ecf20Sopenharmony_ci
2778c2ecf20Sopenharmony_cistatic struct i2c_driver shtc1_i2c_driver = {
2788c2ecf20Sopenharmony_ci	.driver = {
2798c2ecf20Sopenharmony_ci		.name = "shtc1",
2808c2ecf20Sopenharmony_ci		.of_match_table = shtc1_of_match,
2818c2ecf20Sopenharmony_ci	},
2828c2ecf20Sopenharmony_ci	.probe_new    = shtc1_probe,
2838c2ecf20Sopenharmony_ci	.id_table     = shtc1_id,
2848c2ecf20Sopenharmony_ci};
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_cimodule_i2c_driver(shtc1_i2c_driver);
2878c2ecf20Sopenharmony_ci
2888c2ecf20Sopenharmony_ciMODULE_AUTHOR("Johannes Winkelmann <johannes.winkelmann@sensirion.com>");
2898c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Sensirion SHTC1 humidity and temperature sensor driver");
2908c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
291