2019-03-21 16:50:47

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 0/8] iio: adc: stm32-dfsdm: add buffer modes

This patchset adds support for buffer modes, scan mode and triggers
in STM32 DFSDM ADC driver:
- 1st patch is an improvement
- Patch 2 to 5 are basically precursor patchs that reworks the way to
configure the dfsdm for the audio part, so it can be common with the
iio part.
- Last patchs add support for triggered buffer mode (with scan mode and
hardware triggers)

Changes in v2:
- rework 1st patch to better reflect the contrainsts and explain them,
to reflect Jonathan's remarks.
- improve patch 7 description to better explain the DFSDM modes, with
regard to the userland interface.

Fabrice Gasnier (8):
iio: adc: stm32-dfsdm: make spi_master_freq more accurate
iio: adc: stm32-dfsdm: continuous mode depends on current mode
iio: adc: stm32-dfsdm: move dma enable from start_conv() to
start_dma()
iio: adc: stm32-dfsdm: move dma slave config to start routine
iio: adc: stm32-dfsdm: enable hw consumer
iio: adc: stm32-dfsdm: add support for scan mode
iio: adc: stm32-dfsdm: add support for buffer modes
iio: adc: stm32-dfsdm: claim direct mode for raw read and settings

drivers/iio/adc/stm32-dfsdm-adc.c | 513 +++++++++++++++++++++++++++++--------
drivers/iio/adc/stm32-dfsdm-core.c | 17 +-
2 files changed, 413 insertions(+), 117 deletions(-)

--
2.7.4



2019-03-21 16:49:04

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 4/8] iio: adc: stm32-dfsdm: move dma slave config to start routine

Move DMA slave configuration to start routine: depending on regular or
injected mode is in use, DMA needs to read resp. RDATAR or JDATAR.
This is precursor patch to introduce injected mode (used for scan).

Signed-off-by: Fabrice Gasnier <[email protected]>
---
drivers/iio/adc/stm32-dfsdm-adc.c | 31 +++++++++++--------------------
1 file changed, 11 insertions(+), 20 deletions(-)

diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index 818627f..66e2ea0 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -556,6 +556,11 @@ static void stm32_dfsdm_audio_dma_buffer_done(void *data)
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_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ };
struct dma_async_tx_descriptor *desc;
dma_cookie_t cookie;
int ret;
@@ -566,6 +571,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);

+ ret = dmaengine_slave_config(adc->dma_chan, &config);
+ if (ret)
+ return ret;
+
/* Prepare a DMA cyclic transaction */
desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
adc->dma_buf,
@@ -925,12 +934,6 @@ static void stm32_dfsdm_dma_release(struct iio_dev *indio_dev)
static int stm32_dfsdm_dma_request(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_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
- };
- int ret;

adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
if (!adc->dma_chan)
@@ -940,23 +943,11 @@ static int stm32_dfsdm_dma_request(struct iio_dev *indio_dev)
DFSDM_DMA_BUFFER_SIZE,
&adc->dma_buf, GFP_KERNEL);
if (!adc->rx_buf) {
- ret = -ENOMEM;
- goto err_release;
+ dma_release_channel(adc->dma_chan);
+ return -ENOMEM;
}

- ret = dmaengine_slave_config(adc->dma_chan, &config);
- if (ret)
- goto err_free;
-
return 0;
-
-err_free:
- dma_free_coherent(adc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
- adc->rx_buf, adc->dma_buf);
-err_release:
- dma_release_channel(adc->dma_chan);
-
- return ret;
}

static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
--
2.7.4


2019-03-21 16:49:15

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 8/8] iio: adc: stm32-dfsdm: claim direct mode for raw read and settings

Claim direct mode to ensure no buffer mode is in use for:
- single conversion
- sample rate setting (must be set when filter isn't enabled).
- oversampling ratio (must be set when filter isn't enabled).

Signed-off-by: Fabrice Gasnier <[email protected]>
---
drivers/iio/adc/stm32-dfsdm-adc.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index 51688eb..ca42946 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -1039,16 +1039,23 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,

switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
ret = stm32_dfsdm_set_osrs(fl, 0, val);
if (!ret)
adc->oversamp = val;
-
+ iio_device_release_direct_mode(indio_dev);
return ret;

case IIO_CHAN_INFO_SAMP_FREQ:
if (!val)
return -EINVAL;

+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
switch (ch->src) {
case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL:
spi_freq = adc->dfsdm->spi_master_freq;
@@ -1070,9 +1077,11 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
if (ret < 0) {
dev_err(&indio_dev->dev,
"Not able to find parameter that match!\n");
+ iio_device_release_direct_mode(indio_dev);
return ret;
}
adc->sample_freq = val;
+ iio_device_release_direct_mode(indio_dev);

return 0;
}
@@ -1089,11 +1098,15 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,

switch (mask) {
case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
ret = iio_hw_consumer_enable(adc->hwc);
if (ret < 0) {
dev_err(&indio_dev->dev,
"%s: IIO enable failed (channel %d)\n",
__func__, chan->channel);
+ iio_device_release_direct_mode(indio_dev);
return ret;
}
ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
@@ -1102,8 +1115,10 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
dev_err(&indio_dev->dev,
"%s: Conversion failed (channel %d)\n",
__func__, chan->channel);
+ iio_device_release_direct_mode(indio_dev);
return ret;
}
+ iio_device_release_direct_mode(indio_dev);
return IIO_VAL_INT;

case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
--
2.7.4


2019-03-21 16:49:20

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 6/8] iio: adc: stm32-dfsdm: add support for scan mode

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 <[email protected]>
---
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


2019-03-21 16:49:49

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 1/8] iio: adc: stm32-dfsdm: make spi_master_freq more accurate

Current ckout divider may be set to a value that makes ckout to exceed
spi-max-frequency. Rather use lower value (e.g. round up divider when
ckout isn't accurate).

Also when the SPI clock isn't accurate, 'spi_master_freq' is filled in
with expected frequency. Use computed value instead to be more accurate:
- e.g. source clock / (CKOUTDIV + 1)

Enforce checks on the divider: ckoutdiv range can be from 1-255 to provide
divider of 2-256.

Signed-off-by: Fabrice Gasnier <[email protected]>
---
Changes in v2:
- rework the way to determine ckoutdiv bitfield: deal with divider directly
to explicitly handle the rounding, the range constraints of 2-256 and the
divider = ckoutdiv + 1.
---
drivers/iio/adc/stm32-dfsdm-core.c | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)

diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
index bf089f5..472b809 100644
--- a/drivers/iio/adc/stm32-dfsdm-core.c
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -199,7 +199,7 @@ static int stm32_dfsdm_parse_of(struct platform_device *pdev,
{
struct device_node *node = pdev->dev.of_node;
struct resource *res;
- unsigned long clk_freq;
+ unsigned long clk_freq, divider;
unsigned int spi_freq, rem;
int ret;

@@ -243,13 +243,20 @@ static int stm32_dfsdm_parse_of(struct platform_device *pdev,
return 0;
}

- priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
- if (!priv->spi_clk_out_div) {
- /* spi_clk_out_div == 0 means ckout is OFF */
+ divider = div_u64_rem(clk_freq, spi_freq, &rem);
+ /* Round up divider when ckout isn't precise, not to exceed spi_freq */
+ if (rem)
+ divider++;
+
+ /* programmable divider is in range of [2:256] */
+ if (divider < 2 || divider > 256) {
dev_err(&pdev->dev, "spi-max-frequency not achievable\n");
return -EINVAL;
}
- priv->dfsdm.spi_master_freq = spi_freq;
+
+ /* SPI clock output divider is: divider = CKOUTDIV + 1 */
+ priv->spi_clk_out_div = divider - 1;
+ priv->dfsdm.spi_master_freq = clk_freq / (priv->spi_clk_out_div + 1);

if (rem) {
dev_warn(&pdev->dev, "SPI clock not accurate\n");
--
2.7.4


2019-03-21 16:50:02

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 5/8] iio: adc: stm32-dfsdm: enable hw consumer

Optionally enable IIO hw consumer, when provided (e.g. for DFSDM_IIO type).
This is precursor patch to introduce buffer modes.

Signed-off-by: Fabrice Gasnier <[email protected]>
---
drivers/iio/adc/stm32-dfsdm-adc.c | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index 66e2ea0..b491424 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -630,9 +630,15 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
/* Reset adc buffer index */
adc->bufi = 0;

+ if (adc->hwc) {
+ ret = iio_hw_consumer_enable(adc->hwc);
+ if (ret < 0)
+ return ret;
+ }
+
ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
if (ret < 0)
- return ret;
+ goto err_stop_hwc;

ret = stm32_dfsdm_adc_dma_start(indio_dev);
if (ret) {
@@ -652,6 +658,9 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
stm32_dfsdm_adc_dma_stop(indio_dev);
stop_dfsdm:
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+err_stop_hwc:
+ if (adc->hwc)
+ iio_hw_consumer_disable(adc->hwc);

return ret;
}
@@ -667,6 +676,9 @@ static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)

stm32_dfsdm_stop_dfsdm(adc->dfsdm);

+ if (adc->hwc)
+ iio_hw_consumer_disable(adc->hwc);
+
return 0;
}

--
2.7.4


2019-03-21 16:50:24

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 3/8] iio: adc: stm32-dfsdm: move dma enable from start_conv() to start_dma()

