Commit 6fc839dc authored by Olivier Moysan's avatar Olivier Moysan Committed by Jonathan Cameron

iio: adc: stm32-dfsdm: add scaling support to dfsdm

Add scaling support to STM32 DFSDM.

When used in an analog context, a DFSDM filter typically converts the data
from a sigma delta modulator. The IIO device associated to the DFSDM
filter provides these data as raw data.
The IIO device can provide scaling information (voltage and offset) to
allow conversion of raw data into physical values.

With the new binding based on IIO backend framework, the sigma delta
modulators are defined as backends providing scaling information.

The scaling is not supported with legacy binding.
Signed-off-by: default avatarOlivier Moysan <olivier.moysan@foss.st.com>
Acked-by: default avatarNuno Sa <nuno.sa@analog.com>
Link: https://patch.msgid.link/20240730084640.1307938-10-olivier.moysan@foss.st.comSigned-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent bd71b6d0
...@@ -1262,6 +1262,7 @@ config STM32_DFSDM_ADC ...@@ -1262,6 +1262,7 @@ config STM32_DFSDM_ADC
select IIO_BUFFER select IIO_BUFFER
select IIO_BUFFER_HW_CONSUMER select IIO_BUFFER_HW_CONSUMER
select IIO_TRIGGERED_BUFFER select IIO_TRIGGERED_BUFFER
select IIO_BACKEND
help help
Select this option to support ADCSigma delta modulator for Select this option to support ADCSigma delta modulator for
STMicroelectronics STM32 digital filter for sigma delta converter. STMicroelectronics STM32 digital filter for sigma delta converter.
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <linux/dmaengine.h> #include <linux/dmaengine.h>
#include <linux/dma-mapping.h> #include <linux/dma-mapping.h>
#include <linux/iio/adc/stm32-dfsdm-adc.h> #include <linux/iio/adc/stm32-dfsdm-adc.h>
#include <linux/iio/backend.h>
#include <linux/iio/buffer.h> #include <linux/iio/buffer.h>
#include <linux/iio/hw-consumer.h> #include <linux/iio/hw-consumer.h>
#include <linux/iio/sysfs.h> #include <linux/iio/sysfs.h>
...@@ -78,6 +79,7 @@ struct stm32_dfsdm_adc { ...@@ -78,6 +79,7 @@ struct stm32_dfsdm_adc {
/* ADC specific */ /* ADC specific */
unsigned int oversamp; unsigned int oversamp;
struct iio_hw_consumer *hwc; struct iio_hw_consumer *hwc;
struct iio_backend **backend;
struct completion completion; struct completion completion;
u32 *buffer; u32 *buffer;
...@@ -672,6 +674,8 @@ static int stm32_dfsdm_generic_channel_parse_of(struct stm32_dfsdm *dfsdm, ...@@ -672,6 +674,8 @@ static int stm32_dfsdm_generic_channel_parse_of(struct stm32_dfsdm *dfsdm,
struct fwnode_handle *node) struct fwnode_handle *node)
{ {
struct stm32_dfsdm_channel *df_ch; struct stm32_dfsdm_channel *df_ch;
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
struct iio_backend *backend;
const char *of_str; const char *of_str;
int ret, val; int ret, val;
...@@ -721,6 +725,14 @@ static int stm32_dfsdm_generic_channel_parse_of(struct stm32_dfsdm *dfsdm, ...@@ -721,6 +725,14 @@ static int stm32_dfsdm_generic_channel_parse_of(struct stm32_dfsdm *dfsdm,
if (ret != -EINVAL) if (ret != -EINVAL)
df_ch->alt_si = 0; df_ch->alt_si = 0;
if (adc->dev_data->type == DFSDM_IIO) {
backend = devm_iio_backend_fwnode_get(&indio_dev->dev, NULL, node);
if (IS_ERR(backend))
return dev_err_probe(&indio_dev->dev, PTR_ERR(backend),
"Failed to get backend\n");
adc->backend[ch->scan_index] = backend;
}
return 0; return 0;
} }
...@@ -1056,6 +1068,7 @@ static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev, ...@@ -1056,6 +1068,7 @@ static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev,
static int stm32_dfsdm_postenable(struct iio_dev *indio_dev) static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
{ {
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
int i = 0;
int ret; int ret;
/* Reset adc buffer index */ /* Reset adc buffer index */
...@@ -1067,6 +1080,15 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev) ...@@ -1067,6 +1080,15 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
return ret; return ret;
} }
if (adc->backend) {
while (adc->backend[i]) {
ret = iio_backend_enable(adc->backend[i]);
if (ret < 0)
return ret;
i++;
}
}
ret = stm32_dfsdm_start_dfsdm(adc->dfsdm); ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
if (ret < 0) if (ret < 0)
goto err_stop_hwc; goto err_stop_hwc;
...@@ -1099,6 +1121,7 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev) ...@@ -1099,6 +1121,7 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
static int stm32_dfsdm_predisable(struct iio_dev *indio_dev) static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
{ {
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
int i = 0;
stm32_dfsdm_stop_conv(indio_dev); stm32_dfsdm_stop_conv(indio_dev);
...@@ -1106,6 +1129,13 @@ static int stm32_dfsdm_predisable(struct iio_dev *indio_dev) ...@@ -1106,6 +1129,13 @@ static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
stm32_dfsdm_stop_dfsdm(adc->dfsdm); stm32_dfsdm_stop_dfsdm(adc->dfsdm);
if (adc->backend) {
while (adc->backend[i]) {
iio_backend_disable(adc->backend[i]);
i++;
}
}
if (adc->hwc) if (adc->hwc)
iio_hw_consumer_disable(adc->hwc); iio_hw_consumer_disable(adc->hwc);
...@@ -1278,8 +1308,16 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev, ...@@ -1278,8 +1308,16 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
int *val2, long mask) int *val2, long mask)
{ {
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
u32 max = flo->max << (flo->lshift - chan->scan_type.shift);
int idx = chan->scan_index;
int ret; int ret;
if (flo->lshift < chan->scan_type.shift)
max = flo->max >> (chan->scan_type.shift - flo->lshift);
switch (mask) { switch (mask) {
case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_RAW:
ret = iio_device_claim_direct_mode(indio_dev); ret = iio_device_claim_direct_mode(indio_dev);
...@@ -1287,6 +1325,8 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev, ...@@ -1287,6 +1325,8 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
return ret; return ret;
if (adc->hwc) if (adc->hwc)
ret = iio_hw_consumer_enable(adc->hwc); ret = iio_hw_consumer_enable(adc->hwc);
if (adc->backend)
ret = iio_backend_enable(adc->backend[idx]);
if (ret < 0) { if (ret < 0) {
dev_err(&indio_dev->dev, dev_err(&indio_dev->dev,
"%s: IIO enable failed (channel %d)\n", "%s: IIO enable failed (channel %d)\n",
...@@ -1297,6 +1337,8 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev, ...@@ -1297,6 +1337,8 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
ret = stm32_dfsdm_single_conv(indio_dev, chan, val); ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
if (adc->hwc) if (adc->hwc)
iio_hw_consumer_disable(adc->hwc); iio_hw_consumer_disable(adc->hwc);
if (adc->backend)
iio_backend_disable(adc->backend[idx]);
if (ret < 0) { if (ret < 0) {
dev_err(&indio_dev->dev, dev_err(&indio_dev->dev,
"%s: Conversion failed (channel %d)\n", "%s: Conversion failed (channel %d)\n",
...@@ -1316,6 +1358,50 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev, ...@@ -1316,6 +1358,50 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
*val = adc->sample_freq; *val = adc->sample_freq;
return IIO_VAL_INT; return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
/*
* Scale is expressed in mV.
* When fast mode is disabled, actual resolution may be lower
* than 2^n, where n = realbits - 1.
* This leads to underestimating the input voltage.
* To compensate this deviation, the voltage reference can be
* corrected with a factor = realbits resolution / actual max
*/
if (adc->backend) {
ret = iio_backend_read_scale(adc->backend[idx], chan, val, NULL);
if (ret < 0)
return ret;
*val = div_u64((u64)*val * (u64)BIT(DFSDM_DATA_RES - 1), max);
*val2 = chan->scan_type.realbits;
if (chan->differential)
*val *= 2;
}
return IIO_VAL_FRACTIONAL_LOG2;
case IIO_CHAN_INFO_OFFSET:
/*
* DFSDM output data are in the range [-2^n, 2^n],
* with n = realbits - 1.
* - Differential modulator:
* Offset correspond to SD modulator offset.
* - Single ended modulator:
* Input is in [0V, Vref] range,
* where 0V corresponds to -2^n, and Vref to 2^n.
* Add 2^n to offset. (i.e. middle of input range)
* offset = offset(sd) * vref / res(sd) * max / vref.
*/
if (adc->backend) {
ret = iio_backend_read_offset(adc->backend[idx], chan, val, NULL);
if (ret < 0)
return ret;
*val = div_u64((u64)max * *val, BIT(*val2 - 1));
if (!chan->differential)
*val += max;
}
return IIO_VAL_INT;
} }
return -EINVAL; return -EINVAL;
...@@ -1442,7 +1528,15 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev, struct iio_c ...@@ -1442,7 +1528,15 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev, struct iio_c
* IIO_CHAN_INFO_RAW: used to compute regular conversion * IIO_CHAN_INFO_RAW: used to compute regular conversion
* IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
*/ */
ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); if (child) {
ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_OFFSET);
} else {
/* Legacy. Scaling not supported */
ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
}
ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
BIT(IIO_CHAN_INFO_SAMP_FREQ); BIT(IIO_CHAN_INFO_SAMP_FREQ);
...@@ -1571,6 +1665,14 @@ static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev) ...@@ -1571,6 +1665,14 @@ static int stm32_dfsdm_adc_init(struct device *dev, struct iio_dev *indio_dev)
if (IS_ERR(adc->hwc)) if (IS_ERR(adc->hwc))
return dev_err_probe(&indio_dev->dev, -EPROBE_DEFER, return dev_err_probe(&indio_dev->dev, -EPROBE_DEFER,
"waiting for SD modulator\n"); "waiting for SD modulator\n");
} else {
/* Generic binding. SD modulator IIO device not used. Use SD modulator backend. */
adc->hwc = NULL;
adc->backend = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*adc->backend),
GFP_KERNEL);
if (!adc->backend)
return -ENOMEM;
} }
ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch), GFP_KERNEL); ch = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*ch), GFP_KERNEL);
...@@ -1795,3 +1897,4 @@ module_platform_driver(stm32_dfsdm_adc_driver); ...@@ -1795,3 +1897,4 @@ module_platform_driver(stm32_dfsdm_adc_driver);
MODULE_DESCRIPTION("STM32 sigma delta ADC"); MODULE_DESCRIPTION("STM32 sigma delta ADC");
MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>"); MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen@st.com>");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL v2");
MODULE_IMPORT_NS(IIO_BACKEND);
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment