[alsa-devel] [PATCH v3 05/11] IIO: ADC: add stm32 DFSDM support for Sigma delta ADC
Jonathan Cameron
jic23 at kernel.org
Sat Mar 25 16:53:54 CET 2017
On 20/03/17 11:24, Arnaud Pouliquen wrote:
> Hello Jonathan
>
> Thanks for your comments
> Few answers in-line.
>
> Regards
> Arnaud
>
> On 03/19/2017 11:25 PM, Jonathan Cameron wrote:
>> On 17/03/17 14:08, Arnaud Pouliquen wrote:
>>> Add driver for stm32 DFSDM IP. This IP converts a sigma delta stream
>>> in n bit samples through a low pass filter and an integrator.
>>> stm32-dfsdm-adc driver allows to handle sigma delta ADC.
>>>
>>> Signed-off-by: Arnaud Pouliquen <arnaud.pouliquen at st.com>
>> Various minor bits inline.
>>
>> I'm mostly liking this. I do slightly wondering if semantically it
>> should be the front end that has the channels rather than the
>> backend. Would be fiddly to do though and probably not worth the
>> hassle.
> DFSDM support the scan mode, so several front ends can be connected to
> One filter. In this case not possible to expose channel FE.
It still could but would admittedly get really fiddly and require demuxing
the scan...
>
>>
>> Would love to see it running in a continuous mode in IIO, but
>> I guess that can follow along later.
> Yes for the rest of the management it should be quite close to the
> stm32-adc driver.
>
>>
>> The comment about the trigger has me confused
>> - perhaps you could elaborate further on that?
> Code associated to the trigger should be part of the [PATCH v3 06/11]
> IIO: ADC: add stm32 DFSDM support for PDM microphone, as it concern the
> audio part...
> I did not found a way to use consumer.h interface to enable DFSDM IIO,
> without defining triggered buffer. that's why i defined a trigger and
> use it.
> But i just saw that my reasoning is wrong. I'm linked to trigger in
> stm32-dfsdm-audio.c because i use iio_triggered_buffer_postenable and
> iio_triggered_buffer_predisable.
This used to be more obvious until we put those boiler plate functions in
to avoid lots of replication. Pretty much everything should be optional.
> As i don't use the callback for buffer
> no need to call it...i can call the ASoC callback directly in DMA IRQ.
> Still a hack but more logic...
Cool. We definitely need to clean this up long term, but perhaps not
as part of this initially at least!
I hate holding drivers up for internal stuff that we can change in
our own good time!
>
>>
>> Jonathan
>>> ---
>>> V2 -> V3 :
>>> - Split audio and ADC support in 2 drivers
>>> - Implement DMA cyclic mode
>>> - Add SPI bus Trigger for buffer management
>>>
>>> drivers/iio/adc/Kconfig | 26 ++
>>> drivers/iio/adc/Makefile | 2 +
>>> drivers/iio/adc/stm32-dfsdm-adc.c | 419 +++++++++++++++++++++++
>>> drivers/iio/adc/stm32-dfsdm-core.c | 658 +++++++++++++++++++++++++++++++++++++
>>> drivers/iio/adc/stm32-dfsdm.h | 372 +++++++++++++++++++++
>>> 5 files changed, 1477 insertions(+)
>>> create mode 100644 drivers/iio/adc/stm32-dfsdm-adc.c
>>> create mode 100644 drivers/iio/adc/stm32-dfsdm-core.c
>>> create mode 100644 drivers/iio/adc/stm32-dfsdm.h
>>>
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index d411d66..3e0eb11 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -452,6 +452,32 @@ config STM32_ADC
>>> This driver can also be built as a module. If so, the module
>>> will be called stm32-adc.
>>>
>>> +config STM32_DFSDM_CORE
>>> + tristate "STMicroelectronics STM32 dfsdm core"
>>> + depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>> + select REGMAP
>>> + select REGMAP_MMIO
>>> + help
>>> + Select this option to enable the driver for STMicroelectronics
>>> + STM32 digital filter for sigma delta converter.
>>> +
>>> + This driver can also be built as a module. If so, the module
>>> + will be called stm32-dfsdm-core.
>>> +
>>> +config STM32_DFSDM_ADC
>>> + tristate "STMicroelectronics STM32 dfsdm adc"
>>> + depends on (ARCH_STM32 && OF) || COMPILE_TEST
>>> + select STM32_DFSDM_CORE
>>> + select REGMAP_MMIO
>>> + select IIO_BUFFER_DMAENGINE
>>> + select IIO_HW_CONSUMER
>>> + help
>>> + Select this option to support ADCSigma delta modulator for
>>> + STMicroelectronics STM32 digital filter for sigma delta converter.
>>> +
>>> + This driver can also be built as a module. If so, the module
>>> + will be called stm32-dfsdm-adc.
>>> +
>>> config STX104
>>> tristate "Apex Embedded Systems STX104 driver"
>>> depends on X86 && ISA_BUS_API
>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>> index c68819c..161f271 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -43,6 +43,8 @@ obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>>> obj-$(CONFIG_STX104) += stx104.o
>>> obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
>>> obj-$(CONFIG_STM32_ADC) += stm32-adc.o
>>> +obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
>>> +obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
>>> obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>>> obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
>>> obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
>>> diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
>>> new file mode 100644
>>> index 0000000..ebcb3b4
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm-adc.c
>>> @@ -0,0 +1,419 @@
>>> +/*
>>> + * This file is the ADC part of of the STM32 DFSDM driver
>>> + *
>>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>>> + * Author: Arnaud Pouliquen <arnaud.pouliquen at st.com>.
>>> + *
>>> + * License type: GPLv2
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but
>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
>>> + * or FITNESS FOR A PARTICULAR PURPOSE.
>>> + * See the GNU General Public License for more details.
>>> + *
>>> + * You should have received a copy of the GNU General Public License along with
>>> + * this program. If not, see <http://www.gnu.org/licenses/>.
>>> + */
>>> +
>>> +#include <linux/interrupt.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/iio/hw_consumer.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/sysfs.h>
>>> +
>>> +#include "stm32-dfsdm.h"
>>> +
>>> +#define DFSDM_TIMEOUT_US 100000
>>> +#define DFSDM_TIMEOUT (msecs_to_jiffies(DFSDM_TIMEOUT_US / 1000))
>>> +
>>> +struct stm32_dfsdm_adc {
>>> + struct stm32_dfsdm *dfsdm;
>>> + unsigned int fl_id;
>>> + unsigned int ch_id;
>>> +
>>> + unsigned int oversamp;
>>> +
>>> + struct completion completion;
>>> +
>>> + u32 *buffer;
>>> +
>>> + /* Hardware consumer structure for Front End IIO */
>>> + struct iio_hw_consumer *hwc;
>>> +};
>>> +
>>> +static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc)
>>> +{
>>> + int ret;
>>> +
>>> + ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + ret = stm32_dfsdm_start_channel(adc->dfsdm, adc->ch_id);
>>> + if (ret < 0)
>>> + goto stop_dfsdm;
>>> +
>>> + ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id, adc->ch_id);
>>> + if (ret < 0)
>>> + goto stop_channels;
>>> +
>>> + ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
>>> + if (ret < 0)
>>> + goto stop_channels;
>>> +
>>> + return 0;
>>> +
>>> +stop_channels:
>>> + stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>>> +stop_dfsdm:
>>> + stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
>>> +{
>>> + stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
>>> +
>>> + stm32_dfsdm_stop_channel(adc->dfsdm, adc->ch_id);
>>> +
>>> + stm32_dfsdm_stop_dfsdm(adc->dfsdm);
>>> +}
>>> +
>>> +static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev,
>>> + const struct iio_chan_spec *chan, int *res)
>>> +{
>>> + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> + long timeout;
>>> + int ret;
>>> +
>>> + reinit_completion(&adc->completion);
>>> +
>>> + adc->buffer = res;
>>> +
>>> + /* Unmask IRQ for regular conversion achievement*/
>>> + ret = regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>>> + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(1));
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + ret = stm32_dfsdm_start_conv(adc);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + timeout = wait_for_completion_interruptible_timeout(&adc->completion,
>>> + DFSDM_TIMEOUT);
>> blank line perhaps.
>>> + /* Mask IRQ for regular conversion achievement*/
>>> + regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>>> + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>>> +
>>> + if (timeout == 0) {
>>> + dev_warn(&indio_dev->dev, "Conversion timed out!\n");
>>> + ret = -ETIMEDOUT;
>>> + } else if (timeout < 0) {
>>> + ret = timeout;
>>> + } else {
>>> + dev_dbg(&indio_dev->dev, "Converted val %#x\n", *res);
>>> + ret = IIO_VAL_INT;
>>> + }
>>> +
>>> + /* Mask IRQ for regular conversion achievement*/
>>> + regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
>>> + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>>> +
>>> + stm32_dfsdm_stop_conv(adc);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +static int stm32_dfsdm_write_raw(struct iio_dev *indio_dev,
>>> + struct iio_chan_spec const *chan,
>>> + int val, int val2, long mask)
>>> +{
>>> + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> + struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
>>> + int ret = -EINVAL;
>>> +
>>> + if (mask == IIO_CHAN_INFO_OVERSAMPLING_RATIO) {
>>> + ret = stm32_dfsdm_set_osrs(fl, 0, val);
>>> + if (!ret)
>>> + adc->oversamp = val;
>>> + }
>> blank line here.
>>> + return ret;
>>> +}
>>> +
>>> +static int stm32_dfsdm_read_raw(struct iio_dev *indio_dev,
>>> + struct iio_chan_spec const *chan, int *val,
>>> + int *val2, long mask)
>>> +{
>>> + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> + int ret;
>>> +
>>> + switch (mask) {
>>> + case IIO_CHAN_INFO_RAW:
>>> + 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);
>>> + return ret;
>>> + }
>>> + ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
>>> + if (ret < 0) {
>>> + dev_err(&indio_dev->dev,
>>> + "%s: Conversion failed (channel %d)\n",
>>> + __func__, chan->channel);
>>> + return ret;
>>> + }
>>> +
>>> + iio_hw_consumer_disable(adc->hwc);
>>> +
>>> + return IIO_VAL_INT;
>>> +
>>> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>>> + *val = adc->oversamp;
>>> +
>>> + return IIO_VAL_INT;
>>> + }
>>> +
>>> + return -EINVAL;
>>> +}
>>> +
>>> +static const struct iio_info stm32_dfsdm_info_adc = {
>>> + .read_raw = stm32_dfsdm_read_raw,
>>> + .write_raw = stm32_dfsdm_write_raw,
>>> + .driver_module = THIS_MODULE,
>>> +};
>>> +
>>> +static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
>>> +{
>>> + struct stm32_dfsdm_adc *adc = arg;
>>> + struct regmap *regmap = adc->dfsdm->regmap;
>>> + unsigned int status;
>>> +
>>> + regmap_read(regmap, DFSDM_ISR(adc->fl_id), &status);
>>> +
>>> + if (status & DFSDM_ISR_REOCF_MASK) {
>>> + /* read the data register clean the IRQ status */
>>> + regmap_read(regmap, DFSDM_RDATAR(adc->fl_id), adc->buffer);
>>> + complete(&adc->completion);
>>> + }
>>> + if (status & DFSDM_ISR_ROVRF_MASK) {
>> What's this one? Might want a comment given it's an irq you basically eat.
> Yes at least an error message that to inform on an overrun.
>>> + regmap_update_bits(regmap, DFSDM_ICR(adc->fl_id),
>>> + DFSDM_ICR_CLRROVRF_MASK,
>>> + DFSDM_ICR_CLRROVRF_MASK);
>>> + }
>>> +
>>> + return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
>>> +{
>>> + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +
>>> + return stm32_dfsdm_start_conv(adc);
>>> +}
>>> +
>>> +static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
>>> +{
>>> + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> +
>>> + stm32_dfsdm_stop_conv(adc);
>> blank line.
>>> + return 0;
>>> +}
>>> +
>>> +static const struct iio_buffer_setup_ops stm32_dfsdm_buffer_setup_ops = {
>>> + .postenable = &stm32_dfsdm_postenable,
>>> + .predisable = &stm32_dfsdm_predisable,
>>> +};
>>> +
>>> +static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
>>> + struct iio_chan_spec *chan,
>>> + int ch_idx)
>>> +{
>>> + struct iio_chan_spec *ch = &chan[ch_idx];
>>> + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> + int ret;
>>> +
>>> + ret = stm32_dfsdm_channel_parse_of(adc->dfsdm, indio_dev, chan, ch_idx);
>>> +
>>> + ch->type = IIO_VOLTAGE;
>>> + ch->indexed = 1;
>>> + ch->scan_index = ch_idx;
>>> +
>>> + /*
>>> + * IIO_CHAN_INFO_RAW: used to compute regular conversion
>>> + * IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
>>> + */
>>> + ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>>> + BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
>>> +
>>> + ch->scan_type.sign = 'u';
>>> + ch->scan_type.realbits = 24;
>>> + ch->scan_type.storagebits = 32;
>>> + adc->ch_id = ch->channel;
>>> +
>>> + return stm32_dfsdm_chan_configure(adc->dfsdm,
>>> + &adc->dfsdm->ch_list[ch->channel]);
>>> +}
>>> +
>>> +static int stm32_dfsdm_adc_chan_init(struct iio_dev *indio_dev)
>>> +{
>>> + struct iio_chan_spec *channels;
>>> + struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
>>> + unsigned int num_ch;
>>> + int ret, chan_idx;
>>> +
>>> + num_ch = of_property_count_u32_elems(indio_dev->dev.of_node,
>>> + "st,adc-channels");
>>> + if (num_ch < 0 || num_ch >= adc->dfsdm->num_chs) {
>>> + dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>>> + return num_ch < 0 ? num_ch : -EINVAL;
>>> + }
>>> +
>>> + /*
>>> + * Number of channel per filter is temporary limited to 1.
>>> + * Restriction should be cleaned with scan mode
>>> + */
>>> + if (num_ch > 1) {
>>> + dev_err(&indio_dev->dev, "Multi channel not yet supported\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + /* Bind to SD modulator IIO device */
>>> + adc->hwc = iio_hw_consumer_alloc(&indio_dev->dev);
>>> + if (IS_ERR(adc->hwc))
>>> + return -EPROBE_DEFER;
>>> +
>>> + channels = devm_kcalloc(&indio_dev->dev, num_ch, sizeof(*channels),
>>> + GFP_KERNEL);
>>> + if (!channels)
>>> + return -ENOMEM;
>>> +
>>> + for (chan_idx = 0; chan_idx < num_ch; chan_idx++) {
>>> + ret = stm32_dfsdm_adc_chan_init_one(indio_dev, channels,
>>> + chan_idx);
>>> + if (ret < 0)
>>> + goto free_hwc;
>>> + }
>>> +
>>> + indio_dev->num_channels = num_ch;
>>> + indio_dev->channels = channels;
>>> +
>>> + return 0;
>>> +
>>> +free_hwc:
>>> + iio_hw_consumer_free(adc->hwc);
>> Given you have to free this in the error path, I would imagine you will
>> need a free somewhere in the main remove path? Or just create a devm
>> version of iio_hw_consumer_alloc. It will be useful in the long run.
>>> + return ret;
>>> +}
>>> +
>>> +static const struct of_device_id stm32_dfsdm_adc_match[] = {
>>> + { .compatible = "st,stm32-dfsdm-adc"},
>>> + {}
>>> +};
>>> +
>>> +static int stm32_dfsdm_adc_probe(struct platform_device *pdev)
>>> +{
>>> + struct device *dev = &pdev->dev;
>>> + struct stm32_dfsdm_adc *adc;
>>> + struct device_node *np = dev->of_node;
>>> + struct iio_dev *iio;
>>> + char *name;
>>> + int ret, irq, val;
>>> +
>>> + iio = devm_iio_device_alloc(dev, sizeof(*adc));
>>> + if (IS_ERR(iio)) {
>>> + dev_err(dev, "%s: Failed to allocate IIO\n", __func__);
>>> + return PTR_ERR(iio);
>>> + }
>>> +
>>> + adc = iio_priv(iio);
>>> + if (IS_ERR(adc)) {
>>> + dev_err(dev, "%s: Failed to allocate ADC\n", __func__);
>>> + return PTR_ERR(adc);
>>> + }
>>> + adc->dfsdm = dev_get_drvdata(dev->parent);
>>> +
>>> + iio->dev.parent = dev;
>>> + iio->dev.of_node = np;
>>> + iio->info = &stm32_dfsdm_info_adc;
>>> + iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
>>> +
>>> + platform_set_drvdata(pdev, adc);
>>> +
>>> + ret = of_property_read_u32(dev->of_node, "reg", &adc->fl_id);
>>> + if (ret != 0) {
>>> + dev_err(dev, "Missing reg property\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + name = kzalloc(sizeof("dfsdm-adc0"), GFP_KERNEL);
>> not freed. Maybe devm_kzalloc
>>> + if (!name)
>>> + return -ENOMEM;
>>> + snprintf(name, sizeof("dfsdm-adc0"), "dfsdm-adc%d", adc->fl_id);
>>> + iio->name = name;
>>> +
>>> + /*
>>> + * In a first step IRQs generated for channels are not treated.
>>> + * So IRQ associated to filter instance 0 is dedicated to the Filter 0.
>>> + */
>>> + irq = platform_get_irq(pdev, 0);
>>> + ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
>>> + 0, pdev->name, adc);
>>> + if (ret < 0) {
>>> + dev_err(dev, "Failed to request IRQ\n");
>>> + return ret;
>>> + }
>>> +
>>> + ret = of_property_read_u32(dev->of_node, "st,filter-order", &val);
>>> + if (ret < 0) {
>>> + dev_err(dev, "Failed to set filter order\n");
>>> + return ret;
>>> + }
>>> + adc->dfsdm->fl_list[adc->fl_id].ford = val;
>>> +
>>> + ret = of_property_read_u32(dev->of_node, "st,filter0-sync", &val);
>>> + if (!ret)
>>> + adc->dfsdm->fl_list[adc->fl_id].sync_mode = val;
>>> +
>>> + ret = stm32_dfsdm_adc_chan_init(iio);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + init_completion(&adc->completion);
>>> +
>>> + return iio_device_register(iio);
>>> +}
>>> +
>>> +static int stm32_dfsdm_adc_remove(struct platform_device *pdev)
>>> +{
>>> + struct stm32_dfsdm_adc *adc = platform_get_drvdata(pdev);
>>> + struct iio_dev *iio = iio_priv_to_dev(adc);
>>> +
>>> + iio_device_unregister(iio);
>> If all you have is this in remove, you can probably get away with
>> devm_iio_device_register and get rid of the remove entirely.
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static struct platform_driver stm32_dfsdm_adc_driver = {
>>> + .driver = {
>>> + .name = "stm32-dfsdm-adc",
>>> + .of_match_table = stm32_dfsdm_adc_match,
>>> + },
>>> + .probe = stm32_dfsdm_adc_probe,
>>> + .remove = stm32_dfsdm_adc_remove,
>>> +};
>>> +module_platform_driver(stm32_dfsdm_adc_driver);
>>> +
>>> +MODULE_DESCRIPTION("STM32 sigma delta ADC");
>>> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen at st.com>");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
>>> new file mode 100644
>>> index 0000000..488e456
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm-core.c
>>> @@ -0,0 +1,658 @@
>>> +/*
>>> + * This file is part the core part STM32 DFSDM driver
>>> + *
>>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>>> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen at st.com> for STMicroelectronics.
>>> + *
>>> + * License terms: GPL V2.0.
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but
>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
>>> + * details.
>>> + */
>>> +
>>> +#include <linux/clk.h>
>>> +#include <linux/interrupt.h>
>>> +#include <linux/module.h>
>>> +#include <linux/of_device.h>
>>> +#include <linux/regmap.h>
>>> +#include <linux/slab.h>
>>> +
>>> +#include <linux/iio/trigger.h>
>>> +#include <linux/iio/sysfs.h>
>>> +
>>> +#include "stm32-dfsdm.h"
>>> +
>>> +struct stm32_dfsdm_dev_data {
>>> + unsigned int num_filters;
>>> + unsigned int num_channels;
>>> + const struct regmap_config *regmap_cfg;
>>> +};
>>> +
>>> +#define STM32H7_DFSDM_NUM_FILTERS 4
>>> +#define STM32H7_DFSDM_NUM_CHANNELS 8
>>> +
>>> +#define DFSDM_MAX_INT_OVERSAMPLING 256
>>> +
>>> +#define DFSDM_MAX_FL_OVERSAMPLING 1024
>>> +
>>> +#define DFSDM_MAX_RES BIT(31)
>>> +#define DFSDM_DATA_RES BIT(23)
>>> +
>>> +static bool stm32_dfsdm_volatile_reg(struct device *dev, unsigned int reg)
>>> +{
>>> + if (reg < DFSDM_FILTER_BASE_ADR)
>>> + return false;
>>> +
>>> + /*
>>> + * Mask is done on register to avoid to list registers of all them
>>> + * filter instances.
>>> + */
>>> + switch (reg & DFSDM_FILTER_REG_MASK) {
>>> + case DFSDM_CR1(0) & DFSDM_FILTER_REG_MASK:
>>> + case DFSDM_ISR(0) & DFSDM_FILTER_REG_MASK:
>>> + case DFSDM_JDATAR(0) & DFSDM_FILTER_REG_MASK:
>>> + case DFSDM_RDATAR(0) & DFSDM_FILTER_REG_MASK:
>>> + return true;
>>> + }
>>> +
>>> + return false;
>>> +}
>>> +
>>> +static const struct regmap_config stm32h7_dfsdm_regmap_cfg = {
>>> + .reg_bits = 32,
>>> + .val_bits = 32,
>>> + .reg_stride = sizeof(u32),
>>> + .max_register = 0x2B8,
>>> + .volatile_reg = stm32_dfsdm_volatile_reg,
>>> + .fast_io = true,
>>> +};
>>> +
>>> +static const struct stm32_dfsdm_dev_data stm32h7_dfsdm_data = {
>>> + .num_filters = STM32H7_DFSDM_NUM_FILTERS,
>>> + .num_channels = STM32H7_DFSDM_NUM_CHANNELS,
>>> + .regmap_cfg = &stm32h7_dfsdm_regmap_cfg,
>>> +};
>>> +
>>> +struct dfsdm_priv {
>>> + struct platform_device *pdev; /* platform device*/
>>> +
>>> + struct stm32_dfsdm dfsdm; /* common data exported for all instances */
>>> +
>>> + unsigned int spi_clk_out_div; /* SPI clkout divider value */
>>> + atomic_t n_active_ch; /* number of current active channels */
>>> +
>>> + /* Clock */
>>> + struct clk *clk; /* DFSDM clock */
>>> + struct clk *aclk; /* audio clock */
>>> +};
>>> +
>>> +/**
>>> + * stm32_dfsdm_set_osrs - compute filter parameters.
>> Naming would suggest it's more specific than this.
>> Setting over sampling ratios?
> Right, it is a computation not a set.
>>> + *
>>> + * Enable interface if n_active_ch is not null.
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fast: Fast mode enabled or disabled
>>> + * @oversamp: Expected oversampling between filtered sample and SD input stream
>>> + */
>>> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
>>> + unsigned int oversamp)
>>> +{
>>> + unsigned int i, d, fosr, iosr;
>>> + u64 res;
>>> + s64 delta;
>>> + unsigned int m = 1; /* multiplication factor */
>>> + unsigned int p = fl->ford; /* filter order (ford) */
>>> +
>>> + pr_debug("%s: Requested oversampling: %d\n", __func__, oversamp);
>>> + /*
>>> + * This function tries to compute filter oversampling and integrator
>>> + * oversampling, base on oversampling ratio requested by user.
>>> + *
>>> + * Decimation d depends on the filter order and the oversampling ratios.
>>> + * ford: filter order
>>> + * fosr: filter over sampling ratio
>>> + * iosr: integrator over sampling ratio
>>> + */
>>> + if (fl->ford == DFSDM_FASTSINC_ORDER) {
>>> + m = 2;
>>> + p = 2;
>>> + }
>>> +
>>> + /*
>>> + * Looks for filter and integrator oversampling ratios which allows
>>> + * to reach 24 bits data output resolution.
>>> + * Leave at once if exact resolution if reached.
>>> + * Otherwise the higher resolution below 32 bits is kept.
>>> + */
>>> + for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) {
>>> + for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) {
>>> + if (fast)
>>> + d = fosr * iosr;
>>> + else if (fl->ford == DFSDM_FASTSINC_ORDER)
>>> + d = fosr * (iosr + 3) + 2;
>>> + else
>>> + d = fosr * (iosr - 1 + p) + p;
>>> +
>>> + if (d > oversamp)
>>> + break;
>>> + else if (d != oversamp)
>>> + continue;
>>> + /*
>>> + * Check resolution (limited to signed 32 bits)
>>> + * res <= 2^31
>>> + * Sincx filters:
>>> + * res = m * fosr^p x iosr (with m=1, p=ford)
>>> + * FastSinc filter
>>> + * res = m * fosr^p x iosr (with m=2, p=2)
>>> + */
>>> + res = fosr;
>>> + for (i = p - 1; i > 0; i--) {
>>> + res = res * (u64)fosr;
>>> + if (res > DFSDM_MAX_RES)
>>> + break;
>>> + }
>>> + if (res > DFSDM_MAX_RES)
>>> + continue;
>>> + res = res * (u64)m * (u64)iosr;
>>> + if (res > DFSDM_MAX_RES)
>>> + continue;
>>> +
>>> + delta = res - DFSDM_DATA_RES;
>>> +
>>> + if (res >= fl->res) {
>>> + fl->res = res;
>>> + fl->fosr = fosr;
>>> + fl->iosr = iosr;
>>> + fl->fast = fast;
>>> + pr_debug("%s: fosr = %d, iosr = %d\n",
>>> + __func__, fl->fosr, fl->iosr);
>>> + }
>>> +
>>> + if (!delta)
>>> + return 0;
>>> + }
>>> + }
>>> +
>>> + if (!fl->fosr)
>>> + return -EINVAL;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_start_dfsdm - start global dfsdm IP interface.
>>> + *
>>> + * Enable interface if n_active_ch is not null.
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + */
>>> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
>>> +{
>>> + struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> + struct device *dev = &priv->pdev->dev;
>>> + unsigned int clk_div = priv->spi_clk_out_div;
>>> + int ret;
>>> +
>>> + if (atomic_inc_return(&priv->n_active_ch) == 1) {
>>> + /* Enable clocks */
>>> + ret = clk_prepare_enable(priv->clk);
>>> + if (ret < 0) {
>>> + dev_err(dev, "Failed to start clock\n");
>>> + return ret;
>>> + }
>>> + if (priv->aclk) {
>>> + ret = clk_prepare_enable(priv->aclk);
>>> + if (ret < 0) {
>>> + dev_err(dev, "Failed to start audio clock\n");
>>> + goto disable_clk;
>>> + }
>>> + }
>>> +
>>> + /* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
>>> + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> + DFSDM_CHCFGR1_CKOUTDIV_MASK,
>>> + DFSDM_CHCFGR1_CKOUTDIV(clk_div));
>>> + if (ret < 0)
>>> + goto disable_aclk;
>>> +
>>> + /* Global enable of DFSDM interface */
>>> + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> + DFSDM_CHCFGR1_DFSDMEN_MASK,
>>> + DFSDM_CHCFGR1_DFSDMEN(1));
>>> + if (ret < 0)
>>> + goto disable_aclk;
>>> + }
>>> +
>>> + dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
>>> + atomic_read(&priv->n_active_ch));
>>> +
>>> + return 0;
>>> +
>>> +disable_aclk:
>>> + clk_disable_unprepare(priv->aclk);
>>> +disable_clk:
>>> + clk_disable_unprepare(priv->clk);
>>> +
>>> + return ret;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_stop_dfsdm - stop global DFSDM IP interface.
>>> + *
>>> + * Disable interface if n_active_ch is null
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + */
>>> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
>>> +{
>>> + struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
>>> + int ret;
>>> +
>>> + if (atomic_dec_and_test(&priv->n_active_ch)) {
>>> + /* Global disable of DFSDM interface */
>>> + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> + DFSDM_CHCFGR1_DFSDMEN_MASK,
>>> + DFSDM_CHCFGR1_DFSDMEN(0));
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + /* Stop SPI CLKOUT */
>>> + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
>>> + DFSDM_CHCFGR1_CKOUTDIV_MASK,
>>> + DFSDM_CHCFGR1_CKOUTDIV(0));
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + /* Disable clocks */
>>> + clk_disable_unprepare(priv->clk);
>>> + if (priv->aclk)
>>> + clk_disable_unprepare(priv->aclk);
>>> + }
>>> + dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
>>> + atomic_read(&priv->n_active_ch));
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_start_channel
>>> + * Start DFSDM IP channels and associated interface.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @ch_id: Channel index.
>>> + */
>>> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
>>> +{
>>> + return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
>>> + DFSDM_CHCFGR1_CHEN_MASK,
>>> + DFSDM_CHCFGR1_CHEN(1));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_stop_channel
>>> + * Stop DFSDM IP channels and associated interface.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @ch_id: Channel index.
>>> + */
>>> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id)
>>> +{
>>> + regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
>>> + DFSDM_CHCFGR1_CHEN_MASK,
>>> + DFSDM_CHCFGR1_CHEN(0));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_chan_configure
>>> + * Configure DFSDM IP channels and associated interface.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @ch_id: channel index.
>>> + */
>>> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
>>> + struct stm32_dfsdm_channel *ch)
>>> +{
>>> + unsigned int id = ch->id;
>>> + struct regmap *regmap = dfsdm->regmap;
>>> + int ret;
>>> +
>>> + ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>>> + DFSDM_CHCFGR1_SITP_MASK,
>>> + DFSDM_CHCFGR1_SITP(ch->type));
>>> + if (ret < 0)
>>> + return ret;
>> Blank line here and in similar places makes it easier for my
>> eyes to parse at least...
>> I'd also like to see some docs in here, not all of these
>> are self explanatory.
> I will apply recommendation in my whole code for next time
>>> + ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>>> + DFSDM_CHCFGR1_SPICKSEL_MASK,
>>> + DFSDM_CHCFGR1_SPICKSEL(ch->src));
>>> + if (ret < 0)
>>> + return ret;
>>> + return regmap_update_bits(regmap, DFSDM_CHCFGR1(id),
>>> + DFSDM_CHCFGR1_CHINSEL_MASK,
>>> + DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_start_filter - Start DFSDM IP filter conversion.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter index.
>>> + */
>>> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
>>> +{
>>> + int ret;
>>> +
>>> + /* Enable filter */
>>> + ret = regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>>> + DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(1));
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + /* Start conversion */
>>> + return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>>> + DFSDM_CR1_RSWSTART_MASK,
>>> + DFSDM_CR1_RSWSTART(1));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_stop_filter - Stop DFSDM IP filter conversion.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: Filter index.
>>> + */
>>> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id)
>>> +{
>>> + /* Mask IRQ for regular conversion achievement*/
>>> + regmap_update_bits(dfsdm->regmap, DFSDM_CR2(fl_id),
>>> + DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
>>> + /* Disable conversion */
>>> + regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
>>> + DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
>>> +}
>>> +
>>> +/**
>>> + * stm32_dfsdm_filter_configure - Configure DFSDM IP filter and associate it
>>> + * to channel.
>>> + *
>>> + * @dfsdm: Handle used to retrieve dfsdm context.
>>> + * @fl_id: channel index.
>>> + * @fl_id: Filter index.
>>> + */
>>> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>>> + unsigned int ch_id)
>>> +{
>>> + struct regmap *regmap = dfsdm->regmap;
>>> + struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
>>> + int ret;
>>> +
>>> + /* Average integrator oversampling */
>>> + ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
>>> + DFSDM_FCR_IOSR(fl->iosr));
>>> +
>>> + /* Filter order and Oversampling */
>> Please handle each error properly as it happens rather than mudling onwards.
>> If there is reason for this odd construction, then document it clearly.
> If you mention the checks on ret value that are missing at end of
> functions, yes dirty code to fix.
>
>>> + if (!ret)
>>> + ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
>>> + DFSDM_FCR_FOSR_MASK,
>>> + DFSDM_FCR_FOSR(fl->fosr));
>>> +
>>> + if (!ret)
>>> + ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id),
>>> + DFSDM_FCR_FORD_MASK,
>>> + DFSDM_FCR_FORD(fl->ford));
>>> +
>>> + /* If only one channel 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));
>>> +
>>> + return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
>>> + DFSDM_CR1_RSYNC_MASK,
>>> + DFSDM_CR1_RSYNC(fl->sync_mode));
>>> +}
>>> +
>>> +static const struct iio_trigger_ops dfsdm_trigger_ops = {
>>> + .owner = THIS_MODULE,
>>> +};
>>> +
>>> +static int stm32_dfsdm_setup_spi_trigger(struct platform_device *pdev,
>>> + struct stm32_dfsdm *dfsdm)
>>> +{
>>> + /*
>>> + * To be able to use buffer consumer interface a trigger is needed.
>>> + * As conversion are trigged by PDM samples from SPI bus, that makes
>>> + * sense to define the serial interface ( SPI or manchester) as
>>> + * trigger source.
>> It's not actually the case that you have to have a triggrer.
>> There are plenty of drivers (particularly ones with hardware buffering)
>> where there is no trigger envolved. That's not to say it doesn't make sense
>> here.
>>
>> I'm not entirely sure yet if it's needed... Given it has no ops, I doubt it
>> somewhat...
>>> + */
>>> +
>>> + struct iio_trigger *trig;
>>> + int ret;
>>> +
>>> + trig = devm_iio_trigger_alloc(&pdev->dev, DFSDM_SPI_TRIGGER_NAME);
>>> + if (!trig)
>>> + return -ENOMEM;
>>> +
>>> + trig->dev.parent = pdev->dev.parent;
>>> + trig->ops = &dfsdm_trigger_ops;
>>> +
>>> + iio_trigger_set_drvdata(trig, dfsdm);
>>> +
>>> + ret = devm_iio_trigger_register(&pdev->dev, trig);
>>> + if (ret)
>>> + return ret;
>> Just return ret in all cases.
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>>> + struct iio_dev *indio_dev,
>>> + struct iio_chan_spec *chan, int chan_idx)
>>> +{
>>> + struct iio_chan_spec *ch = &chan[chan_idx];
>>> + struct stm32_dfsdm_channel *df_ch;
>>> + const char *of_str;
>>> + int ret, val;
>>> +
>>> + ret = of_property_read_u32_index(indio_dev->dev.of_node,
>>> + "st,adc-channels", chan_idx,
>>> + &ch->channel);
>>> + if (ret < 0) {
>>> + dev_err(&indio_dev->dev,
>>> + " Error parsing 'st,adc-channels' for idx %d\n",
>>> + chan_idx);
>>> + return ret;
>>> + }
>>> +
>>> + ret = of_property_read_string_index(indio_dev->dev.of_node,
>>> + "st,adc-channel-names", chan_idx,
>>> + &ch->datasheet_name);
>>> + if (ret < 0) {
>>> + dev_err(&indio_dev->dev,
>>> + " Error parsing 'st,adc-channel-names' for idx %d\n",
>>> + chan_idx);
>>> + return ret;
>>> + }
>>> +
>>> + df_ch = &dfsdm->ch_list[ch->channel];
>> Extra space on the line above.
>>> + df_ch->id = ch->channel;
>>> + ret = of_property_read_string_index(indio_dev->dev.of_node,
>>> + "st,adc-channel-types", chan_idx,
>>> + &of_str);
>>> + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_type);
>>> + if (ret < 0 || val < 0)
>>> + df_ch->type = 0;
>>> + else
>>> + df_ch->type = val;
>>> +
>>> + ret = of_property_read_string_index(indio_dev->dev.of_node,
>>> + "st,adc-channel-clk-src", chan_idx,
>>> + &of_str);
>>> + val = stm32_dfsdm_str2val(of_str, stm32_dfsdm_chan_src);
>>> + if (ret < 0 || val < 0)
>>> + df_ch->src = 0;
>>> + else
>>> + df_ch->src = val;
>>> +
>>> + ret = of_property_read_u32_index(indio_dev->dev.of_node,
>>> + "st,adc-alt-channel", chan_idx,
>>> + &df_ch->alt_si);
>>> + if (ret < 0)
>>> + df_ch->alt_si = 0;
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int stm32_dfsdm_parse_of(struct platform_device *pdev,
>>> + struct dfsdm_priv *priv)
>>> +{
>>> + struct device_node *node = pdev->dev.of_node;
>>> + struct resource *res;
>>> + unsigned long clk_freq;
>>> + unsigned int spi_freq, rem;
>>> + int ret;
>>> +
>>> + if (!node)
>>> + return -EINVAL;
>>> +
>>> + /* Get resources */
>>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> + if (!res) {
>>> + dev_err(&pdev->dev, "Failed to get memory resource\n");
>>> + return -ENODEV;
>>> + }
>>> + priv->dfsdm.phys_base = res->start;
>>> + priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
>>> +
>>> + /* Source clock */
>>> + priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
>>> + if (IS_ERR(priv->clk)) {
>>> + dev_err(&pdev->dev, "No stm32_dfsdm_clk clock found\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + priv->aclk = devm_clk_get(&pdev->dev, "audio");
>>> + if (IS_ERR(priv->aclk))
>>> + priv->aclk = NULL;
>>> +
>>> + if (priv->aclk)
>>> + clk_freq = clk_get_rate(priv->aclk);
>>> + else
>>> + clk_freq = clk_get_rate(priv->clk);
>>> +
>>> + /* SPI clock freq */
>>> + ret = of_property_read_u32(pdev->dev.of_node, "spi-max-frequency",
>>> + &spi_freq);
>>> + if (ret < 0) {
>>> + dev_err(&pdev->dev, "Failed to get spi-max-frequency\n");
>>> + return ret;
>>> + }
>>> +
>>> + priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
>>> + priv->dfsdm.spi_master_freq = spi_freq;
>>> +
>>> + if (rem) {
>>> + dev_warn(&pdev->dev, "SPI clock not accurate\n");
>>> + dev_warn(&pdev->dev, "%ld = %d * %d + %d\n",
>>> + clk_freq, spi_freq, priv->spi_clk_out_div + 1, rem);
>>> + }
>>> +
>>> + return 0;
>>> +};
>>> +
>>> +static const struct of_device_id stm32_dfsdm_of_match[] = {
>>> + {
>>> + .compatible = "st,stm32h7-dfsdm",
>>> + .data = &stm32h7_dfsdm_data,
>>> + },
>>> + {}
>>> +};
>>> +MODULE_DEVICE_TABLE(of, stm32_dfsdm_of_match);
>>> +
>>> +static int stm32_dfsdm_remove(struct platform_device *pdev)
>>> +{
>>> + of_platform_depopulate(&pdev->dev);
>>> +
>>> + return 0;
>>> +}
>>> +
>>> +static int stm32_dfsdm_probe(struct platform_device *pdev)
>>> +{
>>> + struct dfsdm_priv *priv;
>>> + struct device_node *pnode = pdev->dev.of_node;
>>> + const struct of_device_id *of_id;
>>> + const struct stm32_dfsdm_dev_data *dev_data;
>>> + struct stm32_dfsdm *dfsdm;
>>> + int ret, i;
>>> +
>>> + priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
>>> + if (!priv)
>>> + return -ENOMEM;
>>> +
>>> + priv->pdev = pdev;
>>> +
>>> + /* Populate data structure depending on compatibility */
>>> + of_id = of_match_node(stm32_dfsdm_of_match, pnode);
>>> + if (!of_id->data) {
>>> + dev_err(&pdev->dev, "Data associated to device is missing\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + dev_data = (const struct stm32_dfsdm_dev_data *)of_id->data;
>>> + dfsdm = &priv->dfsdm;
>>> + dfsdm->fl_list = devm_kcalloc(&pdev->dev, dev_data->num_filters,
>>> + sizeof(*dfsdm->fl_list), GFP_KERNEL);
>>> + if (!dfsdm->fl_list)
>>> + return -ENOMEM;
>>> +
>>> + dfsdm->num_fls = dev_data->num_filters;
>>> + dfsdm->ch_list = devm_kcalloc(&pdev->dev, dev_data->num_channels,
>>> + sizeof(*dfsdm->ch_list),
>>> + GFP_KERNEL);
>>> + if (!dfsdm->ch_list)
>>> + return -ENOMEM;
>>> + dfsdm->num_chs = dev_data->num_channels;
>>> +
>>> + ret = stm32_dfsdm_parse_of(pdev, priv);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + dfsdm->regmap = devm_regmap_init_mmio(&pdev->dev, dfsdm->base,
>>> + &stm32h7_dfsdm_regmap_cfg);
>>> + if (IS_ERR(dfsdm->regmap)) {
>>> + ret = PTR_ERR(dfsdm->regmap);
>>> + dev_err(&pdev->dev, "%s: Failed to allocate regmap: %d\n",
>>> + __func__, ret);
>>> + return ret;
>>> + }
>>> +
>>> + for (i = 0; i < STM32H7_DFSDM_NUM_FILTERS; i++) {
>>> + struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[i];
>>> +
>>> + fl->id = i;
>> I'd like a comment on why this is needed...
> to be cleaned.
>>> + }
>>> +
>>> + platform_set_drvdata(pdev, dfsdm);
>>> +
>>> + ret = stm32_dfsdm_setup_spi_trigger(pdev, dfsdm);
>>> + if (ret < 0)
>>> + return ret;
>>> +
>>> + return of_platform_populate(pnode, NULL, NULL, &pdev->dev);
>>> +}
>>> +
>>> +static struct platform_driver stm32_dfsdm_driver = {
>>> + .probe = stm32_dfsdm_probe,
>>> + .remove = stm32_dfsdm_remove,
>>> + .driver = {
>>> + .name = "stm32-dfsdm",
>>> + .of_match_table = stm32_dfsdm_of_match,
>>> + },
>>> +};
>>> +
>>> +module_platform_driver(stm32_dfsdm_driver);
>>> +
>>> +MODULE_AUTHOR("Arnaud Pouliquen <arnaud.pouliquen at st.com>");
>>> +MODULE_DESCRIPTION("STMicroelectronics STM32 dfsdm driver");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
>>> new file mode 100644
>>> index 0000000..bb7d74f
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32-dfsdm.h
>>> @@ -0,0 +1,371 @@
>>> +/*
>>> + * This file is part of STM32 DFSDM driver
>>> + *
>>> + * Copyright (C) 2017, STMicroelectronics - All Rights Reserved
>>> + * Author(s): Arnaud Pouliquen <arnaud.pouliquen at st.com>.
>>> + *
>>> + * License terms: GPL V2.0.
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify it
>>> + * under the terms of the GNU General Public License version 2 as published by
>>> + * the Free Software Foundation.
>>> + *
>>> + * This program is distributed in the hope that it will be useful, but
>>> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
>>> + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
>>> + * details.
>>> + */
>>> +#ifndef MDF_STM32_DFSDM__H
>>> +#define MDF_STM32_DFSDM__H
>>> +
>>> +#include <linux/bitfield.h>
>>> +
>>> +#include <linux/iio/iio.h>
>>> +/*
>>> + * STM32 DFSDM - global register map
>>> + * ________________________________________________________
>>> + * | Offset | Registers block |
>>> + * --------------------------------------------------------
>>> + * | 0x000 | CHANNEL 0 + COMMON CHANNEL FIELDS |
>>> + * --------------------------------------------------------
>>> + * | 0x020 | CHANNEL 1 |
>>> + * --------------------------------------------------------
>>> + * | ... | ..... |
>>> + * --------------------------------------------------------
>>> + * | 0x0E0 | CHANNEL 7 |
>>> + * --------------------------------------------------------
>>> + * | 0x100 | FILTER 0 + COMMON FILTER FIELDs |
>>> + * --------------------------------------------------------
>>> + * | 0x200 | FILTER 1 |
>>> + * --------------------------------------------------------
>>> + * | 0x300 | FILTER 2 |
>>> + * --------------------------------------------------------
>>> + * | 0x400 | FILTER 3 |
>>> + * --------------------------------------------------------
>>> + */
>>> +
>>> +/*
>>> + * Channels register definitions
>>> + */
>>> +#define DFSDM_CHCFGR1(y) ((y) * 0x20 + 0x00)
>>> +#define DFSDM_CHCFGR2(y) ((y) * 0x20 + 0x04)
>>> +#define DFSDM_AWSCDR(y) ((y) * 0x20 + 0x08)
>>> +#define DFSDM_CHWDATR(y) ((y) * 0x20 + 0x0C)
>>> +#define DFSDM_CHDATINR(y) ((y) * 0x20 + 0x10)
>>> +
>>> +/* CHCFGR1: Channel configuration register 1 */
>>> +#define DFSDM_CHCFGR1_SITP_MASK GENMASK(1, 0)
>>> +#define DFSDM_CHCFGR1_SITP(v) FIELD_PREP(DFSDM_CHCFGR1_SITP_MASK, v)
>>> +#define DFSDM_CHCFGR1_SPICKSEL_MASK GENMASK(3, 2)
>>> +#define DFSDM_CHCFGR1_SPICKSEL(v) FIELD_PREP(DFSDM_CHCFGR1_SPICKSEL_MASK, v)
>>> +#define DFSDM_CHCFGR1_SCDEN_MASK BIT(5)
>>> +#define DFSDM_CHCFGR1_SCDEN(v) FIELD_PREP(DFSDM_CHCFGR1_SCDEN_MASK, v)
>>> +#define DFSDM_CHCFGR1_CKABEN_MASK BIT(6)
>>> +#define DFSDM_CHCFGR1_CKABEN(v) FIELD_PREP(DFSDM_CHCFGR1_CKABEN_MASK, v)
>>> +#define DFSDM_CHCFGR1_CHEN_MASK BIT(7)
>>> +#define DFSDM_CHCFGR1_CHEN(v) FIELD_PREP(DFSDM_CHCFGR1_CHEN_MASK, v)
>>> +#define DFSDM_CHCFGR1_CHINSEL_MASK BIT(8)
>>> +#define DFSDM_CHCFGR1_CHINSEL(v) FIELD_PREP(DFSDM_CHCFGR1_CHINSEL_MASK, v)
>>> +#define DFSDM_CHCFGR1_DATMPX_MASK GENMASK(13, 12)
>>> +#define DFSDM_CHCFGR1_DATMPX(v) FIELD_PREP(DFSDM_CHCFGR1_DATMPX_MASK, v)
>>> +#define DFSDM_CHCFGR1_DATPACK_MASK GENMASK(15, 14)
>>> +#define DFSDM_CHCFGR1_DATPACK(v) FIELD_PREP(DFSDM_CHCFGR1_DATPACK_MASK, v)
>>> +#define DFSDM_CHCFGR1_CKOUTDIV_MASK GENMASK(23, 16)
>>> +#define DFSDM_CHCFGR1_CKOUTDIV(v) FIELD_PREP(DFSDM_CHCFGR1_CKOUTDIV_MASK, v)
>>> +#define DFSDM_CHCFGR1_CKOUTSRC_MASK BIT(30)
>>> +#define DFSDM_CHCFGR1_CKOUTSRC(v) FIELD_PREP(DFSDM_CHCFGR1_CKOUTSRC_MASK, v)
>>> +#define DFSDM_CHCFGR1_DFSDMEN_MASK BIT(31)
>>> +#define DFSDM_CHCFGR1_DFSDMEN(v) FIELD_PREP(DFSDM_CHCFGR1_DFSDMEN_MASK, v)
>>> +
>>> +/* CHCFGR2: Channel configuration register 2 */
>>> +#define DFSDM_CHCFGR2_DTRBS_MASK GENMASK(7, 3)
>>> +#define DFSDM_CHCFGR2_DTRBS(v) FIELD_PREP(DFSDM_CHCFGR2_DTRBS_MASK, v)
>>> +#define DFSDM_CHCFGR2_OFFSET_MASK GENMASK(31, 8)
>>> +#define DFSDM_CHCFGR2_OFFSET(v) FIELD_PREP(DFSDM_CHCFGR2_OFFSET_MASK, v)
>>> +
>>> +/* AWSCDR: Channel analog watchdog and short circuit detector */
>>> +#define DFSDM_AWSCDR_SCDT_MASK GENMASK(7, 0)
>>> +#define DFSDM_AWSCDR_SCDT(v) FIELD_PREP(DFSDM_AWSCDR_SCDT_MASK, v)
>>> +#define DFSDM_AWSCDR_BKSCD_MASK GENMASK(15, 12)
>>> +#define DFSDM_AWSCDR_BKSCD(v) FIELD_PREP(DFSDM_AWSCDR_BKSCD_MASK, v)
>>> +#define DFSDM_AWSCDR_AWFOSR_MASK GENMASK(20, 16)
>>> +#define DFSDM_AWSCDR_AWFOSR(v) FIELD_PREP(DFSDM_AWSCDR_AWFOSR_MASK, v)
>>> +#define DFSDM_AWSCDR_AWFORD_MASK GENMASK(23, 22)
>>> +#define DFSDM_AWSCDR_AWFORD(v) FIELD_PREP(DFSDM_AWSCDR_AWFORD_MASK, v)
>>> +
>>> +/*
>>> + * Filters register definitions
>>> + */
>>> +#define DFSDM_FILTER_BASE_ADR 0x100
>>> +#define DFSDM_FILTER_REG_MASK 0x7F
>>> +#define DFSDM_FILTER_X_BASE_ADR(x) ((x) * 0x80 + DFSDM_FILTER_BASE_ADR)
>>> +
>>> +#define DFSDM_CR1(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x00)
>>> +#define DFSDM_CR2(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x04)
>>> +#define DFSDM_ISR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x08)
>>> +#define DFSDM_ICR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x0C)
>>> +#define DFSDM_JCHGR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x10)
>>> +#define DFSDM_FCR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x14)
>>> +#define DFSDM_JDATAR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x18)
>>> +#define DFSDM_RDATAR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x1C)
>>> +#define DFSDM_AWHTR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x20)
>>> +#define DFSDM_AWLTR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x24)
>>> +#define DFSDM_AWSR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x28)
>>> +#define DFSDM_AWCFR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x2C)
>>> +#define DFSDM_EXMAX(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x30)
>>> +#define DFSDM_EXMIN(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x34)
>>> +#define DFSDM_CNVTIMR(x) (DFSDM_FILTER_X_BASE_ADR(x) + 0x38)
>>> +
>>> +/* CR1 Control register 1 */
>>> +#define DFSDM_CR1_DFEN_MASK BIT(0)
>>> +#define DFSDM_CR1_DFEN(v) FIELD_PREP(DFSDM_CR1_DFEN_MASK, v)
>>> +#define DFSDM_CR1_JSWSTART_MASK BIT(1)
>>> +#define DFSDM_CR1_JSWSTART(v) FIELD_PREP(DFSDM_CR1_JSWSTART_MASK, v)
>>> +#define DFSDM_CR1_JSYNC_MASK BIT(3)
>>> +#define DFSDM_CR1_JSYNC(v) FIELD_PREP(DFSDM_CR1_JSYNC_MASK, v)
>>> +#define DFSDM_CR1_JSCAN_MASK BIT(4)
>>> +#define DFSDM_CR1_JSCAN(v) FIELD_PREP(DFSDM_CR1_JSCAN_MASK, v)
>>> +#define DFSDM_CR1_JDMAEN_MASK BIT(5)
>>> +#define DFSDM_CR1_JDMAEN(v) FIELD_PREP(DFSDM_CR1_JDMAEN_MASK, v)
>>> +#define DFSDM_CR1_JEXTSEL_MASK GENMASK(12, 8)
>>> +#define DFSDM_CR1_JEXTSEL(v) FIELD_PREP(DFSDM_CR1_JEXTSEL_MASK, v)
>>> +#define DFSDM_CR1_JEXTEN_MASK GENMASK(14, 13)
>>> +#define DFSDM_CR1_JEXTEN(v) FIELD_PREP(DFSDM_CR1_JEXTEN_MASK, v)
>>> +#define DFSDM_CR1_RSWSTART_MASK BIT(17)
>>> +#define DFSDM_CR1_RSWSTART(v) FIELD_PREP(DFSDM_CR1_RSWSTART_MASK, v)
>>> +#define DFSDM_CR1_RCONT_MASK BIT(18)
>>> +#define DFSDM_CR1_RCONT(v) FIELD_PREP(DFSDM_CR1_RCONT_MASK, v)
>>> +#define DFSDM_CR1_RSYNC_MASK BIT(19)
>>> +#define DFSDM_CR1_RSYNC(v) FIELD_PREP(DFSDM_CR1_RSYNC_MASK, v)
>>> +#define DFSDM_CR1_RDMAEN_MASK BIT(21)
>>> +#define DFSDM_CR1_RDMAEN(v) FIELD_PREP(DFSDM_CR1_RDMAEN_MASK, v)
>>> +#define DFSDM_CR1_RCH_MASK GENMASK(26, 24)
>>> +#define DFSDM_CR1_RCH(v) FIELD_PREP(DFSDM_CR1_RCH_MASK, v)
>>> +#define DFSDM_CR1_FAST_MASK BIT(29)
>>> +#define DFSDM_CR1_FAST(v) FIELD_PREP(DFSDM_CR1_FAST_MASK, v)
>>> +#define DFSDM_CR1_AWFSEL_MASK BIT(30)
>>> +#define DFSDM_CR1_AWFSEL(v) FIELD_PREP(DFSDM_CR1_AWFSEL_MASK, v)
>>> +
>>> +/* CR2: Control register 2 */
>>> +#define DFSDM_CR2_IE_MASK GENMASK(6, 0)
>>> +#define DFSDM_CR2_IE(v) FIELD_PREP(DFSDM_CR2_IE_MASK, v)
>>> +#define DFSDM_CR2_JEOCIE_MASK BIT(0)
>>> +#define DFSDM_CR2_JEOCIE(v) FIELD_PREP(DFSDM_CR2_JEOCIE_MASK, v)
>>> +#define DFSDM_CR2_REOCIE_MASK BIT(1)
>>> +#define DFSDM_CR2_REOCIE(v) FIELD_PREP(DFSDM_CR2_REOCIE_MASK, v)
>>> +#define DFSDM_CR2_JOVRIE_MASK BIT(2)
>>> +#define DFSDM_CR2_JOVRIE(v) FIELD_PREP(DFSDM_CR2_JOVRIE_MASK, v)
>>> +#define DFSDM_CR2_ROVRIE_MASK BIT(3)
>>> +#define DFSDM_CR2_ROVRIE(v) FIELD_PREP(DFSDM_CR2_ROVRIE_MASK, v)
>>> +#define DFSDM_CR2_AWDIE_MASK BIT(4)
>>> +#define DFSDM_CR2_AWDIE(v) FIELD_PREP(DFSDM_CR2_AWDIE_MASK, v)
>>> +#define DFSDM_CR2_SCDIE_MASK BIT(5)
>>> +#define DFSDM_CR2_SCDIE(v) FIELD_PREP(DFSDM_CR2_SCDIE_MASK, v)
>>> +#define DFSDM_CR2_CKABIE_MASK BIT(6)
>>> +#define DFSDM_CR2_CKABIE(v) FIELD_PREP(DFSDM_CR2_CKABIE_MASK, v)
>>> +#define DFSDM_CR2_EXCH_MASK GENMASK(15, 8)
>>> +#define DFSDM_CR2_EXCH(v) FIELD_PREP(DFSDM_CR2_EXCH_MASK, v)
>>> +#define DFSDM_CR2_AWDCH_MASK GENMASK(23, 16)
>>> +#define DFSDM_CR2_AWDCH(v) FIELD_PREP(DFSDM_CR2_AWDCH_MASK, v)
>>> +
>>> +/* ISR: Interrupt status register */
>>> +#define DFSDM_ISR_JEOCF_MASK BIT(0)
>>> +#define DFSDM_ISR_JEOCF(v) FIELD_PREP(DFSDM_ISR_JEOCF_MASK, v)
>>> +#define DFSDM_ISR_REOCF_MASK BIT(1)
>>> +#define DFSDM_ISR_REOCF(v) FIELD_PREP(DFSDM_ISR_REOCF_MASK, v)
>>> +#define DFSDM_ISR_JOVRF_MASK BIT(2)
>>> +#define DFSDM_ISR_JOVRF(v) FIELD_PREP(DFSDM_ISR_JOVRF_MASK, v)
>>> +#define DFSDM_ISR_ROVRF_MASK BIT(3)
>>> +#define DFSDM_ISR_ROVRF(v) FIELD_PREP(DFSDM_ISR_ROVRF_MASK, v)
>>> +#define DFSDM_ISR_AWDF_MASK BIT(4)
>>> +#define DFSDM_ISR_AWDF(v) FIELD_PREP(DFSDM_ISR_AWDF_MASK, v)
>>> +#define DFSDM_ISR_JCIP_MASK BIT(13)
>>> +#define DFSDM_ISR_JCIP(v) FIELD_PREP(DFSDM_ISR_JCIP_MASK, v)
>>> +#define DFSDM_ISR_RCIP_MASK BIT(14)
>>> +#define DFSDM_ISR_RCIP(v) FIELD_PREP(DFSDM_ISR_RCIP, v)
>>> +#define DFSDM_ISR_CKABF_MASK GENMASK(23, 16)
>>> +#define DFSDM_ISR_CKABF(v) FIELD_PREP(DFSDM_ISR_CKABF_MASK, v)
>>> +#define DFSDM_ISR_SCDF_MASK GENMASK(31, 24)
>>> +#define DFSDM_ISR_SCDF(v) FIELD_PREP(DFSDM_ISR_SCDF_MASK, v)
>>> +
>>> +/* ICR: Interrupt flag clear register */
>>> +#define DFSDM_ICR_CLRJOVRF_MASK BIT(2)
>>> +#define DFSDM_ICR_CLRJOVRF(v) FIELD_PREP(DFSDM_ICR_CLRJOVRF_MASK, v)
>>> +#define DFSDM_ICR_CLRROVRF_MASK BIT(3)
>>> +#define DFSDM_ICR_CLRROVRF(v) FIELD_PREP(DFSDM_ICR_CLRROVRF_MASK, v)
>>> +#define DFSDM_ICR_CLRCKABF_MASK GENMASK(23, 16)
>>> +#define DFSDM_ICR_CLRCKABF(v) FIELD_PREP(DFSDM_ICR_CLRCKABF_MASK, v)
>>> +#define DFSDM_ICR_CLRCKABF_CH_MASK(y) BIT(16 + (y))
>>> +#define DFSDM_ICR_CLRCKABF_CH(v, y) \
>>> + (((v) << (16 + (y))) & DFSDM_ICR_CLRCKABF_CH_MASK(y))
>>> +#define DFSDM_ICR_CLRSCDF_MASK GENMASK(31, 24)
>>> +#define DFSDM_ICR_CLRSCDF(v) FIELD_PREP(DFSDM_ICR_CLRSCDF_MASK, v)
>>> +#define DFSDM_ICR_CLRSCDF_CH_MASK(y) BIT(24 + (y))
>>> +#define DFSDM_ICR_CLRSCDF_CH(v, y) \
>>> + (((v) << (24 + (y))) & DFSDM_ICR_CLRSCDF_MASK(y))
>>> +
>>> +/* FCR: Filter control register */
>>> +#define DFSDM_FCR_IOSR_MASK GENMASK(7, 0)
>>> +#define DFSDM_FCR_IOSR(v) FIELD_PREP(DFSDM_FCR_IOSR_MASK, v)
>>> +#define DFSDM_FCR_FOSR_MASK GENMASK(25, 16)
>>> +#define DFSDM_FCR_FOSR(v) FIELD_PREP(DFSDM_FCR_FOSR_MASK, v)
>>> +#define DFSDM_FCR_FORD_MASK GENMASK(31, 29)
>>> +#define DFSDM_FCR_FORD(v) FIELD_PREP(DFSDM_FCR_FORD_MASK, v)
>>> +
>>> +/* RDATAR: Filter data register for regular channel */
>>> +#define DFSDM_DATAR_CH_MASK GENMASK(2, 0)
>>> +#define DFSDM_DATAR_DATA_OFFSET 8
>>> +#define DFSDM_DATAR_DATA_MASK GENMASK(31, DFSDM_DATAR_DATA_OFFSET)
>>> +
>>> +/* AWLTR: Filter analog watchdog low threshold register */
>>> +#define DFSDM_AWLTR_BKAWL_MASK GENMASK(3, 0)
>>> +#define DFSDM_AWLTR_BKAWL(v) FIELD_PREP(DFSDM_AWLTR_BKAWL_MASK, v)
>>> +#define DFSDM_AWLTR_AWLT_MASK GENMASK(31, 8)
>>> +#define DFSDM_AWLTR_AWLT(v) FIELD_PREP(DFSDM_AWLTR_AWLT_MASK, v)
>>> +
>>> +/* AWHTR: Filter analog watchdog low threshold register */
>>> +#define DFSDM_AWHTR_BKAWH_MASK GENMASK(3, 0)
>>> +#define DFSDM_AWHTR_BKAWH(v) FIELD_PREP(DFSDM_AWHTR_BKAWH_MASK, v)
>>> +#define DFSDM_AWHTR_AWHT_MASK GENMASK(31, 8)
>>> +#define DFSDM_AWHTR_AWHT(v) FIELD_PREP(DFSDM_AWHTR_AWHT_MASK, v)
>>> +
>>> +/* AWSR: Filter watchdog status register */
>>> +#define DFSDM_AWSR_AWLTF_MASK GENMASK(7, 0)
>>> +#define DFSDM_AWSR_AWLTF(v) FIELD_PREP(DFSDM_AWSR_AWLTF_MASK, v)
>>> +#define DFSDM_AWSR_AWHTF_MASK GENMASK(15, 8)
>>> +#define DFSDM_AWSR_AWHTF(v) FIELD_PREP(DFSDM_AWSR_AWHTF_MASK, v)
>>> +
>>> +/* AWCFR: Filter watchdog status register */
>>> +#define DFSDM_AWCFR_AWLTF_MASK GENMASK(7, 0)
>>> +#define DFSDM_AWCFR_AWLTF(v) FIELD_PREP(DFSDM_AWCFR_AWLTF_MASK, v)
>>> +#define DFSDM_AWCFR_AWHTF_MASK GENMASK(15, 8)
>>> +#define DFSDM_AWCFR_AWHTF(v) FIELD_PREP(DFSDM_AWCFR_AWHTF_MASK, v)
>>> +
>>> +/* DFSDM filter order */
>>> +enum stm32_dfsdm_sinc_order {
>>> + DFSDM_FASTSINC_ORDER, /* FastSinc filter type */
>>> + DFSDM_SINC1_ORDER, /* Sinc 1 filter type */
>>> + DFSDM_SINC2_ORDER, /* Sinc 2 filter type */
>>> + DFSDM_SINC3_ORDER, /* Sinc 3 filter type */
>>> + DFSDM_SINC4_ORDER, /* Sinc 4 filter type (N.A. for watchdog) */
>>> + DFSDM_SINC5_ORDER, /* Sinc 5 filter type (N.A. for watchdog) */
>>> + DFSDM_NB_SINC_ORDER,
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
>>> + * TODO: complete structure.
>> nice :) RFC I guess :)
>>> + * @id: filetr ID,
>>> + */
>>> +struct stm32_dfsdm_filter {
>>> + unsigned int id;
>>> + unsigned int iosr; /* integrator oversampling */
>>> + unsigned int fosr; /* filter oversampling */
>>> + enum stm32_dfsdm_sinc_order ford;
>>> + u64 res; /* output sample resolution */
>>> + unsigned int sync_mode; /* filter suynchronized with filter0 */
>>> + unsigned int fast; /* filter fast mode */
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_dfsdm_channel - structure relative to stm32 FDSDM channel
>>> + * TODO: complete structure.
>>> + * @id: filetr ID,
>> filter
>>> + */
>>> +struct stm32_dfsdm_channel {
>>> + unsigned int id; /* id of the channel */
>>> + unsigned int type; /* interface type linked to stm32_dfsdm_chan_type */
>>> + unsigned int src; /* interface type linked to stm32_dfsdm_chan_src */
>>> + unsigned int alt_si; /* use alternative serial input interface */
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_dfsdm - stm32 FDSDM driver common data (for all instances)
>>> + * @base: control registers base cpu addr
>>> + * @phys_base: DFSDM IP register physical address.
>>> + * @fl_list: filter resources list
>>> + * @num_fl: number of filter resources available
>>> + * @ch_list: channel resources list
>>> + * @num_chs: number of channel resources available
>>> + */
>>> +struct stm32_dfsdm {
>>> + void __iomem *base;
>>> + phys_addr_t phys_base;
>>> + struct regmap *regmap;
>>> + struct stm32_dfsdm_filter *fl_list;
>>> + unsigned int num_fls;
>>> + struct stm32_dfsdm_channel *ch_list;
>>> + unsigned int num_chs;
>>> + unsigned int spi_master_freq;
>>> +};
>>> +
>>> +struct stm32_dfsdm_str2field {
>>> + const char *name;
>>> + unsigned int val;
>>> +};
>>> +
>>> +/* DFSDM channel serial interface type */
>>> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_type[] = {
>>> + { "SPI_R", 0 }, /* SPI with data on rising edge */
>>> + { "SPI_F", 1 }, /* SPI with data on falling edge */
>>> + { "MANCH_R", 2 }, /* Manchester codec, rising edge = logic 0 */
>>> + { "MANCH_F", 3 }, /* Manchester codec, falling edge = logic 1 */
>>> + { 0, 0},
>>> +};
>>> +
>>> +/* DFSDM channel serial spi clock source */
>>> +enum stm32_dfsdm_spi_clk_src {
>>> + DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL,
>>> + DFSDM_CHANNEL_SPI_CLOCK_INTERNAL,
>>> + DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING,
>>> + DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING
>>> +};
>>> +
>>> +/* DFSDM channel clock source */
>>> +static const struct stm32_dfsdm_str2field stm32_dfsdm_chan_src[] = {
>>> + /* External SPI clock (CLKIN x) */
>>> + { "CLKIN", DFSDM_CHANNEL_SPI_CLOCK_EXTERNAL },
>>> + /* Internal SPI clock (CLKOUT) */
>>> + { "CLKOUT", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL },
>>> + /* Internal SPI clock divided by 2 (falling edge) */
>>> + { "CLKOUT_F", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_FALLING },
>>> + /* Internal SPI clock divided by 2 (falling edge) */
>>> + { "CLKOUT_R", DFSDM_CHANNEL_SPI_CLOCK_INTERNAL_DIV2_RISING },
>>> + { 0, 0 },
>>> +};
>>> +
>>> +/* DFSDM Serial interface trigger name */
>>> +#define DFSDM_SPI_TRIGGER_NAME "DFSDM_SERIAL_IN"
>>> +
>>> +static inline int stm32_dfsdm_str2val(const char *str,
>>> + const struct stm32_dfsdm_str2field *list)
>>> +{
>>> + const struct stm32_dfsdm_str2field *p = list;
>>> +
>>> + for (p = list; p && p->name; p++) {
>>> + if (!strcmp(p->name, str))
>>> + return p->val;
>>> + }
>>> + return -EINVAL;
>>> +}
>>> +
>>> +int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl, unsigned int fast,
>>> + unsigned int oversamp);
>>> +int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm);
>>> +int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm);
>>> +
>>> +int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm, unsigned int fl_id,
>>> + unsigned int ch_id);
>>> +int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
>>> +void stm32_dfsdm_stop_filter(struct stm32_dfsdm *dfsdm, unsigned int fl_id);
>>> +
>>> +int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
>>> + struct stm32_dfsdm_channel *ch);
>>> +int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
>>> +void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm, unsigned int ch_id);
>>> +
>>> +int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
>>> + struct iio_dev *indio_dev,
>>> + struct iio_chan_spec *chan, int chan_idx);
>>> +
>>> +#endif
>>>
>>
>> --
>> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
>> the body of a message to majordomo at vger.kernel.org
>> More majordomo info at http://vger.kernel.org/majordomo-info.html
>>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
>
More information about the Alsa-devel
mailing list