Move DMA enable (e.g. set RDMAEN bit) away from start_conv() that is used
for both buffer and single conversions. Thus, single conv rely on
interrupt, not dma.
Note: take care to prepare all DMA stuff and set RDMAEN before starting
filter (can be set only when DFEN=0).

This is precursor patch to ease support of triggered buffer mode.

Signed-off-by: Fabrice Gasnier <[email protected]>
---
drivers/iio/adc/stm32-dfsdm-adc.c | 77 +++++++++++++++++++--------------------
1 file changed, 38 insertions(+), 39 deletions(-)

diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index 8690672..818627f 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -429,12 +429,10 @@ static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev,
}

static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc,
- const struct iio_chan_spec *chan,
- bool dma)
+ const struct iio_chan_spec *chan)
{
struct regmap *regmap = adc->dfsdm->regmap;
int ret;
- unsigned int dma_en = 0;

ret = stm32_dfsdm_start_channel(adc->dfsdm, chan->channel);
if (ret < 0)
@@ -444,25 +442,12 @@ static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc,
if (ret < 0)
goto stop_channels;

- if (dma) {
- /* Enable DMA transfer*/
- dma_en = DFSDM_CR1_RDMAEN(1);
- }
- /* Enable DMA transfer*/
- ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RDMAEN_MASK, dma_en);
- if (ret < 0)
- goto filter_unconfigure;
-
ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
if (ret < 0)
- goto stop_dma;
+ goto filter_unconfigure;

return 0;

-stop_dma:
- regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RDMAEN_MASK, 0);
filter_unconfigure:
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
DFSDM_CR1_CFG_MASK, 0);
@@ -479,10 +464,6 @@ static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc,

stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);

- /* Clean conversion options */
- regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RDMAEN_MASK, 0);
-
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
DFSDM_CR1_CFG_MASK, 0);

@@ -599,15 +580,36 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)

cookie = dmaengine_submit(desc);
ret = dma_submit_error(cookie);
- if (ret) {
- dmaengine_terminate_all(adc->dma_chan);
- return ret;
- }
+ if (ret)
+ goto err_stop_dma;

/* 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 (ret < 0)
+ goto err_stop_dma;
+
return 0;
+
+err_stop_dma:
+ dmaengine_terminate_all(adc->dma_chan);
+
+ return ret;
+}
+
+static void stm32_dfsdm_adc_dma_stop(struct iio_dev *indio_dev)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ if (!adc->dma_chan)
+ return;
+
+ regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_RDMAEN_MASK, 0);
+ dmaengine_terminate_all(adc->dma_chan);
}

static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
@@ -623,24 +625,22 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
if (ret < 0)
return ret;

- ret = stm32_dfsdm_start_conv(adc, chan, true);
+ ret = stm32_dfsdm_adc_dma_start(indio_dev);
if (ret) {
- dev_err(&indio_dev->dev, "Can't start conversion\n");
+ dev_err(&indio_dev->dev, "Can't start DMA\n");
goto stop_dfsdm;
}

- if (adc->dma_chan) {
- ret = stm32_dfsdm_adc_dma_start(indio_dev);
- if (ret) {
- dev_err(&indio_dev->dev, "Can't start DMA\n");
- goto err_stop_conv;
- }
+ ret = stm32_dfsdm_start_conv(adc, chan);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't start conversion\n");
+ goto err_stop_dma;
}

return 0;

-err_stop_conv:
- stm32_dfsdm_stop_conv(adc, chan);
+err_stop_dma:
+ stm32_dfsdm_adc_dma_stop(indio_dev);
stop_dfsdm:
stm32_dfsdm_stop_dfsdm(adc->dfsdm);

@@ -652,11 +652,10 @@ 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];

- if (adc->dma_chan)
- dmaengine_terminate_all(adc->dma_chan);
-
stm32_dfsdm_stop_conv(adc, chan);

+ stm32_dfsdm_adc_dma_stop(indio_dev);
+
stm32_dfsdm_stop_dfsdm(adc->dfsdm);

return 0;
@@ -736,7 +735,7 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
if (ret < 0)
goto stop_dfsdm;

- ret = stm32_dfsdm_start_conv(adc, chan, false);
+ ret = stm32_dfsdm_start_conv(adc, chan);
if (ret < 0) {
regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
--
2.7.4


2019-03-21 16:51:01

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 7/8] iio: adc: stm32-dfsdm: add support for buffer modes

DFSDM conversions can be launched continuously, or using various
triggers:
- by software
- hardware triggers (e.g. like in stm32-adc: TIM, LPTIM, EXTI)
- synchronously with DFSDM filter 0. e.g. for filters 1, 2

Launching conversions can be done using two methods:
a - injected:
- scan mode can be used to convert several channels each time a
trigger occurs.
- When not is scan mode, channels are converted in sequence, one upon
each trigger.
b - regular:
- supports software triggers or synchronous with filter 0
- single or continuous conversions

This patch finalizes DFSDM operating modes using IIO buffer modes:
- INDIO_BUFFER_SOFTWARE: regular continuous conversions (no trigger)
but limited to 1 channel. Users must set sampling frequency in this case.
For filters > 1, conversions can be started synchronously with filter 0.
- INDIO_BUFFER_TRIGGERED: triggered conversions uses injected mode for
launching conversions. DFSDM can use hardware triggers (e.g. STM32 timer
or lptimer), so add INDIO_HARDWARE_TRIGGERED to supported modes.
- INDIO_DIRECT_MODE: Only support DMA-based buffer modes. In case no DMA is
available, only support single conversions.

From userland perspective, to summarize various use cases:
1 - single conversion on any filter:
$ cd iio:deviceX
$ cat in_voltageY_raw
This uses regular a conversion (not continuous)

2 - Using sampling frequency without trigger (single channel, buffer)
$ cd iio:deviceX
$ echo 100 > sampling_frequency
$ echo "" > trigger/current_trigger
$ echo 1 > scan_elements/in_voltageY_en
$ echo 1 > buffer/enable
This uses regular conversion in continuous mode (Frequency is achieved
by tuning filter parameters)

3 - sync mode with filter 0: other filters can be converted when using
"st,filter0-sync" dt property. The conversions will get started at the
same time as filter 0. So for any filters > 1:
$ cd iio:deviceX
$ echo 100 > sampling_frequency
$ echo "" > trigger/current_trigger
$ echo 1 > scan_elements/in_voltageY_en
$ echo 1 > buffer/enable
Then start filter 0 as in 2 above.

4 - Using a hardware trigger (with one channel):
- check trigger, configure it:
$ cat /sys/bus/iio/devices/trigger1/name
tim6_trgo
$ echo 100 > /sys/bus/iio/devices/trigger1/sampling_frequency
- go to any filter:
$ echo 1 > scan_elements/in_voltageY_en
$ echo tim6_trgo > trigger/current_trigger
$ echo 1 > buffer/enable
This uses injected conversion as it uses a hardware trigger (without scan)

5 - Using a hardware trigger (with 2+ channel):
Same as in 4/ above, but enable two or more channels in scan_elements.
This uses injected conversion as it uses a hardware trigger (with scan mode)

Signed-off-by: Fabrice Gasnier <[email protected]>
---
Changes in v2:
- Update the patch description to better explain working modes and the way
to use them from user perspective
---
drivers/iio/adc/stm32-dfsdm-adc.c | 218 +++++++++++++++++++++++++++++++++-----
1 file changed, 194 insertions(+), 24 deletions(-)

diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index 4ead6bf..51688eb 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -12,6 +12,11 @@
#include <linux/iio/buffer.h>
#include <linux/iio/hw-consumer.h>
#include <linux/iio/sysfs.h>
+#include <linux/iio/timer/stm32-lptim-trigger.h>
+#include <linux/iio/timer/stm32-timer-trigger.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_device.h>
@@ -121,6 +126,61 @@ static int stm32_dfsdm_str2val(const char *str,
return -EINVAL;
}

+/**
+ * struct stm32_dfsdm_trig_info - DFSDM trigger info
+ * @name: name of the trigger, corresponding to its source
+ * @jextsel: trigger signal selection
+ */
+struct stm32_dfsdm_trig_info {
+ const char *name;
+ unsigned int jextsel;
+};
+
+/* hardware injected trigger enable, edge selection */
+enum stm32_dfsdm_jexten {
+ STM32_DFSDM_JEXTEN_DISABLED,
+ STM32_DFSDM_JEXTEN_RISING_EDGE,
+ STM32_DFSDM_JEXTEN_FALLING_EDGE,
+ STM32_DFSDM_EXTEN_BOTH_EDGES,
+};
+
+static const struct stm32_dfsdm_trig_info stm32_dfsdm_trigs[] = {
+ { TIM1_TRGO, 0 },
+ { TIM1_TRGO2, 1 },
+ { TIM8_TRGO, 2 },
+ { TIM8_TRGO2, 3 },
+ { TIM3_TRGO, 4 },
+ { TIM4_TRGO, 5 },
+ { TIM16_OC1, 6 },
+ { TIM6_TRGO, 7 },
+ { TIM7_TRGO, 8 },
+ { LPTIM1_OUT, 26 },
+ { LPTIM2_OUT, 27 },
+ { LPTIM3_OUT, 28 },
+ {},
+};
+
+static int stm32_dfsdm_get_jextsel(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ int i;
+
+ /* lookup triggers registered by stm32 timer trigger driver */
+ for (i = 0; stm32_dfsdm_trigs[i].name; i++) {
+ /**
+ * Checking both stm32 timer trigger type and trig name
+ * should be safe against arbitrary trigger names.
+ */
+ if ((is_stm32_timer_trigger(trig) ||
+ is_stm32_lptim_trigger(trig)) &&
+ !strcmp(stm32_dfsdm_trigs[i].name, trig->name)) {
+ return stm32_dfsdm_trigs[i].jextsel;
+ }
+ }
+
+ return -EINVAL;
+}
+
static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
unsigned int fast, unsigned int oversamp)
{
@@ -265,7 +325,8 @@ static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
}

