Commit a6096762 authored by Fabrice Gasnier's avatar Fabrice Gasnier Committed by Jonathan Cameron

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: default avatarFabrice Gasnier <fabrice.gasnier@st.com>
Signed-off-by: default avatarJonathan Cameron <Jonathan.Cameron@huawei.com>
parent 9491f75f
...@@ -40,7 +40,8 @@ ...@@ -40,7 +40,8 @@
/* Filter configuration */ /* Filter configuration */
#define DFSDM_CR1_CFG_MASK (DFSDM_CR1_RCH_MASK | DFSDM_CR1_RCONT_MASK | \ #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 { enum sd_converter_type {
DFSDM_AUDIO, DFSDM_AUDIO,
...@@ -58,6 +59,8 @@ struct stm32_dfsdm_adc { ...@@ -58,6 +59,8 @@ struct stm32_dfsdm_adc {
struct stm32_dfsdm *dfsdm; struct stm32_dfsdm *dfsdm;
const struct stm32_dfsdm_dev_data *dev_data; const struct stm32_dfsdm_dev_data *dev_data;
unsigned int fl_id; unsigned int fl_id;
unsigned int nconv;
unsigned long smask;
/* ADC specific */ /* ADC specific */
unsigned int oversamp; unsigned int oversamp;
...@@ -204,19 +207,39 @@ static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, ...@@ -204,19 +207,39 @@ static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
return 0; return 0;
} }
static int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, static int stm32_dfsdm_start_channel(struct stm32_dfsdm_adc *adc)
unsigned int ch_id)
{ {
return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id), struct iio_dev *indio_dev = iio_priv_to_dev(adc);
DFSDM_CHCFGR1_CHEN_MASK, struct regmap *regmap = adc->dfsdm->regmap;
DFSDM_CHCFGR1_CHEN(1)); 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, static void stm32_dfsdm_stop_channel(struct stm32_dfsdm_adc *adc)
unsigned int ch_id)
{ {
regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id), struct iio_dev *indio_dev = iio_priv_to_dev(adc);
DFSDM_CHCFGR1_CHEN_MASK, DFSDM_CHCFGR1_CHEN(0)); 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, static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
...@@ -241,9 +264,10 @@ 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)); 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) unsigned int fl_id)
{ {
struct stm32_dfsdm *dfsdm = adc->dfsdm;
int ret; int ret;
/* Enable filter */ /* Enable filter */
...@@ -252,7 +276,11 @@ static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, ...@@ -252,7 +276,11 @@ static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm,
if (ret < 0) if (ret < 0)
return ret; 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), return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
DFSDM_CR1_RSWSTART_MASK, DFSDM_CR1_RSWSTART_MASK,
DFSDM_CR1_RSWSTART(1)); DFSDM_CR1_RSWSTART(1));
...@@ -267,12 +295,14 @@ static void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, ...@@ -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, 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 iio_dev *indio_dev = iio_priv_to_dev(adc);
struct regmap *regmap = adc->dfsdm->regmap; struct regmap *regmap = adc->dfsdm->regmap;
struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id]; struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id];
u32 cr1; u32 cr1;
const struct iio_chan_spec *chan;
unsigned int bit, jchg = 0;
int ret; int ret;
/* Average integrator oversampling */ /* Average integrator oversampling */
...@@ -292,14 +322,59 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc, ...@@ -292,14 +322,59 @@ static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
if (ret) if (ret)
return 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 */ /* Use regular conversion for single channel without trigger */
if (indio_dev->currentmode & INDIO_BUFFER_SOFTWARE) cr1 = DFSDM_CR1_RCH(chan->channel);
cr1 |= DFSDM_CR1_RCONT(1);
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, return regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_CFG_MASK,
cr1); cr1);
...@@ -428,21 +503,20 @@ static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev, ...@@ -428,21 +503,20 @@ static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev,
return len; return len;
} }
static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc, static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
const struct iio_chan_spec *chan)
{ {
struct regmap *regmap = adc->dfsdm->regmap; struct regmap *regmap = adc->dfsdm->regmap;
int ret; int ret;
ret = stm32_dfsdm_start_channel(adc->dfsdm, chan->channel); ret = stm32_dfsdm_start_channel(adc);
if (ret < 0) if (ret < 0)
return ret; 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) if (ret < 0)
goto stop_channels; 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) if (ret < 0)
goto filter_unconfigure; goto filter_unconfigure;
...@@ -452,13 +526,12 @@ static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc, ...@@ -452,13 +526,12 @@ static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc,
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id), regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
DFSDM_CR1_CFG_MASK, 0); DFSDM_CR1_CFG_MASK, 0);
stop_channels: stop_channels:
stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel); stm32_dfsdm_stop_channel(adc);
return ret; return ret;
} }
static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc, static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
const struct iio_chan_spec *chan)
{ {
struct regmap *regmap = adc->dfsdm->regmap; struct regmap *regmap = adc->dfsdm->regmap;
...@@ -467,7 +540,7 @@ static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc, ...@@ -467,7 +540,7 @@ static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc,
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id), regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
DFSDM_CR1_CFG_MASK, 0); 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, 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) ...@@ -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 stm32_dfsdm_adc *adc = iio_priv(indio_dev);
struct dma_slave_config config = { struct dma_slave_config config = {
.src_addr = (dma_addr_t)adc->dfsdm->phys_base + .src_addr = (dma_addr_t)adc->dfsdm->phys_base,
DFSDM_RDATAR(adc->fl_id),
.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES, .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
}; };
struct dma_async_tx_descriptor *desc; struct dma_async_tx_descriptor *desc;
...@@ -571,6 +643,10 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev) ...@@ -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__, dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
adc->buf_sz, adc->buf_sz / 2); 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); ret = dmaengine_slave_config(adc->dma_chan, &config);
if (ret) if (ret)
return ret; return ret;
...@@ -595,9 +671,20 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev) ...@@ -595,9 +671,20 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
/* Issue pending DMA requests */ /* Issue pending DMA requests */
dma_async_issue_pending(adc->dma_chan); dma_async_issue_pending(adc->dma_chan);
/* Enable DMA transfer*/ if (adc->nconv == 1) {
ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR1(adc->fl_id), /* Enable regular DMA transfer*/
DFSDM_CR1_RDMAEN_MASK, DFSDM_CR1_RDMAEN_MASK); 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) if (ret < 0)
goto err_stop_dma; goto err_stop_dma;
...@@ -617,14 +704,26 @@ static void stm32_dfsdm_adc_dma_stop(struct iio_dev *indio_dev) ...@@ -617,14 +704,26 @@ static void stm32_dfsdm_adc_dma_stop(struct iio_dev *indio_dev)
return; return;
regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR1(adc->fl_id), 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); 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) static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
{ {
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
const struct iio_chan_spec *chan = &indio_dev->channels[0];
int ret; int ret;
/* Reset adc buffer index */ /* Reset adc buffer index */
...@@ -646,7 +745,7 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev) ...@@ -646,7 +745,7 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
goto stop_dfsdm; goto stop_dfsdm;
} }
ret = stm32_dfsdm_start_conv(adc, chan); ret = stm32_dfsdm_start_conv(adc);
if (ret) { if (ret) {
dev_err(&indio_dev->dev, "Can't start conversion\n"); dev_err(&indio_dev->dev, "Can't start conversion\n");
goto err_stop_dma; goto err_stop_dma;
...@@ -668,9 +767,8 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev) ...@@ -668,9 +767,8 @@ static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
static int stm32_dfsdm_predisable(struct iio_dev *indio_dev) static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
{ {
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
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); stm32_dfsdm_adc_dma_stop(indio_dev);
...@@ -756,7 +854,9 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev, ...@@ -756,7 +854,9 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
if (ret < 0) if (ret < 0)
goto stop_dfsdm; 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) { if (ret < 0) {
regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id), regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0)); DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
...@@ -777,7 +877,7 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev, ...@@ -777,7 +877,7 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
else else
ret = IIO_VAL_INT; ret = IIO_VAL_INT;
stm32_dfsdm_stop_conv(adc, chan); stm32_dfsdm_stop_conv(adc);
stop_dfsdm: stop_dfsdm:
stm32_dfsdm_stop_dfsdm(adc->dfsdm); stm32_dfsdm_stop_dfsdm(adc->dfsdm);
...@@ -882,11 +982,13 @@ static const struct iio_info stm32_dfsdm_info_audio = { ...@@ -882,11 +982,13 @@ static const struct iio_info stm32_dfsdm_info_audio = {
.hwfifo_set_watermark = stm32_dfsdm_set_watermark, .hwfifo_set_watermark = stm32_dfsdm_set_watermark,
.read_raw = stm32_dfsdm_read_raw, .read_raw = stm32_dfsdm_read_raw,
.write_raw = stm32_dfsdm_write_raw, .write_raw = stm32_dfsdm_write_raw,
.update_scan_mode = stm32_dfsdm_update_scan_mode,
}; };
static const struct iio_info stm32_dfsdm_info_adc = { static const struct iio_info stm32_dfsdm_info_adc = {
.read_raw = stm32_dfsdm_read_raw, .read_raw = stm32_dfsdm_read_raw,
.write_raw = stm32_dfsdm_write_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) static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment