brintos

brintos / linux-shallow public Read only

0
0
Text · 7.2 KiB · b8916d2 Raw
304 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2 3/*4 * Copyright (c) Linumiz 20215 *6 * sht4x.c - Linux hwmon driver for SHT4x Temperature and Humidity sensor7 *8 * Author: Navin Sankar Velliangiri <navin@linumiz.com>9 */10 11#include <linux/crc8.h>12#include <linux/delay.h>13#include <linux/hwmon.h>14#include <linux/i2c.h>15#include <linux/jiffies.h>16#include <linux/module.h>17 18/*19 * Poll intervals (in milliseconds)20 */21#define SHT4X_MIN_POLL_INTERVAL	200022 23/*24 * I2C command delays (in microseconds)25 */26#define SHT4X_MEAS_DELAY_HPM	8200	/* see t_MEAS,h in datasheet */27#define SHT4X_DELAY_EXTRA	1000028 29/*30 * Command Bytes31 */32#define SHT4X_CMD_MEASURE_HPM	0b1111110133#define SHT4X_CMD_RESET		0b1001010034 35#define SHT4X_CMD_LEN		136#define SHT4X_CRC8_LEN		137#define SHT4X_WORD_LEN		238#define SHT4X_RESPONSE_LENGTH	639#define SHT4X_CRC8_POLYNOMIAL	0x3140#define SHT4X_CRC8_INIT		0xff41#define SHT4X_MIN_TEMPERATURE	-4500042#define SHT4X_MAX_TEMPERATURE	12500043#define SHT4X_MIN_HUMIDITY	044#define SHT4X_MAX_HUMIDITY	10000045 46DECLARE_CRC8_TABLE(sht4x_crc8_table);47 48/**49 * struct sht4x_data - All the data required to operate an SHT4X chip50 * @client: the i2c client associated with the SHT4X51 * @lock: a mutex that is used to prevent parallel access to the i2c client52 * @valid: validity of fields below53 * @update_interval: the minimum poll interval54 * @last_updated: the previous time that the SHT4X was polled55 * @temperature: the latest temperature value received from the SHT4X56 * @humidity: the latest humidity value received from the SHT4X57 */58struct sht4x_data {59	struct i2c_client	*client;60	struct mutex		lock;	/* atomic read data updates */61	bool			valid;	/* validity of fields below */62	long			update_interval;	/* in milli-seconds */63	long			last_updated;	/* in jiffies */64	s32			temperature;65	s32			humidity;66};67 68/**69 * sht4x_read_values() - read and parse the raw data from the SHT4X70 * @data: the struct sht4x_data to use for the lock71 * Return: 0 if successful, -ERRNO if not72 */73static int sht4x_read_values(struct sht4x_data *data)74{75	int ret = 0;76	u16 t_ticks, rh_ticks;77	unsigned long next_update;78	struct i2c_client *client = data->client;79	u8 crc;80	u8 cmd[SHT4X_CMD_LEN] = {SHT4X_CMD_MEASURE_HPM};81	u8 raw_data[SHT4X_RESPONSE_LENGTH];82 83	mutex_lock(&data->lock);84	next_update = data->last_updated +85		      msecs_to_jiffies(data->update_interval);86 87	if (data->valid && time_before_eq(jiffies, next_update))88		goto unlock;89 90	ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN);91	if (ret < 0)92		goto unlock;93 94	usleep_range(SHT4X_MEAS_DELAY_HPM, SHT4X_MEAS_DELAY_HPM + SHT4X_DELAY_EXTRA);95 96	ret = i2c_master_recv(client, raw_data, SHT4X_RESPONSE_LENGTH);97	if (ret != SHT4X_RESPONSE_LENGTH) {98		if (ret >= 0)99			ret = -ENODATA;100		goto unlock;101	}102 103	t_ticks = raw_data[0] << 8 | raw_data[1];104	rh_ticks = raw_data[3] << 8 | raw_data[4];105 106	crc = crc8(sht4x_crc8_table, &raw_data[0], SHT4X_WORD_LEN, CRC8_INIT_VALUE);107	if (crc != raw_data[2]) {108		dev_err(&client->dev, "data integrity check failed\n");109		ret = -EIO;110		goto unlock;111	}112 113	crc = crc8(sht4x_crc8_table, &raw_data[3], SHT4X_WORD_LEN, CRC8_INIT_VALUE);114	if (crc != raw_data[5]) {115		dev_err(&client->dev, "data integrity check failed\n");116		ret = -EIO;117		goto unlock;118	}119 120	data->temperature = ((21875 * (int32_t)t_ticks) >> 13) - 45000;121	data->humidity = ((15625 * (int32_t)rh_ticks) >> 13) - 6000;122	data->last_updated = jiffies;123	data->valid = true;124	ret = 0;125 126unlock:127	mutex_unlock(&data->lock);128	return ret;129}130 131static ssize_t sht4x_interval_write(struct sht4x_data *data, long val)132{133	data->update_interval = clamp_val(val, SHT4X_MIN_POLL_INTERVAL, INT_MAX);134 135	return 0;136}137 138/* sht4x_interval_read() - read the minimum poll interval in milliseconds */139static size_t sht4x_interval_read(struct sht4x_data *data, long *val)140{141	*val = data->update_interval;142	return 0;143}144 145/* sht4x_temperature1_read() - read the temperature in millidegrees */146static int sht4x_temperature1_read(struct sht4x_data *data, long *val)147{148	int ret;149 150	ret = sht4x_read_values(data);151	if (ret < 0)152		return ret;153 154	*val = data->temperature;155 156	return 0;157}158 159/* sht4x_humidity1_read() - read a relative humidity in millipercent */160static int sht4x_humidity1_read(struct sht4x_data *data, long *val)161{162	int ret;163 164	ret = sht4x_read_values(data);165	if (ret < 0)166		return ret;167 168	*val = data->humidity;169 170	return 0;171}172 173static umode_t sht4x_hwmon_visible(const void *data,174				   enum hwmon_sensor_types type,175				   u32 attr, int channel)176{177	switch (type) {178	case hwmon_temp:179	case hwmon_humidity:180		return 0444;181	case hwmon_chip:182		return 0644;183	default:184		return 0;185	}186}187 188static int sht4x_hwmon_read(struct device *dev, enum hwmon_sensor_types type,189			    u32 attr, int channel, long *val)190{191	struct sht4x_data *data = dev_get_drvdata(dev);192 193	switch (type) {194	case hwmon_temp:195		return sht4x_temperature1_read(data, val);196	case hwmon_humidity:197		return sht4x_humidity1_read(data, val);198	case hwmon_chip:199		return sht4x_interval_read(data, val);200	default:201		return -EOPNOTSUPP;202	}203}204 205static int sht4x_hwmon_write(struct device *dev, enum hwmon_sensor_types type,206			     u32 attr, int channel, long val)207{208	struct sht4x_data *data = dev_get_drvdata(dev);209 210	switch (type) {211	case hwmon_chip:212		return sht4x_interval_write(data, val);213	default:214		return -EOPNOTSUPP;215	}216}217 218static const struct hwmon_channel_info * const sht4x_info[] = {219	HWMON_CHANNEL_INFO(chip, HWMON_C_UPDATE_INTERVAL),220	HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT),221	HWMON_CHANNEL_INFO(humidity, HWMON_H_INPUT),222	NULL,223};224 225static const struct hwmon_ops sht4x_hwmon_ops = {226	.is_visible = sht4x_hwmon_visible,227	.read = sht4x_hwmon_read,228	.write = sht4x_hwmon_write,229};230 231static const struct hwmon_chip_info sht4x_chip_info = {232	.ops = &sht4x_hwmon_ops,233	.info = sht4x_info,234};235 236static int sht4x_probe(struct i2c_client *client)237{238	struct device *device = &client->dev;239	struct device *hwmon_dev;240	struct sht4x_data *data;241	u8 cmd[] = {SHT4X_CMD_RESET};242	int ret;243 244	/*245	 * we require full i2c support since the sht4x uses multi-byte read and246	 * writes as well as multi-byte commands which are not supported by247	 * the smbus protocol248	 */249	if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))250		return -EOPNOTSUPP;251 252	data = devm_kzalloc(device, sizeof(*data), GFP_KERNEL);253	if (!data)254		return -ENOMEM;255 256	data->update_interval = SHT4X_MIN_POLL_INTERVAL;257	data->client = client;258 259	mutex_init(&data->lock);260 261	crc8_populate_msb(sht4x_crc8_table, SHT4X_CRC8_POLYNOMIAL);262 263	ret = i2c_master_send(client, cmd, SHT4X_CMD_LEN);264	if (ret < 0)265		return ret;266	if (ret != SHT4X_CMD_LEN)267		return -EIO;268 269	hwmon_dev = devm_hwmon_device_register_with_info(device,270							 client->name,271							 data,272							 &sht4x_chip_info,273							 NULL);274 275	return PTR_ERR_OR_ZERO(hwmon_dev);276}277 278static const struct i2c_device_id sht4x_id[] = {279	{ "sht4x" },280	{ },281};282MODULE_DEVICE_TABLE(i2c, sht4x_id);283 284static const struct of_device_id sht4x_of_match[] = {285	{ .compatible = "sensirion,sht4x" },286	{ }287};288MODULE_DEVICE_TABLE(of, sht4x_of_match);289 290static struct i2c_driver sht4x_driver = {291	.driver = {292		.name = "sht4x",293		.of_match_table = sht4x_of_match,294	},295	.probe		= sht4x_probe,296	.id_table	= sht4x_id,297};298 299module_i2c_driver(sht4x_driver);300 301MODULE_AUTHOR("Navin Sankar Velliangiri <navin@linumiz.com>");302MODULE_DESCRIPTION("Sensirion SHT4x humidity and temperature sensor driver");303MODULE_LICENSE("GPL v2");304