static int stm32_dfsdm_start_filter(struct stm32_dfsdm_adc *adc,
- unsigned int fl_id)
+ unsigned int fl_id,
+ struct iio_trigger *trig)
{
struct stm32_dfsdm *dfsdm = adc->dfsdm;
int ret;
@@ -277,7 +338,7 @@ static int stm32_dfsdm_start_filter(struct stm32_dfsdm_adc *adc,
return ret;

/* Nothing more to do for injected (scan mode/triggered) conversions */
- if (adc->nconv > 1)
+ if (adc->nconv > 1 || trig)
return 0;

/* Software start (single or continuous) regular conversion */
@@ -294,8 +355,38 @@ static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm,
DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
}

+static int stm32_dfsdm_filter_set_trig(struct stm32_dfsdm_adc *adc,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ u32 jextsel = 0, jexten = STM32_DFSDM_JEXTEN_DISABLED;
+ int ret;
+
+ if (trig) {
+ ret = stm32_dfsdm_get_jextsel(indio_dev, trig);
+ if (ret < 0)
+ return ret;
+
+ /* set trigger source and polarity (default to rising edge) */
+ jextsel = ret;
+ jexten = STM32_DFSDM_JEXTEN_RISING_EDGE;
+ }
+
+ ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+ DFSDM_CR1_JEXTSEL_MASK | DFSDM_CR1_JEXTEN_MASK,
+ DFSDM_CR1_JEXTSEL(jextsel) |
+ DFSDM_CR1_JEXTEN(jexten));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
- unsigned int fl_id)
+ unsigned int fl_id,
+ struct iio_trigger *trig)
{
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
struct regmap *regmap = adc->dfsdm->regmap;
@@ -322,6 +413,10 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
if (ret)
return ret;

+ ret = stm32_dfsdm_filter_set_trig(adc, fl_id, trig);
+ if (ret)
+ return ret;
+
/*
* DFSDM modes configuration W.R.T audio/iio type modes
* ----------------------------------------------------------------
@@ -341,7 +436,7 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
* | | | | sync_mode |
* ----------------------------------------------------------------
*/
- if (adc->nconv == 1) {
+ if (adc->nconv == 1 && !trig) {
bit = __ffs(adc->smask);
chan = indio_dev->channels + bit;

@@ -365,13 +460,15 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
return ret;

/* Use scan mode for multiple channels */
- cr1 = DFSDM_CR1_JSCAN(1);
+ cr1 = DFSDM_CR1_JSCAN(!!(adc->nconv > 1));

/*
- * Continuous conversions not supported in injected mode:
- * - use conversions in sync with filter 0
+ * Continuous conversions not supported in injected mode,
+ * either use:
+ * - conversions in sync with filter 0
+ * - triggered conversions
*/
- if (!fl->sync_mode)
+ if (!fl->sync_mode && !trig)
return -EINVAL;
cr1 |= DFSDM_CR1_JSYNC(fl->sync_mode);
}
@@ -503,7 +600,8 @@ 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)
+static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc,
+ struct iio_trigger *trig)
{
struct regmap *regmap = adc->dfsdm->regmap;
int ret;
@@ -512,11 +610,11 @@ static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
if (ret < 0)
return ret;

- ret = stm32_dfsdm_filter_configure(adc, adc->fl_id);
+ ret = stm32_dfsdm_filter_configure(adc, adc->fl_id, trig);
if (ret < 0)
goto stop_channels;

- ret = stm32_dfsdm_start_filter(adc, adc->fl_id);
+ ret = stm32_dfsdm_start_filter(adc, adc->fl_id, trig);
if (ret < 0)
goto filter_unconfigure;

@@ -548,6 +646,7 @@ static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
+ unsigned int rx_buf_sz = DFSDM_DMA_BUFFER_SIZE;

/*
* DMA cyclic transfers are used, buffer is split into two periods.
@@ -556,7 +655,7 @@ static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
* - one buffer (period) driver pushed to ASoC side.
*/
watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
- adc->buf_sz = watermark * 2;
+ adc->buf_sz = min(rx_buf_sz, watermark * 2 * adc->nconv);

return 0;
}
@@ -586,13 +685,41 @@ static unsigned int stm32_dfsdm_adc_dma_residue(struct stm32_dfsdm_adc *adc)
return 0;
}

-static void stm32_dfsdm_audio_dma_buffer_done(void *data)
+static irqreturn_t stm32_dfsdm_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ int available = stm32_dfsdm_adc_dma_residue(adc);
+
+ while (available >= indio_dev->scan_bytes) {
+ u32 *buffer = (u32 *)&adc->rx_buf[adc->bufi];
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+ pf->timestamp);
+ available -= indio_dev->scan_bytes;
+ adc->bufi += indio_dev->scan_bytes;
+ if (adc->bufi >= adc->buf_sz)
+ adc->bufi = 0;
+ }
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void stm32_dfsdm_dma_buffer_done(void *data)
{
struct iio_dev *indio_dev = data;
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
int available = stm32_dfsdm_adc_dma_residue(adc);
size_t old_pos;

+ if (indio_dev->currentmode & INDIO_BUFFER_TRIGGERED) {
+ iio_trigger_poll_chained(indio_dev->trig);
+ return;
+ }
+
/*
* FIXME: In Kernel interface does not support cyclic DMA buffer,and
* offers only an interface to push data samples per samples.
@@ -620,6 +747,9 @@ static void stm32_dfsdm_audio_dma_buffer_done(void *data)
adc->bufi = 0;
old_pos = 0;
}
+ /* regular iio buffer without trigger */
+ if (adc->dev_data->type == DFSDM_IIO)
+ iio_push_to_buffers(indio_dev, buffer);
}
if (adc->cb)
adc->cb(&adc->rx_buf[old_pos], adc->bufi - old_pos,
@@ -643,7 +773,7 @@ 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)
+ if (adc->nconv == 1 && !indio_dev->trig)
config.src_addr += DFSDM_RDATAR(adc->fl_id);
else
config.src_addr += DFSDM_JDATAR(adc->fl_id);
@@ -660,7 +790,7 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
if (!desc)
return -EBUSY;

- desc->callback = stm32_dfsdm_audio_dma_buffer_done;
+ desc->callback = stm32_dfsdm_dma_buffer_done;
desc->callback_param = indio_dev;

cookie = dmaengine_submit(desc);
@@ -671,7 +801,7 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
/* Issue pending DMA requests */
dma_async_issue_pending(adc->dma_chan);

