iio: adc: stm32-dfsdm: add support for scan mode
authorFabrice Gasnier <fabrice.gasnier@st.com>
Thu, 21 Mar 2019 16:47:27 +0000 (17:47 +0100)
committerJonathan Cameron <Jonathan.Cameron@huawei.com>
Thu, 4 Apr 2019 19:20:38 +0000 (20:20 +0100)
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@st.com>
Signed-off-by: Jonathan Cameron <Jonathan.Cameron@huawei.com>
drivers/iio/adc/stm32-dfsdm-adc.c

index b491424d15fed7c41adfac72c57fe80cadef137a..4ead6bf6d8a740e06bd44804f092bee6dcd110ab 100644 (file)
@@ -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 @@ filter_unconfigure:
        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 @@ err_stop_hwc:
 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)