On Fri, 14 Feb 2020 14:49:18 +0000 Olivier MOYSAN <olivier.moysan@xxxxxx> wrote: > Hi Jonathan, > > On 2/14/20 2:11 PM, Jonathan Cameron wrote: > > On Tue, 11 Feb 2020 15:19:01 +0000 > > Olivier MOYSAN <olivier.moysan@xxxxxx> wrote: > > > >> Hi Jonathan, > >> > >> On 2/8/20 5:18 PM, Jonathan Cameron wrote: > >>> On Tue, 4 Feb 2020 11:10:08 +0100 > >>> Olivier Moysan <olivier.moysan@xxxxxx> wrote: > >>> > >>>> Add scale and offset attributes support to STM32 DFSDM. > >>>> > >>>> Signed-off-by: Olivier Moysan <olivier.moysan@xxxxxx> > >>> Hmm. I can't remember this history of this but we've kind of > >>> ended up backwards wrt to other consumer drivers. > >>> > >>> In some sense this is similar to the analog gyroscopes. In those > >>> the consumer driver is the gyroscope which is consuming the raw > >>> readings from an ADC connected to the channel. This results > >>> in us getting readings reported by the gyroscope driver. > >>> > >>> Here we have a sigma delta convertor consuming the pulse train > >>> from a sigma delta device. So the channels are reported by > >>> the sigma delta receiver, whereas i think the nearest equivalent > >>> to the analog voltage outputing gyroscopes would have been if > >>> we had reported the channel values at the sigma delta converter. > >> The DFSDM driver is currently used as a consumer of the sd modulator. > >> The scale and offset values of the channels are already computed by > >> the DFSDM driver, and provided by this driver to the IIO ABI. > >> However, the DFSDM has no voltage reference, so it has to retrieve > >> it from sd-modulator channels, for the scale factor computation. > >> > >> scale offset > >> ^ ^ > >> | | IIO ABI > >> +-------------------------------------------------------------+ > >> +---------------+ +-------------+ > >> |sd driver | |DFSDM driver | > >> +---------------+ +-------------+ > >> +-------------------------------------------------------------+ > >> HW > >> +---------------+ +-------------+ > >> +------->+ sd-modulator +--------->+ DFSDM +--------> > >> analog +------+--------+ +-------------+ output > >> input ^ > >> | vref > >> + > >> > >> > >> Is it the topology your are expecting ? > > It's not the one we'd expect if we are aligning with similar cases > > elsewhere in IIO. For example, if we attach an analog accelerometer > > to an ADC, we report the accel channels on the accelerometer not the > > ADC. The equivalent would be to see the DFSDM as providing a > > conversion service to the SD device which is actually executing > > the measurement and has the input channels. > > > > > > scale offset raw > > ^ ^ ^ > > | | | IIO ABI > > +-------------------------------------------------------------+ > > +---------------+ +-------------+ > > |sd driver | |DFSDM driver | > > +---------------+ +-------------+ > > +-------------------------------------------------------------+ > > HW > > +---------------+ +-------------+ > > +------->+ sd-modulator +--------->+ DFSDM +--------> > > analog +------+--------+ +-------------+ output > > input ^ > > | vref > >> + > >> > Thanks for your clarification. > ok, moving to this logic is a significant change. > I need to evaluate further the impact on the dfsdm driver. Understood! If we can't do it without potentially breaking users then such is life. Jonathan > > Regards > Olivier > >> If not, I probably missedsomething. Could you please clarify this point ? > >> > >> Regards > >> Olivier > >>> This wasn't really an issue when the only values available were > >>> raw, but if we are adding scale and offset, they are things that > >>> belong to the ad1201 for example, not the upstream stm32-dfsdm unit. > >>> > >>> Thinking of it another way, we don't report an SPI ADC output in > >>> the driver for the SPI master. > >>> > >>> Could we flip it around without breaking anything? > >>> > >>> Jonathan > >>> > >>>> --- > >>>> drivers/iio/adc/stm32-dfsdm-adc.c | 105 +++++++++++++++++++++++++++++- > >>>> 1 file changed, 102 insertions(+), 3 deletions(-) > >>>> > >>>> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c > >>>> index 07b9dfdf8e76..b85fd3e90496 100644 > >>>> --- a/drivers/iio/adc/stm32-dfsdm-adc.c > >>>> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c > >>>> @@ -10,6 +10,7 @@ > >>>> #include <linux/dma-mapping.h> > >>>> #include <linux/iio/adc/stm32-dfsdm-adc.h> > >>>> #include <linux/iio/buffer.h> > >>>> +#include <linux/iio/consumer.h> > >>>> #include <linux/iio/hw-consumer.h> > >>>> #include <linux/iio/sysfs.h> > >>>> #include <linux/iio/timer/stm32-lptim-trigger.h> > >>>> @@ -67,6 +68,13 @@ struct stm32_dfsdm_dev_data { > >>>> const struct regmap_config *regmap_cfg; > >>>> }; > >>>> > >>>> +struct stm32_dfsdm_sd_chan_info { > >>>> + int scale_val; > >>>> + int scale_val2; > >>>> + int offset; > >>>> + unsigned int differential; > >>>> +}; > >>>> + > >>>> struct stm32_dfsdm_adc { > >>>> struct stm32_dfsdm *dfsdm; > >>>> const struct stm32_dfsdm_dev_data *dev_data; > >>>> @@ -79,6 +87,7 @@ struct stm32_dfsdm_adc { > >>>> struct iio_hw_consumer *hwc; > >>>> struct completion completion; > >>>> u32 *buffer; > >>>> + struct stm32_dfsdm_sd_chan_info *sd_chan; > >>>> > >>>> /* Audio specific */ > >>>> unsigned int spi_freq; /* SPI bus clock frequency */ > >>>> @@ -1271,7 +1280,10 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev, > >>>> int *val2, long mask) > >>>> { > >>>> struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); > >>>> - int ret; > >>>> + 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 ret, idx = chan->scan_index; > >>>> > >>>> switch (mask) { > >>>> case IIO_CHAN_INFO_RAW: > >>>> @@ -1307,6 +1319,41 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev, > >>>> *val = adc->sample_freq; > >>>> > >>>> 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 input voltage. To > >>>> + * compensate this deviation, the voltage reference can be > >>>> + * corrected with a factor = realbits resolution / actual max > >>>> + */ > >>>> + *val = div_u64((u64)adc->sd_chan[idx].scale_val * > >>>> + (u64)BIT(DFSDM_DATA_RES - 1), max); > >>>> + *val2 = chan->scan_type.realbits; > >>>> + if (adc->sd_chan[idx].differential) > >>>> + *val *= 2; > >>>> + > >>>> + return IIO_VAL_FRACTIONAL_LOG2; > >>>> + > >>>> + case IIO_CHAN_INFO_OFFSET: > >>>> + /* > >>>> + * DFSDM output data are in the range [-2^n,2^n-1], > >>>> + * 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. > >>>> + * Add 2^n to offset. (i.e. middle of input range) > >>>> + * offset = offset(sd) * vref / res(sd) * max / vref. > >>>> + */ > >>>> + *val = div_u64((u64)max * adc->sd_chan[idx].offset, > >>>> + BIT(adc->sd_chan[idx].scale_val2 - 1)); > >>>> + if (!adc->sd_chan[idx].differential) > >>>> + *val += max; > >>>> + > >>>> + return IIO_VAL_INT; > >>>> } > >>>> > >>>> return -EINVAL; > >>>> @@ -1430,7 +1477,9 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev, > >>>> * IIO_CHAN_INFO_RAW: used to compute regular conversion > >>>> * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling > >>>> */ > >>>> - ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); > >>>> + ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | > >>>> + BIT(IIO_CHAN_INFO_SCALE) | > >>>> + BIT(IIO_CHAN_INFO_OFFSET); > >>>> ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) | > >>>> BIT(IIO_CHAN_INFO_SAMP_FREQ); > >>>> > >>>> @@ -1481,8 +1530,10 @@ static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev) > >>>> { > >>>> struct iio_chan_spec *ch; > >>>> struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); > >>>> + struct iio_channel *channels, *chan; > >>>> + struct stm32_dfsdm_sd_chan_info *sd_chan; > >>>> int num_ch; > >>>> - int ret, chan_idx; > >>>> + int ret, chan_idx, val2; > >>>> > >>>> adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING; > >>>> ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp); > >>>> @@ -1506,6 +1557,22 @@ static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev) > >>>> if (!ch) > >>>> return -ENOMEM; > >>>> > >>>> + /* Get SD modulator channels */ > >>>> + channels = iio_channel_get_all(&indio_dev->dev); > >>>> + if (IS_ERR(channels)) { > >>>> + dev_err(&indio_dev->dev, "Failed to get channel %ld\n", > >>>> + PTR_ERR(channels)); > >>>> + return PTR_ERR(channels); > >>>> + } > >>>> + chan = &channels[0]; > >>>> + > >>>> + adc->sd_chan = devm_kzalloc(&indio_dev->dev, > >>>> + sizeof(*adc->sd_chan) * num_ch, GFP_KERNEL); > >>>> + if (!adc->sd_chan) > >>>> + return -ENOMEM; > >>>> + > >>>> + sd_chan = adc->sd_chan; > >>>> + > >>>> for (chan_idx = 0; chan_idx < num_ch; chan_idx++) { > >>>> ch[chan_idx].scan_index = chan_idx; > >>>> ret = stm32_dfsdm_adc_chan_init_one(indio_dev, &ch[chan_idx]); > >>>> @@ -1513,6 +1580,38 @@ static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev) > >>>> dev_err(&indio_dev->dev, "Channels init failed\n"); > >>>> return ret; > >>>> } > >>>> + > >>>> + if (!chan->indio_dev) > >>>> + return -EINVAL; > >>>> + > >>>> + ret = iio_read_channel_scale(chan, &sd_chan->scale_val, > >>>> + &sd_chan->scale_val2); > >>>> + if (ret < 0) { > >>>> + dev_err(&indio_dev->dev, > >>>> + "Failed to get channel %d scale\n", chan_idx); > >>>> + return ret; > >>>> + } > >>>> + > >>>> + if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_OFFSET)) { > >>>> + ret = iio_read_channel_offset(chan, &sd_chan->offset, > >>>> + &val2); > >>>> + if (ret < 0) { > >>>> + dev_err(&indio_dev->dev, > >>>> + "Failed to get channel %d offset\n", > >>>> + chan_idx); > >>>> + return ret; > >>>> + } > >>>> + } > >>>> + > >>>> + sd_chan->differential = chan->channel->differential; > >>>> + > >>>> + dev_dbg(&indio_dev->dev, "Channel %d %s scale ref=%d offset=%d", > >>>> + chan_idx, chan->channel->differential ? > >>>> + "differential" : "single-ended", > >>>> + sd_chan->scale_val, sd_chan->offset); > >>>> + > >>>> + chan++; > >>>> + sd_chan++; > >>>> } > >>>> > >>>> indio_dev->num_channels = num_ch;