- if (adc->nconv == 1) {
+ if (adc->nconv == 1 && !indio_dev->trig) {
/* Enable regular DMA transfer*/
ret = regmap_update_bits(adc->dfsdm->regmap,
DFSDM_CR1(adc->fl_id),
@@ -726,13 +856,19 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
int ret;

+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+ ret = iio_triggered_buffer_postenable(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
+
/* Reset adc buffer index */
adc->bufi = 0;

if (adc->hwc) {
ret = iio_hw_consumer_enable(adc->hwc);
if (ret < 0)
- return ret;
+ goto err_predisable;
}

ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
@@ -745,7 +881,7 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
goto stop_dfsdm;
}

- ret = stm32_dfsdm_start_conv(adc);
+ ret = stm32_dfsdm_start_conv(adc, indio_dev->trig);
if (ret) {
dev_err(&indio_dev->dev, "Can't start conversion\n");
goto err_stop_dma;
@@ -760,6 +896,9 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
err_stop_hwc:
if (adc->hwc)
iio_hw_consumer_disable(adc->hwc);
+err_predisable:
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
+ iio_triggered_buffer_predisable(indio_dev);

return ret;
}
@@ -777,6 +916,9 @@ static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
if (adc->hwc)
iio_hw_consumer_disable(adc->hwc);

+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
+ iio_triggered_buffer_predisable(indio_dev);
+
return 0;
}

@@ -856,7 +998,7 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,

adc->nconv = 1;
adc->smask = BIT(chan->scan_index);
- ret = stm32_dfsdm_start_conv(adc);
+ ret = stm32_dfsdm_start_conv(adc, NULL);
if (ret < 0) {
regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
@@ -978,6 +1120,12 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}

+static int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ return stm32_dfsdm_get_jextsel(indio_dev, trig) < 0 ? -EINVAL : 0;
+}
+
static const struct iio_info stm32_dfsdm_info_audio = {
.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
.read_raw = stm32_dfsdm_read_raw,
@@ -986,9 +1134,11 @@ static const struct iio_info stm32_dfsdm_info_audio = {
};

static const struct iio_info stm32_dfsdm_info_adc = {
+ .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,
+ .validate_trigger = stm32_dfsdm_validate_trigger,
};

static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
@@ -1061,6 +1211,9 @@ static int stm32_dfsdm_dma_request(struct iio_dev *indio_dev)
return -ENOMEM;
}

+ indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
+ indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops;
+
return 0;
}

@@ -1082,7 +1235,8 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
* IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
*/
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);

if (adc->dev_data->type == DFSDM_AUDIO) {
ch->scan_type.sign = 's';
@@ -1104,9 +1258,6 @@ static int stm32_dfsdm_audio_init(struct iio_dev *indio_dev)
struct stm32_dfsdm_channel *d_ch;
int ret;

- indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
- indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops;
-
ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
if (!ch)
return -ENOMEM;
@@ -1174,6 +1325,25 @@ static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev)

init_completion(&adc->completion);

+ /* Optionally request DMA */
+ if (stm32_dfsdm_dma_request(indio_dev)) {
+ dev_dbg(&indio_dev->dev, "No DMA support\n");
+ return 0;
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ &iio_pollfunc_store_time,
+ &stm32_dfsdm_adc_trigger_handler,
+ &stm32_dfsdm_buffer_setup_ops);
+ if (ret) {
+ stm32_dfsdm_dma_release(indio_dev);
+ dev_err(&indio_dev->dev, "buffer setup failed\n");
+ return ret;
+ }
+
+ /* lptimer/timer hardware triggers */
+ indio_dev->modes |= INDIO_HARDWARE_TRIGGERED;
+
return 0;
}

@@ -1221,7 +1391,7 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)

iio->dev.parent = dev;
iio->dev.of_node = np;
- iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ iio->modes = INDIO_DIRECT_MODE;

platform_set_drvdata(pdev, adc);

--
2.7.4


2019-03-21 16:51:22

by Fabrice Gasnier

[permalink] [raw]
Subject: [PATCH v2 2/8] iio: adc: stm32-dfsdm: continuous mode depends on current mode

DFSDM regular continuous mode usage depends on current mode (not DMA):
- for single conversion, RCONT doesn't need to be set.
- for buffer mode, RCONT has to be set (e.g. INDIO_BUFFER_SOFTWARE
used by audio currently).
This is related to filter configuration, move it to relevant routine.

This is precursor patch to ease support of triggered buffer mode.

Signed-off-by: Fabrice Gasnier <[email protected]>
---
drivers/iio/adc/stm32-dfsdm-adc.c | 54 +++++++++++++++++++--------------------
1 file changed, 27 insertions(+), 27 deletions(-)

diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index fcd4a1c..8690672 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -38,6 +38,10 @@
#define DFSDM_MAX_RES BIT(31)
#define DFSDM_DATA_RES BIT(23)

+/* Filter configuration */
+#define DFSDM_CR1_CFG_MASK (DFSDM_CR1_RCH_MASK | DFSDM_CR1_RCONT_MASK | \
+ DFSDM_CR1_RSYNC_MASK)
+
enum sd_converter_type {
DFSDM_AUDIO,
DFSDM_IIO,
@@ -262,11 +266,13 @@ static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm,
DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
}

-static int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm,
+static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
unsigned int fl_id, unsigned int ch_id)
{
- struct regmap *regmap = dfsdm->regmap;
- struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[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;
int ret;

/* Average integrator oversampling */
@@ -287,14 +293,16 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm,
return ret;

/* No scan mode supported for the moment */
- ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCH_MASK,
- DFSDM_CR1_RCH(ch_id));
- if (ret)
- return ret;
+ cr1 = DFSDM_CR1_RCH(ch_id);
+
+ /* Continuous conversions triggered by SPI clock in buffer mode */
+ if (indio_dev->currentmode & INDIO_BUFFER_SOFTWARE)
+ cr1 |= DFSDM_CR1_RCONT(1);

- return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
- DFSDM_CR1_RSYNC_MASK,
- DFSDM_CR1_RSYNC(fl->sync_mode));
+ cr1 |= DFSDM_CR1_RSYNC(fl->sync_mode);
+
+ return regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_CFG_MASK,
+ cr1);
}

static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
@@ -426,47 +434,39 @@ static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc,
{
struct regmap *regmap = adc->dfsdm->regmap;
int ret;
- unsigned int dma_en = 0, cont_en = 0;
+ unsigned int dma_en = 0;

ret = stm32_dfsdm_start_channel(adc->dfsdm, chan->channel);
if (ret < 0)
return ret;

- ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id,
- chan->channel);
+ ret = stm32_dfsdm_filter_configure(adc, adc->fl_id, chan->channel);
if (ret < 0)
goto stop_channels;

if (dma) {
/* Enable DMA transfer*/
dma_en = DFSDM_CR1_RDMAEN(1);
- /* Enable conversion triggered by SPI clock*/
- cont_en = DFSDM_CR1_RCONT(1);
}
/* Enable DMA transfer*/
ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
DFSDM_CR1_RDMAEN_MASK, dma_en);
if (ret < 0)
- goto stop_channels;
-
- /* Enable conversion triggered by SPI clock*/
- ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RCONT_MASK, cont_en);
- if (ret < 0)
- goto stop_channels;
+ goto filter_unconfigure;

ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
if (ret < 0)
- goto stop_channels;
+ goto stop_dma;

return 0;

-stop_channels:
+stop_dma:
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
DFSDM_CR1_RDMAEN_MASK, 0);
-
+filter_unconfigure:
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RCONT_MASK, 0);
+ DFSDM_CR1_CFG_MASK, 0);
+stop_channels:
stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel);

return ret;
@@ -484,7 +484,7 @@ static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc,
DFSDM_CR1_RDMAEN_MASK, 0);

regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RCONT_MASK, 0);
+ DFSDM_CR1_CFG_MASK, 0);

stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel);
}
--
2.7.4


2019-03-24 16:42:28

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 1/8] iio: adc: stm32-dfsdm: make spi_master_freq more accurate

On Thu, 21 Mar 2019 17:47:22 +0100
Fabrice Gasnier <[email protected]> wrote:

