Re: [PATCH v2] iio: humidity: si7020: add heater support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Fri,  7 Jun 2024 16:10:30 +0200
Antoni Pokusinski <apokusinski01@xxxxxxxxx> wrote:

> From: Antoni Pokusinski <apokusinski01@xxxxxxxxx>
> 
> This patch adds support for the integrated on-chip heater that is present
> on all the devices supported by this driver (si7020, si7021, si7013, th6).
> In order to configure the heater, the driver interacts with the following
> device registers:
> * User Register - the 2nd bit of this register is a "Heater Enable bit"
>   (0 means that the heater is off, 1 means that it's on).
> * Heater Register - this register is present only on the si70xx devices
>   and controls the current flowing through the heater. The 4 lower bits
>   of this register can be assigned values from 0x0 to 0xF.
> 
> Signed-off-by: Antoni Pokusinski <apokusinski01@xxxxxxxxx>
> 
> --
---

Otherwise this gets picked up in the git log.

Anyhow, I noticed because I was checking b4 picked this one up right.

Excess stuff removed in the description and applied to the togreg
branch of iio.git which is initially pushed out as testing for
0-day to see if it can find anything we missed.

Thanks,

Jonathan
> Changes since v1:
> * macros: remove unnecessary comments
> * macros: add more meaningful names
> * `struct si7020_data`: add comment explaining the mutex
> * `si7020_update_reg()`: change the control flow to more standard
> * use `scoped_guard()` instead of `mutex_lock() <...> mutex_unlock()`
> * `si7020_probe()`: add comment for User Register default value
> * minor format fixes
> --
> Hello, after sending this email I noticed an accidental email mismatch
> between "From:" and the "Signed-off-by:" therefore I send this email
> once again with a corrected signature. Sorry for the confusion.
> ---
>  drivers/iio/humidity/si7020.c | 137 +++++++++++++++++++++++++++++++++-
>  1 file changed, 133 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/iio/humidity/si7020.c b/drivers/iio/humidity/si7020.c
> index fb1006649328..1215fab7b1a5 100644
> --- a/drivers/iio/humidity/si7020.c
> +++ b/drivers/iio/humidity/si7020.c
> @@ -23,6 +23,7 @@
>  #include <linux/mod_devicetable.h>
>  #include <linux/slab.h>
>  #include <linux/sysfs.h>
> +#include <linux/stat.h>
>  
>  #include <linux/iio/iio.h>
>  #include <linux/iio/sysfs.h>
> @@ -33,17 +34,38 @@
>  #define SI7020CMD_TEMP_HOLD	0xE3
>  /* Software Reset */
>  #define SI7020CMD_RESET		0xFE
> +#define SI7020CMD_USR_WRITE	0xE6
> +/* "Heater Enabled" bit in the User Register */
> +#define SI7020_USR_HEATER_EN	BIT(2)
> +#define SI7020CMD_HEATER_WRITE	0x51
> +/* Heater current configuration bits */
> +#define SI7020_HEATER_VAL	GENMASK(3, 0)
> +
> +struct si7020_data {
> +	struct i2c_client *client;
> +	/* Lock for cached register values */
> +	struct mutex lock;
> +	u8 user_reg;
> +	u8 heater_reg;
> +};
> +
> +static const int si7020_heater_vals[] = { 0, 1, 0xF };
>  
>  static int si7020_read_raw(struct iio_dev *indio_dev,
>  			   struct iio_chan_spec const *chan, int *val,
>  			   int *val2, long mask)
>  {
> -	struct i2c_client **client = iio_priv(indio_dev);
> +	struct si7020_data *data = iio_priv(indio_dev);
>  	int ret;
>  
>  	switch (mask) {
>  	case IIO_CHAN_INFO_RAW:
> -		ret = i2c_smbus_read_word_swapped(*client,
> +		if (chan->type == IIO_CURRENT) {
> +			*val = data->heater_reg;
> +			return IIO_VAL_INT;
> +		}
> +
> +		ret = i2c_smbus_read_word_swapped(data->client,
>  						  chan->type == IIO_TEMP ?
>  						  SI7020CMD_TEMP_HOLD :
>  						  SI7020CMD_RH_HOLD);
> @@ -96,17 +118,118 @@ static const struct iio_chan_spec si7020_channels[] = {
>  		.type = IIO_TEMP,
>  		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>  			BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET),
> +	},
> +	{
> +		.type = IIO_CURRENT,
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> +		.info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW),
> +		.extend_name = "heater",
> +	}
> +};
> +
> +static int si7020_update_reg(struct si7020_data *data,
> +				u8 *reg, u8 cmd, u8 mask, u8 val)
> +{
> +	u8 new = (*reg & ~mask) | val;
> +	int ret;
> +
> +	ret = i2c_smbus_write_byte_data(data->client, cmd, new);
> +	if (ret)
> +		return ret;
> +
> +	*reg = new;
> +
> +	return 0;
> +}
> +
> +static int si7020_write_raw(struct iio_dev *indio_dev,
> +			     struct iio_chan_spec const *chan,
> +			     int val, int val2, long mask)
> +{
> +	struct si7020_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		if (chan->type != IIO_CURRENT || val2 != 0 ||
> +			val < si7020_heater_vals[0] || val > si7020_heater_vals[2])
> +			return -EINVAL;
> +
> +		scoped_guard(mutex, &data->lock)
> +			ret = si7020_update_reg(data, &data->heater_reg,
> +					SI7020CMD_HEATER_WRITE, SI7020_HEATER_VAL, val);
> +		return ret;
> +	default:
> +		return -EINVAL;
>  	}
> +}
> +
> +static int si7020_read_available(struct iio_dev *indio_dev,
> +				  struct iio_chan_spec const *chan,
> +				  const int **vals,
> +				  int *type, int *length, long mask)
> +{
> +	if (mask != IIO_CHAN_INFO_RAW || chan->type != IIO_CURRENT)
> +		return -EINVAL;
> +
> +	*vals = si7020_heater_vals;
> +	*type = IIO_VAL_INT;
> +
> +	return IIO_AVAIL_RANGE;
> +}
> +
> +static ssize_t si7020_show_heater_en(struct device *dev,
> +				 struct device_attribute *attr, char *buf)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct si7020_data *data = iio_priv(indio_dev);
> +
> +	return sysfs_emit(buf, "%d\n", !!(data->user_reg & SI7020_USR_HEATER_EN));
> +}
> +
> +static ssize_t si7020_store_heater_en(struct device *dev,
> +				  struct device_attribute *attr,
> +				  const char *buf, size_t len)
> +{
> +	struct iio_dev *indio_dev = dev_to_iio_dev(dev);
> +	struct si7020_data *data = iio_priv(indio_dev);
> +	int ret;
> +	bool val;
> +
> +	ret = kstrtobool(buf, &val);
> +	if (ret)
> +		return ret;
> +
> +	scoped_guard(mutex, &data->lock)
> +		ret = si7020_update_reg(data, &data->user_reg, SI7020CMD_USR_WRITE,
> +				SI7020_USR_HEATER_EN, val ? SI7020_USR_HEATER_EN : 0);
> +
> +	return ret < 0 ? ret : len;
> +}
> +
> +static IIO_DEVICE_ATTR(heater_enable, 0644,
> +		       si7020_show_heater_en, si7020_store_heater_en, 0);
> +
> +static struct attribute *si7020_attributes[] = {
> +	&iio_dev_attr_heater_enable.dev_attr.attr,
> +	NULL
> +};
> +
> +static const struct attribute_group si7020_attribute_group = {
> +	.attrs = si7020_attributes,
>  };
>  
>  static const struct iio_info si7020_info = {
>  	.read_raw = si7020_read_raw,
> +	.write_raw = si7020_write_raw,
> +	.read_avail = si7020_read_available,
> +	.attrs = &si7020_attribute_group,
>  };
>  
>  static int si7020_probe(struct i2c_client *client)
>  {
>  	struct iio_dev *indio_dev;
> -	struct i2c_client **data;
> +	struct si7020_data *data;
>  	int ret;
>  
>  	if (!i2c_check_functionality(client->adapter,
> @@ -126,7 +249,9 @@ static int si7020_probe(struct i2c_client *client)
>  		return -ENOMEM;
>  
>  	data = iio_priv(indio_dev);
> -	*data = client;
> +	i2c_set_clientdata(client, indio_dev);
> +	data->client = client;
> +	mutex_init(&data->lock);
>  
>  	indio_dev->name = dev_name(&client->dev);
>  	indio_dev->modes = INDIO_DIRECT_MODE;
> @@ -134,6 +259,10 @@ static int si7020_probe(struct i2c_client *client)
>  	indio_dev->channels = si7020_channels;
>  	indio_dev->num_channels = ARRAY_SIZE(si7020_channels);
>  
> +	/* All the "reserved" bits in the User Register are 1s by default */
> +	data->user_reg = 0x3A;
> +	data->heater_reg = 0x0;
> +
>  	return devm_iio_device_register(&client->dev, indio_dev);
>  }
>  





[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux