brintos

brintos / linux-shallow public Read only

0
0
Text · 7.6 KiB · ff2dba5 Raw
295 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors4 * Copyright (c) 2013,2014  Uplogix, Inc.5 * David Barksdale <dbarksdale@uplogix.com>6 */7 8/*9 * The Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors10 * are i2c devices which have an identical programming interface for11 * measuring relative humidity and temperature. The Si7013 has an additional12 * temperature input which this driver does not support.13 *14 * Data Sheets:15 *   Si7013: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7013.pdf16 *   Si7020: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7020.pdf17 *   Si7021: http://www.silabs.com/Support%20Documents/TechnicalDocs/Si7021.pdf18 */19 20#include <linux/delay.h>21#include <linux/i2c.h>22#include <linux/module.h>23#include <linux/mod_devicetable.h>24#include <linux/slab.h>25#include <linux/sysfs.h>26#include <linux/stat.h>27 28#include <linux/iio/iio.h>29#include <linux/iio/sysfs.h>30 31/* Measure Relative Humidity, Hold Master Mode */32#define SI7020CMD_RH_HOLD	0xE533/* Measure Temperature, Hold Master Mode */34#define SI7020CMD_TEMP_HOLD	0xE335/* Software Reset */36#define SI7020CMD_RESET		0xFE37#define SI7020CMD_USR_WRITE	0xE638/* "Heater Enabled" bit in the User Register */39#define SI7020_USR_HEATER_EN	BIT(2)40#define SI7020CMD_HEATER_WRITE	0x5141/* Heater current configuration bits */42#define SI7020_HEATER_VAL	GENMASK(3, 0)43 44struct si7020_data {45	struct i2c_client *client;46	/* Lock for cached register values */47	struct mutex lock;48	u8 user_reg;49	u8 heater_reg;50};51 52static const int si7020_heater_vals[] = { 0, 1, 0xF };53 54static int si7020_read_raw(struct iio_dev *indio_dev,55			   struct iio_chan_spec const *chan, int *val,56			   int *val2, long mask)57{58	struct si7020_data *data = iio_priv(indio_dev);59	int ret;60 61	switch (mask) {62	case IIO_CHAN_INFO_RAW:63		if (chan->type == IIO_CURRENT) {64			*val = data->heater_reg;65			return IIO_VAL_INT;66		}67 68		ret = i2c_smbus_read_word_swapped(data->client,69						  chan->type == IIO_TEMP ?70						  SI7020CMD_TEMP_HOLD :71						  SI7020CMD_RH_HOLD);72		if (ret < 0)73			return ret;74		*val = ret >> 2;75		/*76		 * Humidity values can slightly exceed the 0-100%RH77		 * range and should be corrected by software78		 */79		if (chan->type == IIO_HUMIDITYRELATIVE)80			*val = clamp_val(*val, 786, 13893);81		return IIO_VAL_INT;82	case IIO_CHAN_INFO_SCALE:83		if (chan->type == IIO_TEMP)84			*val = 175720; /* = 175.72 * 1000 */85		else86			*val = 125 * 1000;87		*val2 = 65536 >> 2;88		return IIO_VAL_FRACTIONAL;89	case IIO_CHAN_INFO_OFFSET:90		/*91		 * Since iio_convert_raw_to_processed_unlocked assumes offset92		 * is an integer we have to round these values and lose93		 * accuracy.94		 * Relative humidity will be 0.0032959% too high and95		 * temperature will be 0.00277344 degrees too high.96		 * This is no big deal because it's within the accuracy of the97		 * sensor.98		 */99		if (chan->type == IIO_TEMP)100			*val = -4368; /* = -46.85 * (65536 >> 2) / 175.72 */101		else102			*val = -786; /* = -6 * (65536 >> 2) / 125 */103		return IIO_VAL_INT;104	default:105		break;106	}107 108	return -EINVAL;109}110 111static const struct iio_chan_spec si7020_channels[] = {112	{113		.type = IIO_HUMIDITYRELATIVE,114		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |115			BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),116	},117	{118		.type = IIO_TEMP,119		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |120			BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),121	},122	{123		.type = IIO_CURRENT,124		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),125		.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),126		.extend_name = "heater",127	}128};129 130static int si7020_update_reg(struct si7020_data *data,131				u8 *reg, u8 cmd, u8 mask, u8 val)132{133	u8 new = (*reg & ~mask) | val;134	int ret;135 136	ret = i2c_smbus_write_byte_data(data->client, cmd, new);137	if (ret)138		return ret;139 140	*reg = new;141 142	return 0;143}144 145static int si7020_write_raw(struct iio_dev *indio_dev,146			     struct iio_chan_spec const *chan,147			     int val, int val2, long mask)148{149	struct si7020_data *data = iio_priv(indio_dev);150	int ret;151 152	switch (mask) {153	case IIO_CHAN_INFO_RAW:154		if (chan->type != IIO_CURRENT || val2 != 0 ||155			val < si7020_heater_vals[0] || val > si7020_heater_vals[2])156			return -EINVAL;157 158		scoped_guard(mutex, &data->lock)159			ret = si7020_update_reg(data, &data->heater_reg,160					SI7020CMD_HEATER_WRITE, SI7020_HEATER_VAL, val);161		return ret;162	default:163		return -EINVAL;164	}165}166 167static int si7020_read_available(struct iio_dev *indio_dev,168				  struct iio_chan_spec const *chan,169				  const int **vals,170				  int *type, int *length, long mask)171{172	if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_CURRENT)173		return -EINVAL;174 175	*vals = si7020_heater_vals;176	*type = IIO_VAL_INT;177 178	return IIO_AVAIL_RANGE;179}180 181static ssize_t si7020_show_heater_en(struct device *dev,182				 struct device_attribute *attr, char *buf)183{184	struct iio_dev *indio_dev = dev_to_iio_dev(dev);185	struct si7020_data *data = iio_priv(indio_dev);186 187	return sysfs_emit(buf, "%d\n", !!(data->user_reg & SI7020_USR_HEATER_EN));188}189 190static ssize_t si7020_store_heater_en(struct device *dev,191				  struct device_attribute *attr,192				  const char *buf, size_t len)193{194	struct iio_dev *indio_dev = dev_to_iio_dev(dev);195	struct si7020_data *data = iio_priv(indio_dev);196	int ret;197	bool val;198 199	ret = kstrtobool(buf, &val);200	if (ret)201		return ret;202 203	scoped_guard(mutex, &data->lock)204		ret = si7020_update_reg(data, &data->user_reg, SI7020CMD_USR_WRITE,205				SI7020_USR_HEATER_EN, val ? SI7020_USR_HEATER_EN : 0);206 207	return ret < 0 ? ret : len;208}209 210static IIO_DEVICE_ATTR(heater_enable, 0644,211		       si7020_show_heater_en, si7020_store_heater_en, 0);212 213static struct attribute *si7020_attributes[] = {214	&iio_dev_attr_heater_enable.dev_attr.attr,215	NULL216};217 218static const struct attribute_group si7020_attribute_group = {219	.attrs = si7020_attributes,220};221 222static const struct iio_info si7020_info = {223	.read_raw = si7020_read_raw,224	.write_raw = si7020_write_raw,225	.read_avail = si7020_read_available,226	.attrs = &si7020_attribute_group,227};228 229static int si7020_probe(struct i2c_client *client)230{231	struct iio_dev *indio_dev;232	struct si7020_data *data;233	int ret;234 235	if (!i2c_check_functionality(client->adapter,236				     I2C_FUNC_SMBUS_WRITE_BYTE |237				     I2C_FUNC_SMBUS_READ_WORD_DATA))238		return -EOPNOTSUPP;239 240	/* Reset device, loads default settings. */241	ret = i2c_smbus_write_byte(client, SI7020CMD_RESET);242	if (ret < 0)243		return ret;244	/* Wait the maximum power-up time after software reset. */245	msleep(15);246 247	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));248	if (!indio_dev)249		return -ENOMEM;250 251	data = iio_priv(indio_dev);252	i2c_set_clientdata(client, indio_dev);253	data->client = client;254	mutex_init(&data->lock);255 256	indio_dev->name = dev_name(&client->dev);257	indio_dev->modes = INDIO_DIRECT_MODE;258	indio_dev->info = &si7020_info;259	indio_dev->channels = si7020_channels;260	indio_dev->num_channels = ARRAY_SIZE(si7020_channels);261 262	/* All the "reserved" bits in the User Register are 1s by default */263	data->user_reg = 0x3A;264	data->heater_reg = 0x0;265 266	return devm_iio_device_register(&client->dev, indio_dev);267}268 269static const struct i2c_device_id si7020_id[] = {270	{ "si7020" },271	{ "th06" },272	{ }273};274MODULE_DEVICE_TABLE(i2c, si7020_id);275 276static const struct of_device_id si7020_dt_ids[] = {277	{ .compatible = "silabs,si7020" },278	{ }279};280MODULE_DEVICE_TABLE(of, si7020_dt_ids);281 282static struct i2c_driver si7020_driver = {283	.driver = {284		.name = "si7020",285		.of_match_table = si7020_dt_ids,286	},287	.probe		= si7020_probe,288	.id_table	= si7020_id,289};290 291module_i2c_driver(si7020_driver);292MODULE_DESCRIPTION("Silicon Labs Si7013/20/21 Relative Humidity and Temperature Sensors");293MODULE_AUTHOR("David Barksdale <dbarksdale@uplogix.com>");294MODULE_LICENSE("GPL");295