> Current ckout divider may be set to a value that makes ckout to exceed
> spi-max-frequency. Rather use lower value (e.g. round up divider when
> ckout isn't accurate).
>
> Also when the SPI clock isn't accurate, 'spi_master_freq' is filled in
> with expected frequency. Use computed value instead to be more accurate:
> - e.g. source clock / (CKOUTDIV + 1)
>
> Enforce checks on the divider: ckoutdiv range can be from 1-255 to provide
> divider of 2-256.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
Applied to the togreg branch of iio.git and pushed out as testing for the
autobuilders to play with it.

Thanks,

Jonathan

> ---
> Changes in v2:
> - rework the way to determine ckoutdiv bitfield: deal with divider directly
> to explicitly handle the rounding, the range constraints of 2-256 and the
> divider = ckoutdiv + 1.
> ---
> drivers/iio/adc/stm32-dfsdm-core.c | 17 ++++++++++++-----
> 1 file changed, 12 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
> index bf089f5..472b809 100644
> --- a/drivers/iio/adc/stm32-dfsdm-core.c
> +++ b/drivers/iio/adc/stm32-dfsdm-core.c
> @@ -199,7 +199,7 @@ static int stm32_dfsdm_parse_of(struct platform_device *pdev,
> {
> struct device_node *node = pdev->dev.of_node;
> struct resource *res;
> - unsigned long clk_freq;
> + unsigned long clk_freq, divider;
> unsigned int spi_freq, rem;
> int ret;
>
> @@ -243,13 +243,20 @@ static int stm32_dfsdm_parse_of(struct platform_device *pdev,
> return 0;
> }
>
> - priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
> - if (!priv->spi_clk_out_div) {
> - /* spi_clk_out_div == 0 means ckout is OFF */
> + divider = div_u64_rem(clk_freq, spi_freq, &rem);
> + /* Round up divider when ckout isn't precise, not to exceed spi_freq */
> + if (rem)
> + divider++;
> +
> + /* programmable divider is in range of [2:256] */
> + if (divider < 2 || divider > 256) {
> dev_err(&pdev->dev, "spi-max-frequency not achievable\n");
> return -EINVAL;
> }
> - priv->dfsdm.spi_master_freq = spi_freq;
> +
> + /* SPI clock output divider is: divider = CKOUTDIV + 1 */
> + priv->spi_clk_out_div = divider - 1;
> + priv->dfsdm.spi_master_freq = clk_freq / (priv->spi_clk_out_div + 1);
>
> if (rem) {
> dev_warn(&pdev->dev, "SPI clock not accurate\n");


2019-03-24 16:44:19

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 2/8] iio: adc: stm32-dfsdm: continuous mode depends on current mode

On Thu, 21 Mar 2019 17:47:23 +0100
Fabrice Gasnier <[email protected]> wrote:

> DFSDM regular continuous mode usage depends on current mode (not DMA):
> - for single conversion, RCONT doesn't need to be set.
> - for buffer mode, RCONT has to be set (e.g. INDIO_BUFFER_SOFTWARE
> used by audio currently).
> This is related to filter configuration, move it to relevant routine.
>
> This is precursor patch to ease support of triggered buffer mode.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
Applied,

Thanks,

Jonathan
> ---
> drivers/iio/adc/stm32-dfsdm-adc.c | 54 +++++++++++++++++++--------------------
> 1 file changed, 27 insertions(+), 27 deletions(-)
>
> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
> index fcd4a1c..8690672 100644
> --- a/drivers/iio/adc/stm32-dfsdm-adc.c
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -38,6 +38,10 @@
> #define DFSDM_MAX_RES BIT(31)
> #define DFSDM_DATA_RES BIT(23)
>
> +/* Filter configuration */
> +#define DFSDM_CR1_CFG_MASK (DFSDM_CR1_RCH_MASK | DFSDM_CR1_RCONT_MASK | \
> + DFSDM_CR1_RSYNC_MASK)
> +
> enum sd_converter_type {
> DFSDM_AUDIO,
> DFSDM_IIO,
> @@ -262,11 +266,13 @@ static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm,
> DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
> }
>
> -static int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm,
> +static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
> unsigned int fl_id, unsigned int ch_id)
> {
> - struct regmap *regmap = dfsdm->regmap;
> - struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[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;
> int ret;
>
> /* Average integrator oversampling */
> @@ -287,14 +293,16 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm,
> return ret;
>
> /* No scan mode supported for the moment */
> - ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCH_MASK,
> - DFSDM_CR1_RCH(ch_id));
> - if (ret)
> - return ret;
> + cr1 = DFSDM_CR1_RCH(ch_id);
> +
> + /* Continuous conversions triggered by SPI clock in buffer mode */
> + if (indio_dev->currentmode & INDIO_BUFFER_SOFTWARE)
> + cr1 |= DFSDM_CR1_RCONT(1);
>
> - return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
> - DFSDM_CR1_RSYNC_MASK,
> - DFSDM_CR1_RSYNC(fl->sync_mode));
> + cr1 |= DFSDM_CR1_RSYNC(fl->sync_mode);
> +
> + return regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_CFG_MASK,
> + cr1);
> }
>
> static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
> @@ -426,47 +434,39 @@ static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc,
> {
> struct regmap *regmap = adc->dfsdm->regmap;
> int ret;
> - unsigned int dma_en = 0, cont_en = 0;
> + unsigned int dma_en = 0;
>
> ret = stm32_dfsdm_start_channel(adc->dfsdm, chan->channel);
> if (ret < 0)
> return ret;
>
> - ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id,
> - chan->channel);
> + ret = stm32_dfsdm_filter_configure(adc, adc->fl_id, chan->channel);
> if (ret < 0)
> goto stop_channels;
>
> if (dma) {
> /* Enable DMA transfer*/
> dma_en = DFSDM_CR1_RDMAEN(1);
> - /* Enable conversion triggered by SPI clock*/
> - cont_en = DFSDM_CR1_RCONT(1);
> }
> /* Enable DMA transfer*/
> ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> DFSDM_CR1_RDMAEN_MASK, dma_en);
> if (ret < 0)
> - goto stop_channels;
> -
> - /* Enable conversion triggered by SPI clock*/
> - ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> - DFSDM_CR1_RCONT_MASK, cont_en);
> - if (ret < 0)
> - goto stop_channels;
> + goto filter_unconfigure;
>
> ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
> if (ret < 0)
> - goto stop_channels;
> + goto stop_dma;
>
> return 0;
>
> -stop_channels:
> +stop_dma:
> regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> DFSDM_CR1_RDMAEN_MASK, 0);
> -
> +filter_unconfigure:
> regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> - DFSDM_CR1_RCONT_MASK, 0);
> + DFSDM_CR1_CFG_MASK, 0);
> +stop_channels:
> stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel);
>
> return ret;
> @@ -484,7 +484,7 @@ static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc,
> DFSDM_CR1_RDMAEN_MASK, 0);
>
> regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> - DFSDM_CR1_RCONT_MASK, 0);
> + DFSDM_CR1_CFG_MASK, 0);
>
> stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel);
> }


2019-03-24 16:45:34

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 3/8] iio: adc: stm32-dfsdm: move dma enable from start_conv() to start_dma()

On Thu, 21 Mar 2019 17:47:24 +0100
Fabrice Gasnier <[email protected]> wrote:

> Move DMA enable (e.g. set RDMAEN bit) away from start_conv() that is used
> for both buffer and single conversions. Thus, single conv rely on
> interrupt, not dma.
> Note: take care to prepare all DMA stuff and set RDMAEN before starting
> filter (can be set only when DFEN=0).
>
> This is precursor patch to ease support of triggered buffer mode.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
Applied,

Thanks,

Jonathan

