[PATCH v1 6/7] iio: adc: ad4170: Add support for internal temperature sensor

Marcelo Schmitt posted 7 patches 10 months ago
There is a newer version of this series
[PATCH v1 6/7] iio: adc: ad4170: Add support for internal temperature sensor
Posted by Marcelo Schmitt 10 months ago
The AD4170 has an internal temperature sensor that can be read using the
ADC. Whenever possible, configure an IIO channel to provide the chip's
temperature.

Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
 drivers/iio/adc/ad4170.c | 72 ++++++++++++++++++++++++++++++++++++++--
 1 file changed, 69 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c
index b382e7f3dbe0..d204f8ca840f 100644
--- a/drivers/iio/adc/ad4170.c
+++ b/drivers/iio/adc/ad4170.c
@@ -922,6 +922,27 @@ static const struct iio_chan_spec ad4170_channel_template = {
 	},
 };
 
+static const struct iio_chan_spec ad4170_temp_channel_template = {
+	.type = IIO_TEMP,
+	.indexed = 0,
+	.channel = 17,
+	.channel2 = 17,
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+			      BIT(IIO_CHAN_INFO_SCALE) |
+			      BIT(IIO_CHAN_INFO_OFFSET) |
+			      BIT(IIO_CHAN_INFO_CALIBSCALE) |
+			      BIT(IIO_CHAN_INFO_CALIBBIAS) |
+			      BIT(IIO_CHAN_INFO_SAMP_FREQ),
+	.info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+	.scan_type = {
+		.sign = 's',
+		.realbits = 24,
+		.storagebits = 32,
+		.shift = 8,
+		.endianness = IIO_BE,
+	},
+};
+
 /*
  * Receives the number of a multiplexed AD4170 input (ain_n), and stores the
  * voltage (in µV) of the specified input into ain_voltage. If the input number
@@ -1209,9 +1230,27 @@ static int ad4170_read_raw(struct iio_dev *indio_dev,
 		return ret;
 	case IIO_CHAN_INFO_SCALE:
 		pga = FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe);
-		*val = chan_info->scale_tbl[pga][0];
-		*val2 = chan_info->scale_tbl[pga][1];
-		return IIO_VAL_INT_PLUS_NANO;
+		switch (chan->type) {
+		case IIO_VOLTAGE:
+			*val = chan_info->scale_tbl[pga][0];
+			*val2 = chan_info->scale_tbl[pga][1];
+			return IIO_VAL_INT_PLUS_NANO;
+
+		case IIO_TEMP:
+			/*
+			 * The scale_tbl converts output codes to mV units so
+			 * multiply by MILLI to make the factor convert to µV.
+			 * Then, apply the temperature sensor change sensitivity
+			 * of 477 μV/K. Finally, multiply the result by MILLI
+			 * again to comply with milli degrees Celsius IIO ABI.
+			 */
+			*val = 0; /* The scale integer part is always 0. */
+			*val2 = DIV_ROUND_CLOSEST(chan_info->scale_tbl[pga][1] * MILLI,
+						  477) * MILLI;
+			return IIO_VAL_INT_PLUS_NANO;
+		default:
+			return -EINVAL;
+		}
 	case IIO_CHAN_INFO_OFFSET:
 		pga = FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe);
 		*val = chan_info->offset_tbl[pga];
@@ -1855,12 +1894,39 @@ static int ad4170_parse_channels(struct iio_dev *indio_dev)
 	if (num_channels > AD4170_MAX_CHANNELS)
 		return dev_err_probe(dev, -EINVAL, "Too many channels\n");
 
+	/* Add one for temperature */
+	num_channels = min(num_channels + 1, AD4170_MAX_CHANNELS);
+
 	device_for_each_child_node_scoped(dev, child) {
 		ret = ad4170_parse_channel_node(indio_dev, child, chan_num++);
 		if (ret)
 			return ret;
 	}
 
+	/*
+	 * Add internal temperature sensor channel if the maximum number of
+	 * channels has not been reached.
+	 */
+	if (num_channels < AD4170_MAX_CHANNELS) {
+		struct ad4170_setup *setup = &st->chan_infos[chan_num].setup;
+
+		st->chans[chan_num] = ad4170_temp_channel_template;
+		st->chans[chan_num].address = chan_num;
+		st->chans[chan_num].scan_index = chan_num;
+
+		st->chan_infos[chan_num].setup_num = AD4170_INVALID_SETUP;
+		st->chan_infos[chan_num].initialized = true;
+
+		setup->afe |= FIELD_PREP(AD4170_AFE_REF_SELECT_MSK,
+					 AD4170_REF_AVDD);
+
+		ret = ad4170_get_input_range(st, &st->chans[chan_num], chan_num,
+					     AD4170_REF_AVDD);
+		if (ret < 0)
+			return dev_err_probe(dev, ret, "Invalid input config\n");
+
+		st->chan_infos[chan_num].input_range_uv = ret;
+	}
 	indio_dev->num_channels = num_channels;
 	indio_dev->channels = st->chans;
 	return 0;
-- 
2.47.2

Re: [PATCH v1 6/7] iio: adc: ad4170: Add support for internal temperature sensor
Posted by Nuno Sá 10 months ago
On Wed, 2025-04-09 at 09:26 -0300, Marcelo Schmitt wrote:
> The AD4170 has an internal temperature sensor that can be read using the
> ADC. Whenever possible, configure an IIO channel to provide the chip's
> temperature.
> 
> Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> ---

