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@xxxxxx> >> 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@xxxxxx>. >>> + * >>> + * 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@xxxxxx>"); >>> +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@xxxxxx> 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@xxxxxx>"); >>> +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@xxxxxx>. >>> + * >>> + * 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@xxxxxxxxxxxxxxx >> 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@xxxxxxxxxxxxxxx > More majordomo info at http://vger.kernel.org/majordomo-info.html > -- To unsubscribe from this list: send the line "unsubscribe devicetree" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html