> ---
> drivers/iio/adc/stm32-dfsdm-adc.c | 77 +++++++++++++++++++--------------------
> 1 file changed, 38 insertions(+), 39 deletions(-)
>
> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
> index 8690672..818627f 100644
> --- a/drivers/iio/adc/stm32-dfsdm-adc.c
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -429,12 +429,10 @@ static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev,
> }
>
> static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc,
> - const struct iio_chan_spec *chan,
> - bool dma)
> + const struct iio_chan_spec *chan)
> {
> struct regmap *regmap = adc->dfsdm->regmap;
> int ret;
> - unsigned int dma_en = 0;
>
> ret = stm32_dfsdm_start_channel(adc->dfsdm, chan->channel);
> if (ret < 0)
> @@ -444,25 +442,12 @@ static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc,
> if (ret < 0)
> goto stop_channels;
>
> - if (dma) {
> - /* Enable DMA transfer*/
> - dma_en = DFSDM_CR1_RDMAEN(1);
> - }
> - /* Enable DMA transfer*/
> - ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> - DFSDM_CR1_RDMAEN_MASK, dma_en);
> - if (ret < 0)
> - goto filter_unconfigure;
> -
> ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
> if (ret < 0)
> - goto stop_dma;
> + goto filter_unconfigure;
>
> return 0;
>
> -stop_dma:
> - regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> - DFSDM_CR1_RDMAEN_MASK, 0);
> filter_unconfigure:
> regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> DFSDM_CR1_CFG_MASK, 0);
> @@ -479,10 +464,6 @@ static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc,
>
> stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
>
> - /* Clean conversion options */
> - regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> - DFSDM_CR1_RDMAEN_MASK, 0);
> -
> regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
> DFSDM_CR1_CFG_MASK, 0);
>
> @@ -599,15 +580,36 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
>
> cookie = dmaengine_submit(desc);
> ret = dma_submit_error(cookie);
> - if (ret) {
> - dmaengine_terminate_all(adc->dma_chan);
> - return ret;
> - }
> + if (ret)
> + goto err_stop_dma;
>
> /* 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 (ret < 0)
> + goto err_stop_dma;
> +
> return 0;
> +
> +err_stop_dma:
> + dmaengine_terminate_all(adc->dma_chan);
> +
> + return ret;
> +}
> +
> +static void stm32_dfsdm_adc_dma_stop(struct iio_dev *indio_dev)
> +{
> + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> +
> + if (!adc->dma_chan)
> + return;
> +
> + regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR1(adc->fl_id),
> + DFSDM_CR1_RDMAEN_MASK, 0);
> + dmaengine_terminate_all(adc->dma_chan);
> }
>
> static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
> @@ -623,24 +625,22 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
> if (ret < 0)
> return ret;
>
> - ret = stm32_dfsdm_start_conv(adc, chan, true);
> + ret = stm32_dfsdm_adc_dma_start(indio_dev);
> if (ret) {
> - dev_err(&indio_dev->dev, "Can't start conversion\n");
> + dev_err(&indio_dev->dev, "Can't start DMA\n");
> goto stop_dfsdm;
> }
>
> - if (adc->dma_chan) {
> - ret = stm32_dfsdm_adc_dma_start(indio_dev);
> - if (ret) {
> - dev_err(&indio_dev->dev, "Can't start DMA\n");
> - goto err_stop_conv;
> - }
> + ret = stm32_dfsdm_start_conv(adc, chan);
> + if (ret) {
> + dev_err(&indio_dev->dev, "Can't start conversion\n");
> + goto err_stop_dma;
> }
>
> return 0;
>
> -err_stop_conv:
> - stm32_dfsdm_stop_conv(adc, chan);
> +err_stop_dma:
> + stm32_dfsdm_adc_dma_stop(indio_dev);
> stop_dfsdm:
> stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>
> @@ -652,11 +652,10 @@ 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];
>
> - if (adc->dma_chan)
> - dmaengine_terminate_all(adc->dma_chan);
> -
> stm32_dfsdm_stop_conv(adc, chan);
>
> + stm32_dfsdm_adc_dma_stop(indio_dev);
> +
> stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>
> return 0;
> @@ -736,7 +735,7 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
> if (ret < 0)
> goto stop_dfsdm;
>
> - ret = stm32_dfsdm_start_conv(adc, chan, false);
> + ret = stm32_dfsdm_start_conv(adc, chan);
> if (ret < 0) {
> regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
> DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));


2019-03-24 16:47:16

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 4/8] iio: adc: stm32-dfsdm: move dma slave config to start routine

On Thu, 21 Mar 2019 17:47:25 +0100
Fabrice Gasnier <[email protected]> wrote:

> Move DMA slave configuration to start routine: depending on regular or
> injected mode is in use, DMA needs to read resp. RDATAR or JDATAR.
> This is precursor patch to introduce injected mode (used for scan).
>
> Signed-off-by: Fabrice Gasnier <[email protected]>

Applied, thanks,

Jonathan
> ---
> drivers/iio/adc/stm32-dfsdm-adc.c | 31 +++++++++++--------------------
> 1 file changed, 11 insertions(+), 20 deletions(-)
>
> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
> index 818627f..66e2ea0 100644
> --- a/drivers/iio/adc/stm32-dfsdm-adc.c
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -556,6 +556,11 @@ static void stm32_dfsdm_audio_dma_buffer_done(void *data)
> 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_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
> + };
> struct dma_async_tx_descriptor *desc;
> dma_cookie_t cookie;
> int ret;
> @@ -566,6 +571,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);
>
> + ret = dmaengine_slave_config(adc->dma_chan, &config);
> + if (ret)
> + return ret;
> +
> /* Prepare a DMA cyclic transaction */
> desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
> adc->dma_buf,
> @@ -925,12 +934,6 @@ static void stm32_dfsdm_dma_release(struct iio_dev *indio_dev)
> static int stm32_dfsdm_dma_request(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_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
> - };
> - int ret;
>
> adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
> if (!adc->dma_chan)
> @@ -940,23 +943,11 @@ static int stm32_dfsdm_dma_request(struct iio_dev *indio_dev)
> DFSDM_DMA_BUFFER_SIZE,
> &adc->dma_buf, GFP_KERNEL);
> if (!adc->rx_buf) {
> - ret = -ENOMEM;
> - goto err_release;
> + dma_release_channel(adc->dma_chan);
> + return -ENOMEM;
> }
>
> - ret = dmaengine_slave_config(adc->dma_chan, &config);
> - if (ret)
> - goto err_free;
> -
> return 0;
> -
> -err_free:
> - dma_free_coherent(adc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
> - adc->rx_buf, adc->dma_buf);
> -err_release:
> - dma_release_channel(adc->dma_chan);
> -
> - return ret;
> }
>
> static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,


2019-03-24 16:48:06

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 5/8] iio: adc: stm32-dfsdm: enable hw consumer

On Thu, 21 Mar 2019 17:47:26 +0100
Fabrice Gasnier <[email protected]> wrote:

> Optionally enable IIO hw consumer, when provided (e.g. for DFSDM_IIO type).
> This is precursor patch to introduce buffer modes.
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
Applied,
Thanks,

Jonathan
> ---
> drivers/iio/adc/stm32-dfsdm-adc.c | 14 +++++++++++++-
> 1 file changed, 13 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
> index 66e2ea0..b491424 100644
> --- a/drivers/iio/adc/stm32-dfsdm-adc.c
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -630,9 +630,15 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
> /* Reset adc buffer index */
> adc->bufi = 0;
>
> + if (adc->hwc) {
> + ret = iio_hw_consumer_enable(adc->hwc);
> + if (ret < 0)
> + return ret;
> + }
> +
> ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
> if (ret < 0)
> - return ret;
> + goto err_stop_hwc;
>
> ret = stm32_dfsdm_adc_dma_start(indio_dev);
> if (ret) {
> @@ -652,6 +658,9 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
> stm32_dfsdm_adc_dma_stop(indio_dev);
> stop_dfsdm:
> stm32_dfsdm_stop_dfsdm(adc->dfsdm);
> +err_stop_hwc:
> + if (adc->hwc)
> + iio_hw_consumer_disable(adc->hwc);
>
> return ret;
> }
> @@ -667,6 +676,9 @@ static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>
> stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>
> + if (adc->hwc)
> + iio_hw_consumer_disable(adc->hwc);
> +
> return 0;
> }
>


2019-03-24 16:48:06

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 6/8] iio: adc: stm32-dfsdm: add support for scan mode

On Thu, 21 Mar 2019 17:47:27 +0100
Fabrice Gasnier <[email protected]> wrote:

> 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 <[email protected]>
Applied,

Thanks,

Jonathan

> ---
> 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)


2019-03-24 17:12:39

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 7/8] iio: adc: stm32-dfsdm: add support for buffer modes

On Thu, 21 Mar 2019 17:47:28 +0100
Fabrice Gasnier <[email protected]> wrote:

> DFSDM conversions can be launched continuously, or using various
> triggers:
> - by software
> - hardware triggers (e.g. like in stm32-adc: TIM, LPTIM, EXTI)
> - synchronously with DFSDM filter 0. e.g. for filters 1, 2
>
> Launching conversions can be done using two methods:
> a - injected:
> - scan mode can be used to convert several channels each time a
> trigger occurs.
> - When not is scan mode, channels are converted in sequence, one upon
> each trigger.
> b - regular:
> - supports software triggers or synchronous with filter 0
> - single or continuous conversions
>
> This patch finalizes DFSDM operating modes using IIO buffer modes:
> - INDIO_BUFFER_SOFTWARE: regular continuous conversions (no trigger)
> but limited to 1 channel. Users must set sampling frequency in this case.
> For filters > 1, conversions can be started synchronously with filter 0.
> - INDIO_BUFFER_TRIGGERED: triggered conversions uses injected mode for
> launching conversions. DFSDM can use hardware triggers (e.g. STM32 timer
> or lptimer), so add INDIO_HARDWARE_TRIGGERED to supported modes.
> - INDIO_DIRECT_MODE: Only support DMA-based buffer modes. In case no DMA is
> available, only support single conversions.
>
> From userland perspective, to summarize various use cases:
> 1 - single conversion on any filter:
> $ cd iio:deviceX
> $ cat in_voltageY_raw
> This uses regular a conversion (not continuous)
>
> 2 - Using sampling frequency without trigger (single channel, buffer)
> $ cd iio:deviceX
> $ echo 100 > sampling_frequency
> $ echo "" > trigger/current_trigger
> $ echo 1 > scan_elements/in_voltageY_en
> $ echo 1 > buffer/enable
> This uses regular conversion in continuous mode (Frequency is achieved
> by tuning filter parameters)
>
> 3 - sync mode with filter 0: other filters can be converted when using
> "st,filter0-sync" dt property. The conversions will get started at the
> same time as filter 0. So for any filters > 1:
> $ cd iio:deviceX
> $ echo 100 > sampling_frequency
> $ echo "" > trigger/current_trigger
> $ echo 1 > scan_elements/in_voltageY_en
> $ echo 1 > buffer/enable
> Then start filter 0 as in 2 above.
>
> 4 - Using a hardware trigger (with one channel):
> - check trigger, configure it:
> $ cat /sys/bus/iio/devices/trigger1/name
> tim6_trgo
> $ echo 100 > /sys/bus/iio/devices/trigger1/sampling_frequency
> - go to any filter:
> $ echo 1 > scan_elements/in_voltageY_en
> $ echo tim6_trgo > trigger/current_trigger
> $ echo 1 > buffer/enable
> This uses injected conversion as it uses a hardware trigger (without scan)
>
> 5 - Using a hardware trigger (with 2+ channel):
> Same as in 4/ above, but enable two or more channels in scan_elements.
> This uses injected conversion as it uses a hardware trigger (with scan mode)

Great. It may well be worth putting this doc somewhere else as well
(assuming it isn't!) People don't always find the docs hidden in commit
messages ;)

>
> Signed-off-by: Fabrice Gasnier <[email protected]>
Applied with a minor tweak for a 'false' build warning. Check I didn't
mess that up!

Thanks,

Jonathan