One minor nit... Otherwise looks good:

Reviewed-by: Nuno Sá <nuno.sa@analog.com>

>  drivers/iio/adc/ad4170.c | 72 ++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 69 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/iio/adc/ad4170.c b/drivers/iio/adc/ad4170.c
> index b382e7f3dbe0..d204f8ca840f 100644
> --- a/drivers/iio/adc/ad4170.c
> +++ b/drivers/iio/adc/ad4170.c
> @@ -922,6 +922,27 @@ static const struct iio_chan_spec ad4170_channel_template
> = {
>  	},
>  };
>  
> +static const struct iio_chan_spec ad4170_temp_channel_template = {
> +	.type = IIO_TEMP,
> +	.indexed = 0,
> +	.channel = 17,
> +	.channel2 = 17,
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
> +			      BIT(IIO_CHAN_INFO_SCALE) |
> +			      BIT(IIO_CHAN_INFO_OFFSET) |
> +			      BIT(IIO_CHAN_INFO_CALIBSCALE) |
> +			      BIT(IIO_CHAN_INFO_CALIBBIAS) |
> +			      BIT(IIO_CHAN_INFO_SAMP_FREQ),
> +	.info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
> +	.scan_type = {
> +		.sign = 's',
> +		.realbits = 24,
> +		.storagebits = 32,
> +		.shift = 8,
> +		.endianness = IIO_BE,
> +	},
> +};
> +
>  /*
>   * Receives the number of a multiplexed AD4170 input (ain_n), and stores the
>   * voltage (in µV) of the specified input into ain_voltage. If the input
> number
> @@ -1209,9 +1230,27 @@ static int ad4170_read_raw(struct iio_dev *indio_dev,
>  		return ret;
>  	case IIO_CHAN_INFO_SCALE:
>  		pga = FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe);
> -		*val = chan_info->scale_tbl[pga][0];
> -		*val2 = chan_info->scale_tbl[pga][1];
> -		return IIO_VAL_INT_PLUS_NANO;
> +		switch (chan->type) {
> +		case IIO_VOLTAGE:
> +			*val = chan_info->scale_tbl[pga][0];
> +			*val2 = chan_info->scale_tbl[pga][1];
> +			return IIO_VAL_INT_PLUS_NANO;
> +
> +		case IIO_TEMP:
> +			/*
> +			 * The scale_tbl converts output codes to mV units so
> +			 * multiply by MILLI to make the factor convert to
> µV.
> +			 * Then, apply the temperature sensor change
> sensitivity
> +			 * of 477 μV/K. Finally, multiply the result by MILLI
> +			 * again to comply with milli degrees Celsius IIO
> ABI.
> +			 */
> +			*val = 0; /* The scale integer part is always 0. */

Hmm this comment does not add much...

> +			*val2 = DIV_ROUND_CLOSEST(chan_info-
> >scale_tbl[pga][1] * MILLI,
> +						  477) * MILLI;
> +			return IIO_VAL_INT_PLUS_NANO;
> +		default:
> +			return -EINVAL;
> +		}
>  	case IIO_CHAN_INFO_OFFSET:
>  		pga = FIELD_GET(AD4170_AFE_PGA_GAIN_MSK, setup->afe);
>  		*val = chan_info->offset_tbl[pga];
> @@ -1855,12 +1894,39 @@ static int ad4170_parse_channels(struct iio_dev
> *indio_dev)
>  	if (num_channels > AD4170_MAX_CHANNELS)
>  		return dev_err_probe(dev, -EINVAL, "Too many channels\n");
>  
> +	/* Add one for temperature */
> +	num_channels = min(num_channels + 1, AD4170_MAX_CHANNELS);
> +
>  	device_for_each_child_node_scoped(dev, child) {
>  		ret = ad4170_parse_channel_node(indio_dev, child,
> chan_num++);
>  		if (ret)
>  			return ret;
>  	}
>  
> +	/*
> +	 * Add internal temperature sensor channel if the maximum number of
> +	 * channels has not been reached.
> +	 */
> +	if (num_channels < AD4170_MAX_CHANNELS) {
> +		struct ad4170_setup *setup = &st->chan_infos[chan_num].setup;
> +
> +		st->chans[chan_num] = ad4170_temp_channel_template;
> +		st->chans[chan_num].address = chan_num;
> +		st->chans[chan_num].scan_index = chan_num;
> +
> +		st->chan_infos[chan_num].setup_num = AD4170_INVALID_SETUP;
> +		st->chan_infos[chan_num].initialized = true;
> +
> +		setup->afe |= FIELD_PREP(AD4170_AFE_REF_SELECT_MSK,
> +					 AD4170_REF_AVDD);
> +
> +		ret = ad4170_get_input_range(st, &st->chans[chan_num],
> chan_num,
> +					     AD4170_REF_AVDD);
> +		if (ret < 0)
> +			return dev_err_probe(dev, ret, "Invalid input
> config\n");
> +
> +		st->chan_infos[chan_num].input_range_uv = ret;
> +	}
>  	indio_dev->num_channels = num_channels;
>  	indio_dev->channels = st->chans;
>  	return 0;