In order to support multiple channels in buffer mode, add support for scan mode. This is precursor patch to ease support of triggered buffer mode. Currently, only audio uses buffer mode: Regular continuous conversions with a single channel (per filter). DFSDM hardware supports scan mode (only) with injected conversions. Conversions can be launched by software (JSWSTART), trigger or synchronously with filter 0 (e.g. JSYNC). Continuous conversion mode isn't available for injected. Signed-off-by: Fabrice Gasnier <fabrice.gasnier@xxxxxx> --- drivers/iio/adc/stm32-dfsdm-adc.c | 182 +++++++++++++++++++++++++++++--------- 1 file changed, 142 insertions(+), 40 deletions(-) diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index b491424..4ead6bf 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -40,7 +40,8 @@ /* Filter configuration */ #define DFSDM_CR1_CFG_MASK (DFSDM_CR1_RCH_MASK | DFSDM_CR1_RCONT_MASK | \ - DFSDM_CR1_RSYNC_MASK) + DFSDM_CR1_RSYNC_MASK | DFSDM_CR1_JSYNC_MASK | \ + DFSDM_CR1_JSCAN_MASK) enum sd_converter_type { DFSDM_AUDIO, @@ -58,6 +59,8 @@ struct stm32_dfsdm_adc { struct stm32_dfsdm *dfsdm; const struct stm32_dfsdm_dev_data *dev_data; unsigned int fl_id; + unsigned int nconv; + unsigned long smask; /* ADC specific */ unsigned int oversamp; @@ -204,19 +207,39 @@ static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, return 0; } -static int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, - unsigned int ch_id) +static int stm32_dfsdm_start_channel(struct stm32_dfsdm_adc *adc) { - return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id), - DFSDM_CHCFGR1_CHEN_MASK, - DFSDM_CHCFGR1_CHEN(1)); + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + struct regmap *regmap = adc->dfsdm->regmap; + const struct iio_chan_spec *chan; + unsigned int bit; + int ret; + + for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) { + chan = indio_dev->channels + bit; + ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel), + DFSDM_CHCFGR1_CHEN_MASK, + DFSDM_CHCFGR1_CHEN(1)); + if (ret < 0) + return ret; + } + + return 0; } -static void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, - unsigned int ch_id) +static void stm32_dfsdm_stop_channel(struct stm32_dfsdm_adc *adc) { - regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id), - DFSDM_CHCFGR1_CHEN_MASK, DFSDM_CHCFGR1_CHEN(0)); + struct iio_dev *indio_dev = iio_priv_to_dev(adc); + struct regmap *regmap = adc->dfsdm->regmap; + const struct iio_chan_spec *chan; + unsigned int bit; + + for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) { + chan = indio_dev->channels + bit; + regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel), + DFSDM_CHCFGR1_CHEN_MASK, + DFSDM_CHCFGR1_CHEN(0)); + } } static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm, @@ -241,9 +264,10 @@ static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm, DFSDM_CHCFGR1_CHINSEL(ch->alt_si)); } -static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, +static int stm32_dfsdm_start_filter(struct stm32_dfsdm_adc *adc, unsigned int fl_id) { + struct stm32_dfsdm *dfsdm = adc->dfsdm; int ret; /* Enable filter */ @@ -252,7 +276,11 @@ static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, if (ret < 0) return ret; - /* Start conversion */ + /* Nothing more to do for injected (scan mode/triggered) conversions */ + if (adc->nconv > 1) + return 0; + + /* Software start (single or continuous) regular conversion */ return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RSWSTART_MASK, DFSDM_CR1_RSWSTART(1)); @@ -267,12 +295,14 @@ static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, } static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc, - unsigned int fl_id, unsigned int ch_id) + unsigned int fl_id) { struct iio_dev *indio_dev = iio_priv_to_dev(adc); struct regmap *regmap = adc->dfsdm->regmap; struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id]; u32 cr1; + const struct iio_chan_spec *chan; + unsigned int bit, jchg = 0; int ret; /* Average integrator oversampling */ @@ -292,14 +322,59 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc, if (ret) return ret; - /* No scan mode supported for the moment */ - cr1 = DFSDM_CR1_RCH(ch_id); + /* + * DFSDM modes configuration W.R.T audio/iio type modes + * ---------------------------------------------------------------- + * Modes | regular | regular | injected | injected | + * | | continuous | | + scan | + * --------------|---------|--------------|----------|------------| + * single conv | x | | | | + * (1 chan) | | | | | + * --------------|---------|--------------|----------|------------| + * 1 Audio chan | | sample freq | | | + * | | or sync_mode | | | + * --------------|---------|--------------|----------|------------| + * 1 IIO chan | | sample freq | trigger | | + * | | or sync_mode | | | + * --------------|---------|--------------|----------|------------| + * 2+ IIO chans | | | | trigger or | + * | | | | sync_mode | + * ---------------------------------------------------------------- + */ + if (adc->nconv == 1) { + bit = __ffs(adc->smask); + chan = indio_dev->channels + bit; - /* Continuous conversions triggered by SPI clock in buffer mode */ - if (indio_dev->currentmode & INDIO_BUFFER_SOFTWARE) - cr1 |= DFSDM_CR1_RCONT(1); + /* Use regular conversion for single channel without trigger */ + cr1 = DFSDM_CR1_RCH(chan->channel); - cr1 |= DFSDM_CR1_RSYNC(fl->sync_mode); + /* Continuous conversions triggered by SPI clk in buffer mode */ + if (indio_dev->currentmode & INDIO_BUFFER_SOFTWARE) + cr1 |= DFSDM_CR1_RCONT(1); + + cr1 |= DFSDM_CR1_RSYNC(fl->sync_mode); + } else { + /* Use injected conversion for multiple channels */ + for_each_set_bit(bit, &adc->smask, + sizeof(adc->smask) * BITS_PER_BYTE) { + chan = indio_dev->channels + bit; + jchg |= BIT(chan->channel); + } + ret = regmap_write(regmap, DFSDM_JCHGR(fl_id), jchg); + if (ret < 0) + return ret; + + /* Use scan mode for multiple channels */ + cr1 = DFSDM_CR1_JSCAN(1); + + /* + * Continuous conversions not supported in injected mode: + * - use conversions in sync with filter 0 + */ + if (!fl->sync_mode) + return -EINVAL; + cr1 |= DFSDM_CR1_JSYNC(fl->sync_mode); + } return regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_CFG_MASK, cr1); @@ -428,21 +503,20 @@ static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev, return len; } -static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc, - const struct iio_chan_spec *chan) +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc) { struct regmap *regmap = adc->dfsdm->regmap; int ret; - ret = stm32_dfsdm_start_channel(adc->dfsdm, chan->channel); + ret = stm32_dfsdm_start_channel(adc); if (ret < 0) return ret; - ret = stm32_dfsdm_filter_configure(adc, adc->fl_id, chan->channel); + ret = stm32_dfsdm_filter_configure(adc, adc->fl_id); if (ret < 0) goto stop_channels; - ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id); + ret = stm32_dfsdm_start_filter(adc, adc->fl_id); if (ret < 0) goto filter_unconfigure; @@ -452,13 +526,12 @@ static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc, regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id), DFSDM_CR1_CFG_MASK, 0); stop_channels: - stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel); + stm32_dfsdm_stop_channel(adc); return ret; } -static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc, - const struct iio_chan_spec *chan) +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc) { struct regmap *regmap = adc->dfsdm->regmap; @@ -467,7 +540,7 @@ static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc, regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id), DFSDM_CR1_CFG_MASK, 0); - stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel); + stm32_dfsdm_stop_channel(adc); } static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev, @@ -557,8 +630,7 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev) { struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); struct dma_slave_config config = { - .src_addr = (dma_addr_t)adc->dfsdm->phys_base + - DFSDM_RDATAR(adc->fl_id), + .src_addr = (dma_addr_t)adc->dfsdm->phys_base, .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, }; struct dma_async_tx_descriptor *desc; @@ -571,6 +643,10 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev) dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__, adc->buf_sz, adc->buf_sz / 2); + if (adc->nconv == 1) + config.src_addr += DFSDM_RDATAR(adc->fl_id); + else + config.src_addr += DFSDM_JDATAR(adc->fl_id); ret = dmaengine_slave_config(adc->dma_chan, &config); if (ret) return ret; @@ -595,9 +671,20 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev) /* Issue pending DMA requests */ dma_async_issue_pending(adc->dma_chan); - /* Enable DMA transfer*/ - ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR1(adc->fl_id), - DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN_MASK); + if (adc->nconv == 1) { + /* Enable regular DMA transfer*/ + ret = regmap_update_bits(adc->dfsdm->regmap, + DFSDM_CR1(adc->fl_id), + DFSDM_CR1_RDMAEN_MASK, + DFSDM_CR1_RDMAEN_MASK); + } else { + /* Enable injected DMA transfer*/ + ret = regmap_update_bits(adc->dfsdm->regmap, + DFSDM_CR1(adc->fl_id), + DFSDM_CR1_JDMAEN_MASK, + DFSDM_CR1_JDMAEN_MASK); + } + if (ret < 0) goto err_stop_dma; @@ -617,14 +704,26 @@ static void stm32_dfsdm_adc_dma_stop(struct iio_dev *indio_dev) return; regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR1(adc->fl_id), - DFSDM_CR1_RDMAEN_MASK, 0); + DFSDM_CR1_RDMAEN_MASK | DFSDM_CR1_JDMAEN_MASK, 0); dmaengine_terminate_all(adc->dma_chan); } +static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); + + adc->nconv = bitmap_weight(scan_mask, indio_dev->masklength); + adc->smask = *scan_mask; + + dev_dbg(&indio_dev->dev, "nconv=%d mask=%lx\n", adc->nconv, *scan_mask); + + return 0; +} + static int stm32_dfsdm_postenable(struct iio_dev *indio_dev) { struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); - const struct iio_chan_spec *chan = &indio_dev->channels[0]; int ret; /* Reset adc buffer index */ @@ -646,7 +745,7 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev) goto stop_dfsdm; } - ret = stm32_dfsdm_start_conv(adc, chan); + ret = stm32_dfsdm_start_conv(adc); if (ret) { dev_err(&indio_dev->dev, "Can't start conversion\n"); goto err_stop_dma; @@ -668,9 +767,8 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev) static int stm32_dfsdm_predisable(struct iio_dev *indio_dev) { struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); - const struct iio_chan_spec *chan = &indio_dev->channels[0]; - stm32_dfsdm_stop_conv(adc, chan); + stm32_dfsdm_stop_conv(adc); stm32_dfsdm_adc_dma_stop(indio_dev); @@ -756,7 +854,9 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev, if (ret < 0) goto stop_dfsdm; - ret = stm32_dfsdm_start_conv(adc, chan); + adc->nconv = 1; + adc->smask = BIT(chan->scan_index); + ret = stm32_dfsdm_start_conv(adc); if (ret < 0) { regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id), DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0)); @@ -777,7 +877,7 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev, else ret = IIO_VAL_INT; - stm32_dfsdm_stop_conv(adc, chan); + stm32_dfsdm_stop_conv(adc); stop_dfsdm: stm32_dfsdm_stop_dfsdm(adc->dfsdm); @@ -882,11 +982,13 @@ static const struct iio_info stm32_dfsdm_info_audio = { .hwfifo_set_watermark = stm32_dfsdm_set_watermark, .read_raw = stm32_dfsdm_read_raw, .write_raw = stm32_dfsdm_write_raw, + .update_scan_mode = stm32_dfsdm_update_scan_mode, }; static const struct iio_info stm32_dfsdm_info_adc = { .read_raw = stm32_dfsdm_read_raw, .write_raw = stm32_dfsdm_write_raw, + .update_scan_mode = stm32_dfsdm_update_scan_mode, }; static irqreturn_t stm32_dfsdm_irq(int irq, void *arg) -- 2.7.4