> ---
> Changes in v2:
> - Update the patch description to better explain working modes and the way
> to use them from user perspective
> ---
> drivers/iio/adc/stm32-dfsdm-adc.c | 218 +++++++++++++++++++++++++++++++++-----
> 1 file changed, 194 insertions(+), 24 deletions(-)
>
> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
> index 4ead6bf..51688eb 100644
> --- a/drivers/iio/adc/stm32-dfsdm-adc.c
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -12,6 +12,11 @@
> #include <linux/iio/buffer.h>
> #include <linux/iio/hw-consumer.h>
> #include <linux/iio/sysfs.h>
> +#include <linux/iio/timer/stm32-lptim-trigger.h>
> +#include <linux/iio/timer/stm32-timer-trigger.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> #include <linux/interrupt.h>
> #include <linux/module.h>
> #include <linux/of_device.h>
> @@ -121,6 +126,61 @@ static int stm32_dfsdm_str2val(const char *str,
> return -EINVAL;
> }
>
> +/**
> + * struct stm32_dfsdm_trig_info - DFSDM trigger info
> + * @name: name of the trigger, corresponding to its source
> + * @jextsel: trigger signal selection
> + */
> +struct stm32_dfsdm_trig_info {
> + const char *name;
> + unsigned int jextsel;
> +};
> +
> +/* hardware injected trigger enable, edge selection */
> +enum stm32_dfsdm_jexten {
> + STM32_DFSDM_JEXTEN_DISABLED,
> + STM32_DFSDM_JEXTEN_RISING_EDGE,
> + STM32_DFSDM_JEXTEN_FALLING_EDGE,
> + STM32_DFSDM_EXTEN_BOTH_EDGES,
> +};
> +
> +static const struct stm32_dfsdm_trig_info stm32_dfsdm_trigs[] = {
> + { TIM1_TRGO, 0 },
> + { TIM1_TRGO2, 1 },
> + { TIM8_TRGO, 2 },
> + { TIM8_TRGO2, 3 },
> + { TIM3_TRGO, 4 },
> + { TIM4_TRGO, 5 },
> + { TIM16_OC1, 6 },
> + { TIM6_TRGO, 7 },
> + { TIM7_TRGO, 8 },
> + { LPTIM1_OUT, 26 },
> + { LPTIM2_OUT, 27 },
> + { LPTIM3_OUT, 28 },
> + {},
> +};
> +
> +static int stm32_dfsdm_get_jextsel(struct iio_dev *indio_dev,
> + struct iio_trigger *trig)
> +{
> + int i;
> +
> + /* lookup triggers registered by stm32 timer trigger driver */
> + for (i = 0; stm32_dfsdm_trigs[i].name; i++) {
> + /**
> + * Checking both stm32 timer trigger type and trig name
> + * should be safe against arbitrary trigger names.
> + */
> + if ((is_stm32_timer_trigger(trig) ||
> + is_stm32_lptim_trigger(trig)) &&
> + !strcmp(stm32_dfsdm_trigs[i].name, trig->name)) {
> + return stm32_dfsdm_trigs[i].jextsel;
> + }
> + }
> +
> + return -EINVAL;
> +}
> +
> static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
> unsigned int fast, unsigned int oversamp)
> {
> @@ -265,7 +325,8 @@ static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
> }
>
> static int stm32_dfsdm_start_filter(struct stm32_dfsdm_adc *adc,
> - unsigned int fl_id)
> + unsigned int fl_id,
> + struct iio_trigger *trig)
> {
> struct stm32_dfsdm *dfsdm = adc->dfsdm;
> int ret;
> @@ -277,7 +338,7 @@ static int stm32_dfsdm_start_filter(struct stm32_dfsdm_adc *adc,
> return ret;
>
> /* Nothing more to do for injected (scan mode/triggered) conversions */
> - if (adc->nconv > 1)
> + if (adc->nconv > 1 || trig)
> return 0;
>
> /* Software start (single or continuous) regular conversion */
> @@ -294,8 +355,38 @@ static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm,
> DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
> }
>
> +static int stm32_dfsdm_filter_set_trig(struct stm32_dfsdm_adc *adc,
> + unsigned int fl_id,
> + struct iio_trigger *trig)
> +{
> + struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> + struct regmap *regmap = adc->dfsdm->regmap;
> + u32 jextsel = 0, jexten = STM32_DFSDM_JEXTEN_DISABLED;
> + int ret;
> +
> + if (trig) {
> + ret = stm32_dfsdm_get_jextsel(indio_dev, trig);
> + if (ret < 0)
> + return ret;
> +
> + /* set trigger source and polarity (default to rising edge) */
> + jextsel = ret;
> + jexten = STM32_DFSDM_JEXTEN_RISING_EDGE;
> + }
> +
> + ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
> + DFSDM_CR1_JEXTSEL_MASK | DFSDM_CR1_JEXTEN_MASK,
> + DFSDM_CR1_JEXTSEL(jextsel) |
> + DFSDM_CR1_JEXTEN(jexten));
> + if (ret < 0)
> + return ret;
> +
> + return 0;
> +}
> +
> static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
> - unsigned int fl_id)
> + unsigned int fl_id,
> + struct iio_trigger *trig)
> {
> struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> struct regmap *regmap = adc->dfsdm->regmap;
> @@ -322,6 +413,10 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
> if (ret)
> return ret;
>
> + ret = stm32_dfsdm_filter_set_trig(adc, fl_id, trig);
> + if (ret)
> + return ret;
> +
> /*
> * DFSDM modes configuration W.R.T audio/iio type modes
> * ----------------------------------------------------------------
> @@ -341,7 +436,7 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
> * | | | | sync_mode |
> * ----------------------------------------------------------------
> */
> - if (adc->nconv == 1) {
> + if (adc->nconv == 1 && !trig) {
> bit = __ffs(adc->smask);
> chan = indio_dev->channels + bit;
>
> @@ -365,13 +460,15 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
> return ret;
>
> /* Use scan mode for multiple channels */
> - cr1 = DFSDM_CR1_JSCAN(1);
> + cr1 = DFSDM_CR1_JSCAN(!!(adc->nconv > 1));
This is tripping a (false as far as I can see) build warning.
drivers/iio/adc/stm32-dfsdm-adc.c:463:23: warning: dubious: x & !y

I've modified it to a ternary operator rather than the !!

Jonathan
>
> /*
> - * Continuous conversions not supported in injected mode:
> - * - use conversions in sync with filter 0
> + * Continuous conversions not supported in injected mode,
> + * either use:
> + * - conversions in sync with filter 0
> + * - triggered conversions
> */
> - if (!fl->sync_mode)
> + if (!fl->sync_mode && !trig)
> return -EINVAL;
> cr1 |= DFSDM_CR1_JSYNC(fl->sync_mode);
> }
> @@ -503,7 +600,8 @@ 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)
> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc,
> + struct iio_trigger *trig)
> {
> struct regmap *regmap = adc->dfsdm->regmap;
> int ret;
> @@ -512,11 +610,11 @@ static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
> if (ret < 0)
> return ret;
>
> - ret = stm32_dfsdm_filter_configure(adc, adc->fl_id);
> + ret = stm32_dfsdm_filter_configure(adc, adc->fl_id, trig);
> if (ret < 0)
> goto stop_channels;
>
> - ret = stm32_dfsdm_start_filter(adc, adc->fl_id);
> + ret = stm32_dfsdm_start_filter(adc, adc->fl_id, trig);
> if (ret < 0)
> goto filter_unconfigure;
>
> @@ -548,6 +646,7 @@ static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
> {
> struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
> + unsigned int rx_buf_sz = DFSDM_DMA_BUFFER_SIZE;
>
> /*
> * DMA cyclic transfers are used, buffer is split into two periods.
> @@ -556,7 +655,7 @@ static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
> * - one buffer (period) driver pushed to ASoC side.
> */
> watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
> - adc->buf_sz = watermark * 2;
> + adc->buf_sz = min(rx_buf_sz, watermark * 2 * adc->nconv);
>
> return 0;
> }
> @@ -586,13 +685,41 @@ static unsigned int stm32_dfsdm_adc_dma_residue(struct stm32_dfsdm_adc *adc)
> return 0;
> }
>
> -static void stm32_dfsdm_audio_dma_buffer_done(void *data)
> +static irqreturn_t stm32_dfsdm_adc_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> + int available = stm32_dfsdm_adc_dma_residue(adc);
> +
> + while (available >= indio_dev->scan_bytes) {
> + u32 *buffer = (u32 *)&adc->rx_buf[adc->bufi];
> +
> + iio_push_to_buffers_with_timestamp(indio_dev, buffer,
> + pf->timestamp);
> + available -= indio_dev->scan_bytes;
> + adc->bufi += indio_dev->scan_bytes;
> + if (adc->bufi >= adc->buf_sz)
> + adc->bufi = 0;
> + }
> +
> + iio_trigger_notify_done(indio_dev->trig);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void stm32_dfsdm_dma_buffer_done(void *data)
> {
> struct iio_dev *indio_dev = data;
> struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> int available = stm32_dfsdm_adc_dma_residue(adc);
> size_t old_pos;
>
> + if (indio_dev->currentmode & INDIO_BUFFER_TRIGGERED) {
> + iio_trigger_poll_chained(indio_dev->trig);
> + return;
> + }
> +
> /*
> * FIXME: In Kernel interface does not support cyclic DMA buffer,and
> * offers only an interface to push data samples per samples.
> @@ -620,6 +747,9 @@ static void stm32_dfsdm_audio_dma_buffer_done(void *data)
> adc->bufi = 0;
> old_pos = 0;
> }
> + /* regular iio buffer without trigger */
> + if (adc->dev_data->type == DFSDM_IIO)
> + iio_push_to_buffers(indio_dev, buffer);
> }
> if (adc->cb)
> adc->cb(&adc->rx_buf[old_pos], adc->bufi - old_pos,
> @@ -643,7 +773,7 @@ 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)
> + if (adc->nconv == 1 && !indio_dev->trig)
> config.src_addr += DFSDM_RDATAR(adc->fl_id);
> else
> config.src_addr += DFSDM_JDATAR(adc->fl_id);
> @@ -660,7 +790,7 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
> if (!desc)
> return -EBUSY;
>
> - desc->callback = stm32_dfsdm_audio_dma_buffer_done;
> + desc->callback = stm32_dfsdm_dma_buffer_done;
> desc->callback_param = indio_dev;
>
> cookie = dmaengine_submit(desc);
> @@ -671,7 +801,7 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
> /* Issue pending DMA requests */
> dma_async_issue_pending(adc->dma_chan);
>
> - if (adc->nconv == 1) {
> + if (adc->nconv == 1 && !indio_dev->trig) {
> /* Enable regular DMA transfer*/
> ret = regmap_update_bits(adc->dfsdm->regmap,
> DFSDM_CR1(adc->fl_id),
> @@ -726,13 +856,19 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
> struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
> int ret;
>
> + if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
> + ret = iio_triggered_buffer_postenable(indio_dev);
> + if (ret < 0)
> + return ret;
> + }
> +
> /* Reset adc buffer index */
> adc->bufi = 0;
>
> if (adc->hwc) {
> ret = iio_hw_consumer_enable(adc->hwc);
> if (ret < 0)
> - return ret;
> + goto err_predisable;
> }
>
> ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
> @@ -745,7 +881,7 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
> goto stop_dfsdm;
> }
>
> - ret = stm32_dfsdm_start_conv(adc);
> + ret = stm32_dfsdm_start_conv(adc, indio_dev->trig);
> if (ret) {
> dev_err(&indio_dev->dev, "Can't start conversion\n");
> goto err_stop_dma;
> @@ -760,6 +896,9 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
> err_stop_hwc:
> if (adc->hwc)
> iio_hw_consumer_disable(adc->hwc);
> +err_predisable:
> + if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
> + iio_triggered_buffer_predisable(indio_dev);
>
> return ret;
> }
> @@ -777,6 +916,9 @@ static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
> if (adc->hwc)
> iio_hw_consumer_disable(adc->hwc);
>
> + if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
> + iio_triggered_buffer_predisable(indio_dev);
> +
> return 0;
> }
>
> @@ -856,7 +998,7 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>
> adc->nconv = 1;
> adc->smask = BIT(chan->scan_index);
> - ret = stm32_dfsdm_start_conv(adc);
> + ret = stm32_dfsdm_start_conv(adc, NULL);
> if (ret < 0) {
> regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
> DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
> @@ -978,6 +1120,12 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
> return -EINVAL;
> }
>
> +static int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
> + struct iio_trigger *trig)
> +{
> + return stm32_dfsdm_get_jextsel(indio_dev, trig) < 0 ? -EINVAL : 0;
> +}
> +
> static const struct iio_info stm32_dfsdm_info_audio = {
> .hwfifo_set_watermark = stm32_dfsdm_set_watermark,
> .read_raw = stm32_dfsdm_read_raw,
> @@ -986,9 +1134,11 @@ static const struct iio_info stm32_dfsdm_info_audio = {
> };
>
> static const struct iio_info stm32_dfsdm_info_adc = {
> + .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,
> + .validate_trigger = stm32_dfsdm_validate_trigger,
> };
>
> static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
> @@ -1061,6 +1211,9 @@ static int stm32_dfsdm_dma_request(struct iio_dev *indio_dev)
> return -ENOMEM;
> }
>
> + indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
> + indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops;
> +
> return 0;
> }
>
> @@ -1082,7 +1235,8 @@ static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
> * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
> */
> 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);
>
> if (adc->dev_data->type == DFSDM_AUDIO) {
> ch->scan_type.sign = 's';
> @@ -1104,9 +1258,6 @@ static int stm32_dfsdm_audio_init(struct iio_dev *indio_dev)
> struct stm32_dfsdm_channel *d_ch;
> int ret;
>
> - indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
> - indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops;
> -
> ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
> if (!ch)
> return -ENOMEM;
> @@ -1174,6 +1325,25 @@ static int stm32_dfsdm_adc_init(struct iio_dev *indio_dev)
>
> init_completion(&adc->completion);
>
> + /* Optionally request DMA */
> + if (stm32_dfsdm_dma_request(indio_dev)) {
> + dev_dbg(&indio_dev->dev, "No DMA support\n");
> + return 0;
> + }
> +
> + ret = iio_triggered_buffer_setup(indio_dev,
> + &iio_pollfunc_store_time,
> + &stm32_dfsdm_adc_trigger_handler,
> + &stm32_dfsdm_buffer_setup_ops);
> + if (ret) {
> + stm32_dfsdm_dma_release(indio_dev);
> + dev_err(&indio_dev->dev, "buffer setup failed\n");
> + return ret;
> + }
> +
> + /* lptimer/timer hardware triggers */
> + indio_dev->modes |= INDIO_HARDWARE_TRIGGERED;
> +
> return 0;
> }
>
> @@ -1221,7 +1391,7 @@ static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
>
> iio->dev.parent = dev;
> iio->dev.of_node = np;
> - iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
> + iio->modes = INDIO_DIRECT_MODE;
>
> platform_set_drvdata(pdev, adc);
>


2019-03-24 17:13:39

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v2 8/8] iio: adc: stm32-dfsdm: claim direct mode for raw read and settings

On Thu, 21 Mar 2019 17:47:29 +0100
Fabrice Gasnier <[email protected]> wrote:

> Claim direct mode to ensure no buffer mode is in use for:
> - single conversion
> - sample rate setting (must be set when filter isn't enabled).
> - oversampling ratio (must be set when filter isn't enabled).
>
> Signed-off-by: Fabrice Gasnier <[email protected]>
Applied to the togreg branch of iio.git and pushed out as testing for
the autobuilders to play with it.

Thanks,

Jonathan

> ---
> drivers/iio/adc/stm32-dfsdm-adc.c | 17 ++++++++++++++++-
> 1 file changed, 16 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
> index 51688eb..ca42946 100644
> --- a/drivers/iio/adc/stm32-dfsdm-adc.c
> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
> @@ -1039,16 +1039,23 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
>
> switch (mask) {
> case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> ret = stm32_dfsdm_set_osrs(fl, 0, val);
> if (!ret)
> adc->oversamp = val;
> -
> + iio_device_release_direct_mode(indio_dev);
> return ret;
>
> case IIO_CHAN_INFO_SAMP_FREQ:
> if (!val)
> return -EINVAL;
>
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> +
> switch (ch->src) {
> case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL:
> spi_freq = adc->dfsdm->spi_master_freq;
> @@ -1070,9 +1077,11 @@ static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
> if (ret < 0) {
> dev_err(&indio_dev->dev,
> "Not able to find parameter that match!\n");
> + iio_device_release_direct_mode(indio_dev);
> return ret;
> }
> adc->sample_freq = val;
> + iio_device_release_direct_mode(indio_dev);
>
> return 0;
> }
> @@ -1089,11 +1098,15 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
>
> switch (mask) {
> case IIO_CHAN_INFO_RAW:
> + ret = iio_device_claim_direct_mode(indio_dev);
> + if (ret)
> + return ret;
> ret = iio_hw_consumer_enable(adc->hwc);
> if (ret < 0) {
> dev_err(&indio_dev->dev,
> "%s: IIO enable failed (channel %d)\n",
> __func__, chan->channel);
> + iio_device_release_direct_mode(indio_dev);
> return ret;
> }
> ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
> @@ -1102,8 +1115,10 @@ static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
> dev_err(&indio_dev->dev,
> "%s: Conversion failed (channel %d)\n",
> __func__, chan->channel);
> + iio_device_release_direct_mode(indio_dev);
> return ret;
> }
> + iio_device_release_direct_mode(indio_dev);
> return IIO_VAL_INT;
>
> case IIO_CHAN_INFO_OVERSAMPLING_RATIO: