linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/10] Add support for STM32 ADC
@ 2016-10-25 16:25 Fabrice Gasnier
  2016-10-25 16:25 ` [PATCH 01/10] Documentation: dt-bindings: Document STM32 ADC DT bindings Fabrice Gasnier
                   ` (9 more replies)
  0 siblings, 10 replies; 20+ messages in thread
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier

This series adds support for STM32F4 ADC into IIO framework.
STM32F4 ADC is a 12-bit successive approximation analog-to-digital
converter. It has up to 19 multiplexed input channels. Conversions can
be performed in single, continuous, scan or discontinuous mode.
Conversions can be launched in software or using hardware triggers.

This driver has been developed and tested on STM32F429 eval board.
It consist of a core driver, to ease support for other STM32 family
ADCs, and a specific driver for STM32F4 ADC.
It allows to use direct or triggered buffer modes with triggers.
Optional support for DMA and few extended attributes is included.

Fabrice Gasnier (10):
  Documentation: dt-bindings: Document STM32 ADC DT bindings
  iio: adc: Add stm32 support
  iio: adc: stm32: add optional dma support
  iio: adc: stm32: add optional support for exti gpios
  iio: adc: stm32: add trigger polarity ext attr
  iio: adc: stm32: add ext attrs to configure sampling time
  ARM: configs: stm32: enable IIO ADC driver
  ARM: dts: stm32f429: Add adc support
  ARM: dts: stm32f429: enable adc on eval board
  ARM: dts: stm32f429: Add dma support to adc

 .../devicetree/bindings/iio/adc/st,stm32-adc.txt   |   78 ++
 arch/arm/boot/dts/stm32429i-eval.dts               |   30 +
 arch/arm/boot/dts/stm32f429.dtsi                   |   68 +
 arch/arm/configs/stm32_defconfig                   |    2 +
 drivers/iio/adc/Kconfig                            |    2 +
 drivers/iio/adc/Makefile                           |    1 +
 drivers/iio/adc/stm32/Kconfig                      |   36 +
 drivers/iio/adc/stm32/Makefile                     |    4 +
 drivers/iio/adc/stm32/stm32-adc.c                  | 1334 ++++++++++++++++++++
 drivers/iio/adc/stm32/stm32-adc.h                  |  489 +++++++
 drivers/iio/adc/stm32/stm32f4-adc.c                |  652 ++++++++++
 11 files changed, 2696 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
 create mode 100644 drivers/iio/adc/stm32/Kconfig
 create mode 100644 drivers/iio/adc/stm32/Makefile
 create mode 100644 drivers/iio/adc/stm32/stm32-adc.c
 create mode 100644 drivers/iio/adc/stm32/stm32-adc.h
 create mode 100644 drivers/iio/adc/stm32/stm32f4-adc.c

-- 
1.9.1

^ permalink raw reply	[flat|nested] 20+ messages in thread

* [PATCH 01/10] Documentation: dt-bindings: Document STM32 ADC DT bindings
  2016-10-25 16:25 [PATCH 00/10] Add support for STM32 ADC Fabrice Gasnier
@ 2016-10-25 16:25 ` Fabrice Gasnier
  2016-10-31  3:02   ` Rob Herring
  2016-10-25 16:25 ` [PATCH 02/10] iio: adc: Add stm32 support Fabrice Gasnier
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 20+ messages in thread
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier

This patch adds documentation of device tree bindings for the STM32 ADC.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 .../devicetree/bindings/iio/adc/st,stm32-adc.txt   | 78 ++++++++++++++++++++++
 1 file changed, 78 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt

diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
new file mode 100644
index 0000000..a9a8b3c
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
@@ -0,0 +1,78 @@
+STMicroelectronics STM32 ADC device driver
+
+STM32 ADC is a successive approximation analog-to-digital converter.
+It has several multiplexed input channels. Conversions can be performed
+in single, continuous, scan or discontinuous mode. Result of the ADC is
+stored in a left-aligned or right-aligned 32-bit data register.
+Conversions can be launched in software or using hardware triggers.
+
+The analog watchdog feature allows the application to detect if the input
+voltage goes beyond the user-defined, higher or lower thresholds.
+
+Each STM32 ADC block can have up to 3 ADC instances.
+
+Each instance supports two contexts to manage conversions, each one has its
+own configurable sequence and trigger:
+- regular conversion can be done in sequence, running in background
+- injected conversions have higher priority, and so have the ability to
+  interrupt regular conversion sequence (either triggered in SW or HW).
+  Regular sequence is resumed, in case it has been interrupted.
+
+Required properties:
+- compatible: Should be "st,stm32f4-adc".
+- reg: Offset and length of the ADC block register set.
+- interrupts: Must contain the interrupt for ADC.
+- clocks: Clock for the analog circuitry (common to all ADCs).
+- clock-names: Must be "adc".
+- vref-supply: Phandle to the vref input analog reference voltage.
+- #address-cells = <1>;
+- #size-cells = <0>;
+
+Optional properties:
+- A pinctrl state named "default" for each ADC channel may be defined to set
+  inX ADC pins in mode of operation for analog input on external pin.
+- gpios: Array of gpios that may be configured as EXTi trigger sources.
+
+Example:
+	adc: adc@40012000 {
+		compatible = "st,stm32f4-adc";
+		reg = <0x40012000 0x400>;
+		interrupts = <18>;
+		clocks = <&rcc 0 168>;
+		clock-names = "adc";
+		vref-supply = <&reg_vref>;
+		pinctrl-names = "default";
+		pinctrl-0 = <&adc3_in8_pin>;
+		gpios = <&gpioa 11 0>,
+			<&gpioa 15 0>;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		adc1: adc1-master@0 {
+			#io-channel-cells = <1>;
+			reg = <0x0>;
+			clocks = <&rcc 0 168>;
+			st,adc-channels = <8>;
+		};
+		...
+		other adc child nodes follow...
+	};
+
+Contents of a stm32 adc child node:
+-----------------------------------
+An ADC block node should contain at least one subnode, representing an
+ADC instance available on the machine.
+
+Required properties:
+- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
+- st,adc-channels: List of single-ended channels muxed for this ADC.
+- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
+  Documentation/devicetree/bindings/iio/iio-bindings.txt
+
+Optional properties:
+- clocks: Input clock private to this ADC instance.
+- st,injected: Use injected conversion sequence on an ADC, rather than regular.
+- dmas: Phandle to dma channel for this ADC instance, only for regular
+  conversions. See ../../dma/dma.txt for details.
+- dma-names: Must be "rx" when dmas property is being used.
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 02/10] iio: adc: Add stm32 support
  2016-10-25 16:25 [PATCH 00/10] Add support for STM32 ADC Fabrice Gasnier
  2016-10-25 16:25 ` [PATCH 01/10] Documentation: dt-bindings: Document STM32 ADC DT bindings Fabrice Gasnier
@ 2016-10-25 16:25 ` Fabrice Gasnier
  2016-10-30 15:27   ` Jonathan Cameron
  2016-10-25 16:25 ` [PATCH 03/10] iio: adc: stm32: add optional dma support Fabrice Gasnier
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 20+ messages in thread
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier

This patch adds support for STMicroelectronics STM32 MCU's analog to
digital converter.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 drivers/iio/adc/Kconfig             |   2 +
 drivers/iio/adc/Makefile            |   1 +
 drivers/iio/adc/stm32/Kconfig       |  34 ++
 drivers/iio/adc/stm32/Makefile      |   4 +
 drivers/iio/adc/stm32/stm32-adc.c   | 999 ++++++++++++++++++++++++++++++++++++
 drivers/iio/adc/stm32/stm32-adc.h   | 442 ++++++++++++++++
 drivers/iio/adc/stm32/stm32f4-adc.c | 574 +++++++++++++++++++++
 7 files changed, 2056 insertions(+)
 create mode 100644 drivers/iio/adc/stm32/Kconfig
 create mode 100644 drivers/iio/adc/stm32/Makefile
 create mode 100644 drivers/iio/adc/stm32/stm32-adc.c
 create mode 100644 drivers/iio/adc/stm32/stm32-adc.h
 create mode 100644 drivers/iio/adc/stm32/stm32f4-adc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7edcf32..5c96a55 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -583,4 +583,6 @@ config XILINX_XADC
 	  The driver can also be build as a module. If so, the module will be called
 	  xilinx-xadc.
 
+source "drivers/iio/adc/stm32/Kconfig"
+
 endmenu
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7a40c04..a9dbf3a 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -13,6 +13,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
 obj-$(CONFIG_AD7793) += ad7793.o
 obj-$(CONFIG_AD7887) += ad7887.o
 obj-$(CONFIG_AD799X) += ad799x.o
+obj-$(CONFIG_ARCH_STM32) += stm32/
 obj-$(CONFIG_AT91_ADC) += at91_adc.o
 obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
 obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
new file mode 100644
index 0000000..245d037
--- /dev/null
+++ b/drivers/iio/adc/stm32/Kconfig
@@ -0,0 +1,34 @@
+#
+# STM32 familly ADC drivers
+#
+
+config STM32_ADC
+	tristate
+	select REGULATOR
+	select REGULATOR_FIXED_VOLTAGE
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say yes here to build the driver for the STMicroelectronics
+	  STM32 analog-to-digital converter (ADC).
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called stm32-adc.
+
+config STM32F4_ADC
+	tristate "STMicroelectronics STM32F4 adc"
+	depends on ARCH_STM32 || COMPILE_TEST
+	depends on OF
+	select STM32_ADC
+	help
+	  Say yes here to build support for STMicroelectronics stm32f4 Analog
+	  to Digital Converter (ADC).
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called stm32f4-adc.
+
+config STM32_ADC_DEBUG
+	bool "Enable debug for stm32 ADC drivers"
+	depends on STM32_ADC
+	help
+	  Say "yes" to enable debug messages, on stm32 ADC drivers.
diff --git a/drivers/iio/adc/stm32/Makefile b/drivers/iio/adc/stm32/Makefile
new file mode 100644
index 0000000..83e8154
--- /dev/null
+++ b/drivers/iio/adc/stm32/Makefile
@@ -0,0 +1,4 @@
+# Core
+subdir-ccflags-$(CONFIG_STM32_ADC_DEBUG) := -DDEBUG
+obj-$(CONFIG_STM32_ADC) += stm32-adc.o
+obj-$(CONFIG_STM32F4_ADC) += stm32f4-adc.o
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
new file mode 100644
index 0000000..1e0850d
--- /dev/null
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -0,0 +1,999 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@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/clk.h>
+#include <linux/debugfs.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include "stm32-adc.h"
+
+/**
+ * stm32_adc_conf_scan_seq() - Build regular or injected channels scan sequence
+ * @indio_dev: IIO device
+ * @scan_mask: channels to be converted
+ *
+ * Conversion sequence :
+ * Configure ADC scan sequence based on selected channels in scan_mask.
+ * Add channels to SQR or JSQR registers, from scan_mask LSB to MSB, then
+ * program sequence len.
+ *
+ * Note: This can be done only when ADC enabled and no conversion is ongoing.
+ */
+static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
+				   const unsigned long *scan_mask)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	const struct stm32_adc_regs *sq;
+	const struct iio_chan_spec *chan;
+	u32 val, bit;
+	int sq_max, i = 0;
+
+	if (!stm32_adc_is_enabled(adc))
+		return -EBUSY;
+
+	if (adc->injected) {
+		if (stm32_adc_injected_started(adc))
+			return -EBUSY;
+
+		sq = adc->common->data->adc_reginfo->jsqr_reg;
+		sq_max = STM32_ADC_MAX_JSQ;
+	} else {
+		if (stm32_adc_regular_started(adc))
+			return -EBUSY;
+
+		sq = adc->common->data->adc_reginfo->sqr_regs;
+		sq_max = STM32_ADC_MAX_SQ;
+	}
+
+	/* Build sequence for regular or injected channels */
+	for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
+		chan = indio_dev->channels + bit;
+		/*
+		 * Assign one channel per SQ/JSQ entry in regular/injected
+		 * sequence, starting with SQ1/JSQ1.
+		 */
+		i++;
+		if (i > sq_max)
+			return -EINVAL;
+
+		dev_dbg(adc->common->dev, "%s chan %d to %s%d\n",
+			__func__, chan->channel, adc->injected ? "JSQ" : "SQ",
+			i);
+
+		val = stm32_adc_readl(adc, sq[i].reg);
+		val &= ~sq[i].mask;
+		val |= (chan->channel << sq[i].shift) & sq[i].mask;
+		stm32_adc_writel(adc, sq[i].reg, val);
+	}
+
+	if (!i)
+		return -EINVAL;
+
+	/* Sequence len */
+	val = stm32_adc_readl(adc, sq[0].reg);
+	val &= ~sq[0].mask;
+	val |= ((i - 1) << sq[0].shift) & sq[0].mask;
+	stm32_adc_writel(adc, sq[0].reg, val);
+
+	return 0;
+}
+
+static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
+			       const unsigned long *scan_mask)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	int ret;
+
+	ret = stm32_adc_clk_sel(adc);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Clock sel failed\n");
+		return ret;
+	}
+
+	ret = stm32_adc_enable(adc);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Failed to enable adc\n");
+		return ret;
+	}
+
+	ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Failed to configure sequence\n");
+		goto err_dis;
+	}
+
+	return 0;
+
+err_dis:
+	stm32_adc_disable(adc);
+
+	return ret;
+}
+
+/**
+ * stm32_adc_get_trig_index() - Get trigger index
+ * @indio_dev: IIO device
+ * @trig: trigger
+ *
+ * Returns trigger index, if trig matches one of the triggers registered by
+ * stm32 adc driver, -EINVAL otherwise.
+ */
+static int stm32_adc_get_trig_index(struct iio_dev *indio_dev,
+				    struct iio_trigger *trig)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	struct iio_trigger *tr;
+	int i = 0;
+
+	list_for_each_entry(tr, &adc->extrig_list, alloc_list) {
+		if (tr == trig)
+			return i;
+		i++;
+	}
+
+	return -EINVAL;
+}
+
+/**
+ * stm32_adc_set_trig() - Set a regular or injected trigger
+ * @indio_dev: IIO device
+ * @trig: IIO trigger
+ *
+ * Set trigger source/polarity (e.g. SW, or HW with polarity) :
+ * - if HW trigger disabled (e.g. trig == NULL, conversion launched by sw)
+ * - if HW trigger enabled, set source & polarity (trigger_pol / jtrigger_pol)
+ *
+ * Note: must be called when ADC is enabled
+ */
+static int stm32_adc_set_trig(struct iio_dev *indio_dev,
+			      struct iio_trigger *trig)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	const struct stm32_adc_trig_info *trig_info;
+	const struct stm32_adc_trig_reginfo *reginfo;
+	u32 val, extsel = 0, exten = STM32_EXTEN_SWTRIG;
+	unsigned long flags;
+	int ret;
+
+	if (!stm32_adc_is_enabled(adc))
+		return -EBUSY;
+
+	if (adc->injected) {
+		if (stm32_adc_injected_started(adc))
+			return -EBUSY;
+		reginfo = adc->common->data->adc_reginfo->jtrig_reginfo;
+		trig_info = adc->common->data->jext_triggers;
+	} else {
+		if (stm32_adc_regular_started(adc))
+			return -EBUSY;
+		reginfo = adc->common->data->adc_reginfo->trig_reginfo;
+		trig_info = adc->common->data->ext_triggers;
+	}
+
+	if (trig) {
+		ret = stm32_adc_get_trig_index(indio_dev, trig);
+		if (ret < 0)
+			return ret;
+
+		/* trigger source */
+		extsel = trig_info[ret].extsel;
+		exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
+	}
+
+	spin_lock_irqsave(&adc->lock, flags);
+	val = stm32_adc_readl(adc, reginfo->reg);
+	val &= ~(reginfo->exten_mask | reginfo->extsel_mask);
+	val |= (exten << reginfo->exten_shift) & reginfo->exten_mask;
+	val |= (extsel << reginfo->extsel_shift) & reginfo->extsel_mask;
+	stm32_adc_writel(adc, reginfo->reg, val);
+	spin_unlock_irqrestore(&adc->lock, flags);
+
+	return 0;
+}
+
+/**
+ * stm32_adc_conv_irq_enable() - Unmask end of conversion irq
+ * @adc: stm32 adc instance
+ *
+ * Unmask either eoc or jeoc, depending on injected configuration.
+ */
+static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
+{
+	const struct stm32_adc_reginfo *reginfo =
+		adc->common->data->adc_reginfo;
+	u32 mask;
+
+	if (adc->injected)
+		mask = reginfo->jeocie;
+	else
+		mask = reginfo->eocie;
+
+	stm32_adc_set_bits(adc, reginfo->ier, mask);
+}
+
+/**
+ * stm32_adc_conv_irq_disable() - Mask end of conversion irq
+ * @adc: stm32 adc instance
+ *
+ * Mask either eoc or jeoc, depending on injected configuration.
+ */
+static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
+{
+	const struct stm32_adc_reginfo *reginfo =
+		adc->common->data->adc_reginfo;
+	u32 mask;
+
+	if (adc->injected)
+		mask = reginfo->jeocie;
+	else
+		mask = reginfo->eocie;
+
+	stm32_adc_clr_bits(adc, reginfo->ier, mask);
+}
+
+/**
+ * stm32_adc_single_conv() - perform a single conversion
+ * @indio_dev: IIO device
+ * @chan: IIO channel
+ * @result: conversion result
+ *
+ * The function performs a single conversion on a given channel, by
+ * by:
+ * - creating scan mask with only one channel
+ * - using SW trigger
+ * - then start single conv
+ */
+static int stm32_adc_single_conv(struct iio_dev *indio_dev,
+				 const struct iio_chan_spec *chan,
+				 int *val)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	unsigned long *scan_mask;
+	long timeout;
+	u16 result;
+	int ret;
+
+	scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
+			    GFP_KERNEL);
+	if (!scan_mask)
+		return -ENOMEM;
+
+	set_bit(chan->scan_index, scan_mask);
+
+	reinit_completion(&adc->completion);
+
+	adc->bufi = 0;
+	adc->num_conv = 1;
+	adc->buffer = &result;
+
+	ret = stm32_adc_conf_scan(indio_dev, scan_mask);
+	if (ret)
+		goto free;
+
+	/* No HW trigger: conversion can be launched in SW */
+	ret = stm32_adc_set_trig(indio_dev, NULL);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Can't set SW trigger\n");
+		goto adc_disable;
+	}
+
+	stm32_adc_conv_irq_enable(adc);
+
+	ret = stm32_adc_start_conv(adc);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Failed to start single conv\n");
+		goto irq_disable;
+	}
+
+	timeout = wait_for_completion_interruptible_timeout(
+					&adc->completion, STM32_ADC_TIMEOUT);
+	if (timeout == 0) {
+		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
+		ret = -ETIMEDOUT;
+	} else if (timeout < 0) {
+		dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
+		ret = -EINTR;
+	} else {
+		*val = result & STM32_RESULT_MASK;
+		ret = IIO_VAL_INT;
+	}
+
+	if (stm32_adc_stop_conv(adc))
+		dev_err(&indio_dev->dev, "stop failed\n");
+
+irq_disable:
+	stm32_adc_conv_irq_disable(adc);
+
+adc_disable:
+	stm32_adc_disable(adc);
+
+free:
+	kfree(scan_mask);
+	adc->buffer = NULL;
+
+	return ret;
+}
+
+static int stm32_adc_read_raw(struct iio_dev *indio_dev,
+			      struct iio_chan_spec const *chan,
+			      int *val, int *val2, long mask)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	int ret = -EINVAL;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = iio_device_claim_direct_mode(indio_dev);
+		if (ret)
+			return ret;
+		if (chan->type == IIO_VOLTAGE)
+			ret = stm32_adc_single_conv(indio_dev, chan, val);
+		iio_device_release_direct_mode(indio_dev);
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		*val = adc->common->vref_mv;
+		*val2 = chan->scan_type.realbits;
+		ret = IIO_VAL_FRACTIONAL_LOG2;
+		break;
+	default:
+		break;
+	}
+
+	return ret;
+}
+
+/**
+ * stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
+ */
+static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
+{
+	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+	const struct stm32_adc_reginfo *reginfo =
+		adc->common->data->adc_reginfo;
+	u32 mask, clr_mask, status = stm32_adc_readl(adc, reginfo->isr);
+
+	if (adc->injected) {
+		mask = reginfo->jeoc;
+		clr_mask = mask;
+	} else {
+		mask = reginfo->eoc;
+		/* don't clear 'eoc' as it is cleared when reading 'dr' */
+		clr_mask = 0;
+	}
+
+	/* clear irq */
+	stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
+	status &= mask;
+
+	/* Regular data */
+	if (status & reginfo->eoc) {
+		adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
+		if (iio_buffer_enabled(indio_dev)) {
+			adc->bufi++;
+			if (adc->bufi >= adc->num_conv) {
+				stm32_adc_conv_irq_disable(adc);
+				iio_trigger_poll(indio_dev->trig);
+			}
+		} else {
+			complete(&adc->completion);
+		}
+	}
+
+	/* Injected data */
+	if (status & reginfo->jeoc) {
+		int i;
+
+		for (i = 0; i < adc->num_conv; i++) {
+			adc->buffer[i] = stm32_adc_readl(adc, reginfo->jdr[i]);
+			adc->bufi++;
+		}
+
+		if (iio_buffer_enabled(indio_dev)) {
+			stm32_adc_conv_irq_disable(adc);
+			iio_trigger_poll(indio_dev->trig);
+		} else {
+			complete(&adc->completion);
+		}
+	}
+
+	/*
+	 * In case end of conversion flags have been handled, this has been
+	 * handled for this ADC instance
+	 */
+	if (status)
+		return IRQ_HANDLED;
+
+	/* This adc instance didn't trigger this interrupt */
+	return IRQ_NONE;
+}
+
+/**
+ * stm32_adc_common_isr() - Common isr for the whole ADC block
+ *
+ * There is one IRQ for all ADCs in ADC block, check all instances.
+ */
+static irqreturn_t stm32_adc_common_isr(int irq, void *data)
+{
+	struct stm32_adc_common *common = data;
+	irqreturn_t ret = IRQ_NONE;
+	struct stm32_adc *adc;
+
+	list_for_each_entry(adc, &common->adc_list, adc_list)
+		ret |= stm32_adc_isr(adc);
+
+	return ret;
+}
+
+/**
+ * stm32_adc_validate_trigger() - validate trigger for stm32 adc
+ * @indio_dev: IIO device
+ * @trig: new trigger
+ *
+ * Returns: 0 if trig matches one of the triggers registered by stm32 adc
+ * driver, -EINVAL otherwise.
+ */
+static int stm32_adc_validate_trigger(struct iio_dev *indio_dev,
+				      struct iio_trigger *trig)
+{
+	return stm32_adc_get_trig_index(indio_dev, trig) < 0 ? -EINVAL : 0;
+}
+
+static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
+				      const unsigned long *scan_mask)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	int ret;
+	u32 bit;
+
+	adc->num_conv = 0;
+	for_each_set_bit(bit, scan_mask, indio_dev->masklength)
+		adc->num_conv++;
+
+	ret = stm32_adc_conf_scan(indio_dev, scan_mask);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
+			      const struct of_phandle_args *iiospec)
+{
+	int i;
+
+	for (i = 0; i < indio_dev->num_channels; i++)
+		if (indio_dev->channels[i].channel == iiospec->args[0])
+			return i;
+
+	return -EINVAL;
+}
+
+/**
+ * stm32_adc_debugfs_reg_access - read or write register value
+ *
+ * To read a value from an ADC register:
+ *   echo [ADC reg offset] > direct_reg_access
+ *   cat direct_reg_access
+ *
+ * To write a value in a ADC register:
+ *   echo [ADC_reg_offset] [value] > direct_reg_access
+ */
+static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
+					unsigned reg, unsigned writeval,
+					unsigned *readval)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+
+	if (!readval)
+		stm32_adc_writel(adc, reg, writeval);
+	else
+		*readval = stm32_adc_readl(adc, reg);
+
+	return 0;
+}
+
+static const struct iio_info stm32_adc_iio_info = {
+	.read_raw = stm32_adc_read_raw,
+	.validate_trigger = stm32_adc_validate_trigger,
+	.update_scan_mode = stm32_adc_update_scan_mode,
+	.debugfs_reg_access = stm32_adc_debugfs_reg_access,
+	.of_xlate = stm32_adc_of_xlate,
+	.driver_module = THIS_MODULE,
+};
+
+static int stm32_adc_buffer_postdisable(struct iio_dev *indio_dev)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+
+	stm32_adc_disable(adc);
+
+	return 0;
+}
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+	.postenable = &iio_triggered_buffer_postenable,
+	.predisable = &iio_triggered_buffer_predisable,
+	.postdisable = &stm32_adc_buffer_postdisable,
+};
+
+static int stm32_adc_validate_device(struct iio_trigger *trig,
+				     struct iio_dev *indio_dev)
+{
+	struct iio_dev *indio = iio_trigger_get_drvdata(trig);
+
+	return indio != indio_dev ? -EINVAL : 0;
+}
+
+static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
+				       bool state)
+{
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	int ret;
+
+	if (state) {
+		/* Reset adc buffer index */
+		adc->bufi = 0;
+
+		/* Allocate adc buffer */
+		adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
+		if (!adc->buffer)
+			return -ENOMEM;
+
+		ret = stm32_adc_set_trig(indio_dev, trig);
+		if (ret) {
+			dev_err(&indio_dev->dev, "Can't set trigger\n");
+			goto err_buffer_free;
+		}
+
+		stm32_adc_conv_irq_enable(adc);
+
+		ret = stm32_adc_start_conv(adc);
+		if (ret) {
+			dev_err(&indio_dev->dev, "Failed to start\n");
+			goto err_irq_trig_disable;
+		}
+	} else {
+		ret = stm32_adc_stop_conv(adc);
+		if (ret < 0) {
+			dev_err(&indio_dev->dev, "Failed to stop\n");
+			return ret;
+		}
+
+		stm32_adc_conv_irq_disable(adc);
+
+		ret = stm32_adc_set_trig(indio_dev, NULL);
+		if (ret)
+			dev_warn(&indio_dev->dev, "Can't clear trigger\n");
+
+		kfree(adc->buffer);
+		adc->buffer = NULL;
+	}
+
+	return 0;
+
+err_irq_trig_disable:
+	stm32_adc_conv_irq_disable(adc);
+	stm32_adc_set_trig(indio_dev, NULL);
+
+err_buffer_free:
+	kfree(adc->buffer);
+	adc->buffer = NULL;
+
+	return ret;
+}
+
+static const struct iio_trigger_ops stm32_adc_trigger_ops = {
+	.owner = THIS_MODULE,
+	.validate_device = stm32_adc_validate_device,
+	.set_trigger_state = stm32_adc_set_trigger_state,
+};
+
+static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct stm32_adc *adc = iio_priv(indio_dev);
+
+	dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
+
+	/* reset buffer index */
+	adc->bufi = 0;
+	iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
+					   pf->timestamp);
+
+	iio_trigger_notify_done(indio_dev->trig);
+
+	/* re-enable eoc irq */
+	stm32_adc_conv_irq_enable(adc);
+
+	return IRQ_HANDLED;
+}
+
+static void stm32_adc_trig_unregister(struct iio_dev *indio_dev)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	struct iio_trigger *trig, *_t;
+
+	list_for_each_entry_safe(trig, _t, &adc->extrig_list, alloc_list) {
+		iio_trigger_unregister(trig);
+		list_del(&trig->alloc_list);
+	}
+}
+
+static int stm32_adc_trig_register(struct iio_dev *indio_dev)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	struct stm32_adc_common *common = adc->common;
+	const struct stm32_adc_trig_info *ext = common->data->ext_triggers;
+	struct iio_trigger *trig;
+	int i, ret = 0;
+
+	if (adc->injected)
+		ext = common->data->jext_triggers;
+	else
+		ext = common->data->ext_triggers;
+
+	for (i = 0; ext && ext[i].name; i++) {
+		trig = devm_iio_trigger_alloc(common->dev, "%s_%s%d_%s",
+					      indio_dev->name,
+					      adc->injected ? "jext" : "ext",
+					      ext[i].extsel, ext[i].name);
+		if (!trig) {
+			dev_err(common->dev, "trig %s_%s%d_%s alloc failed\n",
+				indio_dev->name,
+				adc->injected ? "jext" : "ext",
+				ext[i].extsel, ext[i].name);
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		trig->dev.parent = common->dev;
+		trig->ops = &stm32_adc_trigger_ops;
+		iio_trigger_set_drvdata(trig, indio_dev);
+
+		ret = iio_trigger_register(trig);
+		if (ret) {
+			dev_err(common->dev,
+				"trig %s_%s%d_%s register failed\n",
+				indio_dev->name,
+				adc->injected ? "jext" : "ext",
+				ext[i].extsel, ext[i].name);
+			goto err;
+		}
+
+		list_add_tail(&trig->alloc_list, &adc->extrig_list);
+	}
+
+	return 0;
+err:
+	stm32_adc_trig_unregister(indio_dev);
+
+	return ret;
+}
+
+static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
+				    struct iio_chan_spec *chan,
+				    const struct stm32_adc_chan_spec *channel,
+				    int scan_index)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+
+	chan->type = channel->type;
+	chan->channel = channel->channel;
+	chan->datasheet_name = channel->name;
+	chan->extend_name = channel->name;
+	chan->scan_index = scan_index;
+	chan->indexed = 1;
+	chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+	chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+	chan->scan_type.sign = 'u';
+	chan->scan_type.realbits = adc->common->data->highres;
+	chan->scan_type.storagebits = STM32_STORAGEBITS;
+	chan->scan_type.shift = 0;
+}
+
+static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
+				  const struct stm32_adc_info *adc_info)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	struct device_node *node = indio_dev->dev.of_node;
+	struct property *prop;
+	const __be32 *cur;
+	struct iio_chan_spec *channels;
+	int scan_index = 0, num_channels = 0;
+	u32 val;
+
+	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
+		num_channels++;
+
+	channels = devm_kcalloc(&indio_dev->dev, num_channels,
+				sizeof(struct iio_chan_spec), GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
+		stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+					&adc_info->channels[val],
+					scan_index);
+		scan_index++;
+	}
+
+	adc->max_channels = adc_info->max_channels;
+	indio_dev->num_channels = scan_index;
+	indio_dev->channels = channels;
+
+	return 0;
+}
+
+static int stm32_adc_register(struct stm32_adc_common *common,
+			      struct device_node *child)
+{
+	struct iio_dev *indio_dev;
+	struct stm32_adc *adc;
+	int i, ret;
+	u32 reg;
+
+	ret = of_property_read_u32(child, "reg", &reg);
+	if (ret != 0) {
+		dev_err(common->dev, "missing reg property\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; common->data->adc_info[i].channels; i++)
+		if (common->data->adc_info[i].reg == reg)
+			break;
+
+	if (i >= STM32_ADC_ID_MAX || !common->data->adc_info[i].channels) {
+		dev_err(common->dev, "bad adc reg offset\n");
+		return -ENOENT;
+	}
+
+	indio_dev = devm_iio_device_alloc(common->dev, sizeof(*adc));
+	if (!indio_dev) {
+		dev_err(common->dev, "iio device allocation failed\n");
+		return -ENOMEM;
+	}
+
+	adc = iio_priv(indio_dev);
+	adc->id = i;
+	adc->offset = reg;
+	adc->common = common;
+	INIT_LIST_HEAD(&adc->extrig_list);
+	spin_lock_init(&adc->lock);
+	init_completion(&adc->completion);
+
+	if (child->name)
+		indio_dev->name = child->name;
+	else
+		indio_dev->name = common->data->adc_info[i].name;
+	indio_dev->dev.parent = common->dev;
+	indio_dev->dev.of_node = child;
+	indio_dev->info = &stm32_adc_iio_info;
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	if (of_property_read_bool(child, "st,injected")) {
+		dev_dbg(common->dev, "%s Configured to use injected\n",
+			indio_dev->name);
+		adc->injected = true;
+	}
+
+	adc->clk = of_clk_get(child, 0);
+	if (IS_ERR(adc->clk)) {
+		adc->clk = NULL;
+		dev_dbg(common->dev, "No child clk found\n");
+	} else {
+		ret = clk_prepare_enable(adc->clk);
+		if (ret < 0)
+			goto err_clk_put;
+	}
+
+	ret = stm32_adc_chan_of_init(indio_dev, &common->data->adc_info[i]);
+	if (ret < 0) {
+		dev_err(common->dev, "iio channels init failed\n");
+		goto err_clk_disable;
+	}
+
+	ret = stm32_adc_trig_register(indio_dev);
+	if (ret)
+		goto err_clk_disable;
+
+	ret = iio_triggered_buffer_setup(indio_dev,
+					 &iio_pollfunc_store_time,
+					 &stm32_adc_trigger_handler,
+					 &iio_triggered_buffer_setup_ops);
+	if (ret) {
+		dev_err(common->dev, "buffer setup failed\n");
+		goto err_trig_unregister;
+	}
+
+	ret = iio_device_register(indio_dev);
+	if (ret) {
+		dev_err(common->dev, "iio dev register failed\n");
+		goto err_buffer_cleanup;
+	}
+
+	list_add_tail(&adc->adc_list, &common->adc_list);
+
+	return 0;
+
+err_buffer_cleanup:
+	iio_triggered_buffer_cleanup(indio_dev);
+
+err_trig_unregister:
+	stm32_adc_trig_unregister(indio_dev);
+
+err_clk_disable:
+	if (adc->clk)
+		clk_disable_unprepare(adc->clk);
+
+err_clk_put:
+	if (adc->clk)
+		clk_put(adc->clk);
+
+	return ret;
+}
+
+static void stm32_adc_unregister(struct stm32_adc *adc)
+{
+	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+	iio_device_unregister(indio_dev);
+	iio_triggered_buffer_cleanup(indio_dev);
+	stm32_adc_trig_unregister(indio_dev);
+	if (adc->clk) {
+		clk_disable_unprepare(adc->clk);
+		clk_put(adc->clk);
+	}
+}
+
+int stm32_adc_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node, *child;
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *match;
+	struct stm32_adc_common *common;
+	struct stm32_adc *adc;
+	struct resource *res;
+	int ret;
+
+	match = of_match_device(dev->driver->of_match_table, &pdev->dev);
+	if (!match || !match->data) {
+		dev_err(&pdev->dev, "compatible data not provided\n");
+		return -EINVAL;
+	}
+
+	common = devm_kzalloc(&pdev->dev, sizeof(*common), GFP_KERNEL);
+	if (!common)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	common->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(common->base))
+		return PTR_ERR(common->base);
+
+	common->data = match->data;
+	common->dev = &pdev->dev;
+	platform_set_drvdata(pdev, common);
+	mutex_init(&common->lock);
+	INIT_LIST_HEAD(&common->adc_list);
+
+	common->vref = devm_regulator_get(&pdev->dev, "vref");
+	if (IS_ERR(common->vref)) {
+		ret = PTR_ERR(common->vref);
+		dev_err(&pdev->dev, "vref get failed, %d\n", ret);
+		return ret;
+	}
+
+	ret = regulator_enable(common->vref);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "vref enable failed\n");
+		return ret;
+	}
+
+	ret = regulator_get_voltage(common->vref);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
+		goto err_regulator_disable;
+	}
+	common->vref_mv = ret / 1000;
+	dev_dbg(&pdev->dev, "vref+=%dmV\n", common->vref_mv);
+
+	common->aclk = devm_clk_get(&pdev->dev, "adc");
+	if (IS_ERR(common->aclk)) {
+		ret = PTR_ERR(common->aclk);
+		dev_err(&pdev->dev, "Can't get 'adc' clock\n");
+		goto err_regulator_disable;
+	}
+
+	ret = clk_prepare_enable(common->aclk);
+	if (ret < 0) {
+		dev_err(common->dev, "adc clk enable failed\n");
+		goto err_regulator_disable;
+	}
+
+	common->irq = platform_get_irq(pdev, 0);
+	if (common->irq < 0) {
+		dev_err(&pdev->dev, "failed to get irq\n");
+		ret = common->irq;
+		goto err_clk_disable;
+	}
+
+	ret = devm_request_irq(&pdev->dev, common->irq,	stm32_adc_common_isr,
+			       0, pdev->name, common);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to request irq\n");
+		goto err_clk_disable;
+	}
+
+	/* Parse adc child nodes to retrieve master/slave instances data */
+	for_each_available_child_of_node(np, child) {
+		ret = stm32_adc_register(common, child);
+		if (ret)
+			goto err_unregister;
+	}
+
+	dev_info(&pdev->dev, "registered\n");
+
+	return 0;
+
+err_unregister:
+	list_for_each_entry(adc, &common->adc_list, adc_list)
+		stm32_adc_unregister(adc);
+
+err_clk_disable:
+	clk_disable_unprepare(common->aclk);
+
+err_regulator_disable:
+	regulator_disable(common->vref);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_probe);
+
+int stm32_adc_remove(struct platform_device *pdev)
+{
+	struct stm32_adc_common *common = platform_get_drvdata(pdev);
+	struct stm32_adc *adc;
+
+	list_for_each_entry(adc, &common->adc_list, adc_list)
+		stm32_adc_unregister(adc);
+	clk_disable_unprepare(common->aclk);
+	regulator_disable(common->vref);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_remove);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
new file mode 100644
index 0000000..0be603c
--- /dev/null
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -0,0 +1,442 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@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/>.
+ */
+
+#ifndef __STM32_ADC_H
+#define __STM32_ADC_H
+
+/*
+ * STM32 - ADC global register map
+ * ________________________________________________________
+ * | Offset |                 Register                    |
+ * --------------------------------------------------------
+ * | 0x000  |                Master ADC1                  |
+ * --------------------------------------------------------
+ * | 0x100  |                Slave ADC2                   |
+ * --------------------------------------------------------
+ * | 0x200  |                Slave ADC3                   |
+ * --------------------------------------------------------
+ * | 0x300  |         Master & Slave common regs          |
+ * --------------------------------------------------------
+ */
+#define STM32_ADCX_COMN_OFFSET		0x300
+#define STM32_ADC_ID_MAX		3
+#define STM32_ADC_MAX_SQ		16	/* SQ1..SQ16 */
+#define STM32_ADC_MAX_JSQ		4	/* JSQ1..JSQ4 */
+
+/* STM32 value masks */
+#define STM32_RESULT_MASK	GENMASK(15, 0)
+#define STM32_STORAGEBITS	16
+
+/* External trigger enable for regular or injected channels (exten/jexten) */
+enum stm32_adc_exten {
+	STM32_EXTEN_SWTRIG,
+	STM32_EXTEN_HWTRIG_RISING_EDGE,
+	STM32_EXTEN_HWTRIG_FALLING_EDGE,
+	STM32_EXTEN_HWTRIG_BOTH_EDGES,
+};
+
+enum stm32_adc_extsel {
+	STM32_EXT0,
+	STM32_EXT1,
+	STM32_EXT2,
+	STM32_EXT3,
+	STM32_EXT4,
+	STM32_EXT5,
+	STM32_EXT6,
+	STM32_EXT7,
+	STM32_EXT8,
+	STM32_EXT9,
+	STM32_EXT10,
+	STM32_EXT11,
+	STM32_EXT12,
+	STM32_EXT13,
+	STM32_EXT14,
+	STM32_EXT15,
+	STM32_EXT16,
+	STM32_EXT17,
+	STM32_EXT18,
+	STM32_EXT19,
+	STM32_EXT20,
+	STM32_EXT21,
+	STM32_EXT22,
+	STM32_EXT23,
+	STM32_EXT24,
+	STM32_EXT25,
+	STM32_EXT26,
+	STM32_EXT27,
+	STM32_EXT28,
+	STM32_EXT29,
+	STM32_EXT30,
+	STM32_EXT31,
+};
+
+enum stm32_adc_jextsel {
+	STM32_JEXT0,
+	STM32_JEXT1,
+	STM32_JEXT2,
+	STM32_JEXT3,
+	STM32_JEXT4,
+	STM32_JEXT5,
+	STM32_JEXT6,
+	STM32_JEXT7,
+	STM32_JEXT8,
+	STM32_JEXT9,
+	STM32_JEXT10,
+	STM32_JEXT11,
+	STM32_JEXT12,
+	STM32_JEXT13,
+	STM32_JEXT14,
+	STM32_JEXT15,
+	STM32_JEXT16,
+	STM32_JEXT17,
+	STM32_JEXT18,
+	STM32_JEXT19,
+	STM32_JEXT20,
+	STM32_JEXT21,
+	STM32_JEXT22,
+	STM32_JEXT23,
+	STM32_JEXT24,
+	STM32_JEXT25,
+	STM32_JEXT26,
+	STM32_JEXT27,
+	STM32_JEXT28,
+	STM32_JEXT29,
+	STM32_JEXT30,
+	STM32_JEXT31,
+};
+
+#define	STM32_ADC_TIMEOUT_US	100000
+#define	STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
+
+/**
+ * struct stm32_adc_chan_spec - specification of stm32 adc channel
+ * @type:	IIO channel type
+ * @channel:	channel number (single ended)
+ * @name:	channel name (single ended)
+ */
+struct stm32_adc_chan_spec {
+	enum iio_chan_type	type;
+	int			channel;
+	const char		*name;
+};
+
+/**
+ * struct stm32_adc_trig_info - ADC trigger info
+ * @extsel:		trigger selection for regular or injected
+ * @name:		name of the trigger, corresponding to its source
+ */
+struct stm32_adc_trig_info {
+	u32 extsel;
+	const char *name;
+};
+
+/**
+ * struct stm32_adc_info - stm32 ADC, per instance config data
+ * @name:		default name for this instance (like "adc1")
+ * @reg:		reg offset for this instance (e.g. 0x0 for adc1...)
+ * @channels:		Reference to stm32 channels spec
+ * @max_channels:	Number of single ended channels
+ */
+struct stm32_adc_info {
+	const char *name;
+	u32 reg;
+	const struct stm32_adc_chan_spec *channels;
+	int max_channels;
+};
+
+/**
+ * stm32_adc_regs - stm32 ADC misc registers & bitfield desc
+ * @reg:		register offset
+ * @mask:		bitfield mask
+ * @shift:		left shift
+ */
+struct stm32_adc_regs {
+	int reg;
+	int mask;
+	int shift;
+};
+
+/**
+ * stm32_adc_trig_reginfo - stm32 ADC trigger control registers description
+ * @reg:		trigger control register offset (exten/jexten)
+ * @exten_mask:		external trigger en/polarity mask in @reg
+ * @exten_shift:	external trigger en/polarity shift in @reg
+ * @extsel_mask:	external trigger source mask in @reg
+ * @extsel_shift:	external trigger source shift in @reg
+ */
+struct stm32_adc_trig_reginfo {
+	u32 reg;
+	u32 exten_mask;
+	u32 exten_shift;
+	u32 extsel_mask;
+	u32 extsel_shift;
+};
+
+/**
+ * struct stm32_adc_reginfo - stm32 ADC registers description
+ * @isr:		interrupt status register offset
+ * @eoc:		end of conversion mask in @isr
+ * @jeoc:		end of injected conversion sequence mask in @isr
+ * @ier:		interrupt enable register offset
+ * @eocie:		end of conversion interrupt enable mask in @ier
+ * @jeocie:		end of injected conversion sequence interrupt en mask
+ * @dr:			data register offset
+ * @jdr:		injected data registers offsets
+ * @sqr_regs:		Regular sequence registers description
+ * @jsqr_reg:		Injected sequence register description
+ * @trig_reginfo:	regular trigger control registers description
+ * @jtrig_reginfo:	injected trigger control registers description
+ */
+struct stm32_adc_reginfo {
+	u32 isr;
+	u32 eoc;
+	u32 jeoc;
+	u32 ier;
+	u32 eocie;
+	u32 jeocie;
+	u32 dr;
+	u32 jdr[4];
+	const struct stm32_adc_regs *sqr_regs;
+	const struct stm32_adc_regs *jsqr_reg;
+	const struct stm32_adc_trig_reginfo *trig_reginfo;
+	const struct stm32_adc_trig_reginfo *jtrig_reginfo;
+};
+
+struct stm32_adc;
+
+/**
+ * struct stm32_adc_ops - stm32 ADC, compatible dependent data
+ * - stm32 ADC may work as single ADC, or as tightly coupled master/slave ADCs.
+ *
+ * @adc_info:		Array spec for stm32 adc master/slaves instances
+ * @ext_triggers:	Reference to trigger info for regular channels
+ * @jext_triggers:	Reference to trigger info for injected channels
+ * @adc_reginfo:	stm32 ADC registers description
+ * @highres:		Max resolution
+ * @max_clock_rate:	Max input clock rate
+ * @clk_sel:		routine to select common clock and prescaler
+ * @start_conv:		routine to start conversions
+ * @stop_conv:		routine to stop conversions
+ * @is_started:		routine to get adc 'started' state
+ * @regular_started	routine to check regular conversions status
+ * @injected_started	routine to check injected conversions status
+ * @enable:		optional routine to enable stm32 adc
+ * @disable:		optional routine to disable stm32 adc
+ * @is_enabled		reports enabled state
+ */
+struct stm32_adc_ops {
+	const struct stm32_adc_info *adc_info;
+	const struct stm32_adc_trig_info *ext_triggers;
+	const struct stm32_adc_trig_info *jext_triggers;
+	const struct stm32_adc_reginfo *adc_reginfo;
+	int highres;
+	unsigned long max_clock_rate;
+	int (*clk_sel)(struct stm32_adc *adc);
+	int (*start_conv)(struct stm32_adc *adc);
+	int (*stop_conv)(struct stm32_adc *adc);
+	bool (*is_started)(struct stm32_adc *adc);
+	bool (*regular_started)(struct stm32_adc *adc);
+	bool (*injected_started)(struct stm32_adc *adc);
+	int (*enable)(struct stm32_adc *adc);
+	void (*disable)(struct stm32_adc *adc);
+	bool (*is_enabled)(struct stm32_adc *adc);
+};
+
+struct stm32_adc_common;
+
+/**
+ * struct stm32_adc - private data of each ADC IIO instance
+ * @common:		reference to ADC block common data
+ * @adc_list:		current ADC entry in common ADC list
+ * @id:			ADC instance number (e.g. adc 1, 2 or 3)
+ * @offset:		ADC instance register offset in ADC block
+ * @max_channels:	Max channels number for this ADC.
+ * @extrig_list:	External trigger list (for regular channel)
+ * @completion:		end of single conversion completion
+ * @buffer:		data buffer
+ * @bufi:		data buffer index
+ * @num_conv:		expected number of scan conversions
+ * @injected:		use injected channels on this adc
+ * @lock:		spinlock
+ * @clk:		optional adc clock, for this adc instance
+ * @calib:		optional calibration data
+ * @en:			emulates enabled state on some stm32 adc
+ */
+struct stm32_adc {
+	struct stm32_adc_common	*common;
+	struct list_head	adc_list;
+	int			id;
+	int			offset;
+	int			max_channels;
+	struct list_head	extrig_list;
+	struct completion	completion;
+	u16			*buffer;
+	int			bufi;
+	int			num_conv;
+	bool			injected;
+	spinlock_t		lock;		/* interrupt lock */
+	struct clk		*clk;
+	void			*calib;
+	bool			en;
+};
+
+/**
+ * struct stm32_adc_common - private data of ADC driver, common to all
+ * ADC instances (ADC block)
+ * @dev:		device for this controller
+ * @base:		control registers base cpu addr
+ * @irq:		Common irq line for all adc instances
+ * @data:		STM32 dependent data from compatible
+ * @adc_list:		list of all stm32 ADC in this ADC block
+ * @aclk:		common clock for the analog circuitry
+ * @vref:		regulator reference
+ * @vref_mv:		vref voltage (mv)
+ * @lock:		mutex
+ */
+struct stm32_adc_common {
+	struct device			*dev;
+	void __iomem			*base;
+	int				irq;
+	const struct stm32_adc_ops	*data;
+	struct list_head		adc_list;
+	struct clk			*aclk;
+	struct regulator		*vref;
+	int				vref_mv;
+	struct mutex			lock;	/* read_raw lock */
+};
+
+/* Helper routines */
+static inline int stm32_adc_start_conv(struct stm32_adc *adc)
+{
+	return adc->common->data->start_conv(adc);
+}
+
+static inline int stm32_adc_stop_conv(struct stm32_adc *adc)
+{
+	return adc->common->data->stop_conv(adc);
+}
+
+static inline bool stm32_adc_is_started(struct stm32_adc *adc)
+{
+	return adc->common->data->is_started(adc);
+}
+
+static inline bool stm32_adc_regular_started(struct stm32_adc *adc)
+{
+	return adc->common->data->regular_started(adc);
+}
+
+static inline bool stm32_adc_injected_started(struct stm32_adc *adc)
+{
+	return adc->common->data->injected_started(adc);
+}
+
+static inline bool stm32_adc_clk_sel(struct stm32_adc *adc)
+{
+	return adc->common->data->clk_sel(adc);
+}
+
+static inline int stm32_adc_enable(struct stm32_adc *adc)
+{
+	if (adc->common->data->enable)
+		return adc->common->data->enable(adc);
+
+	adc->en = true;
+
+	return 0;
+}
+
+static inline bool stm32_adc_is_enabled(struct stm32_adc *adc)
+{
+	if (adc->common->data->is_enabled)
+		return adc->common->data->is_enabled(adc);
+	else
+		return adc->en;
+}
+
+static inline void stm32_adc_disable(struct stm32_adc *adc)
+{
+	/* Check there is no regular or injected on-going conversions */
+	if (stm32_adc_is_started(adc))
+		return;
+
+	if (adc->common->data->disable)
+		adc->common->data->disable(adc);
+	else
+		adc->en = false;
+}
+
+/* STM32 ADC registers access routines */
+static inline u32 stm32_adc_common_readl(struct stm32_adc_common *com, u32 reg)
+{
+	u32 val = readl_relaxed(com->base + reg);
+
+	return val;
+}
+
+static inline void stm32_adc_common_writel(struct stm32_adc_common *com,
+					   u32 reg, u32 val)
+{
+	writel_relaxed(val, com->base + reg);
+}
+
+static inline u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
+{
+	u32 val = readl_relaxed(adc->common->base + adc->offset + reg);
+
+	return val;
+}
+
+#define stm32_adc_readl_addr(addr)	stm32_adc_readl(adc, addr)
+
+#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \
+	readx_poll_timeout(stm32_adc_readl_addr, reg, val, \
+			   cond, sleep_us, timeout_us)
+
+static inline void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
+{
+	writel_relaxed(val, adc->common->base + adc->offset + reg);
+}
+
+static inline void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&adc->lock, flags);
+	stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
+	spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&adc->lock, flags);
+	stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
+	spin_unlock_irqrestore(&adc->lock, flags);
+}
+
+/* STM32 common extended attributes */
+extern const struct iio_enum stm32_adc_trig_pol;
+int stm32_adc_probe(struct platform_device *pdev);
+int stm32_adc_remove(struct platform_device *pdev);
+
+#endif
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
new file mode 100644
index 0000000..147fe9c
--- /dev/null
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -0,0 +1,574 @@
+/*
+ * This file is part of STM32F4 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@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/clk.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/platform_device.h>
+#include "stm32-adc.h"
+
+/*
+ * STM32F4 - ADC global register map
+ * ________________________________________________________
+ * | Offset |                 Register                    |
+ * --------------------------------------------------------
+ * | 0x000  |                Master ADC1                  |
+ * --------------------------------------------------------
+ * | 0x100  |                Slave ADC2                   |
+ * --------------------------------------------------------
+ * | 0x200  |                Slave ADC3                   |
+ * --------------------------------------------------------
+ * | 0x300  |         Master & Slave common regs          |
+ * --------------------------------------------------------
+ */
+
+/* STM32F4 - Registers for each ADC instance */
+#define STM32F4_ADCX_SR			0x00
+#define STM32F4_ADCX_CR1		0x04
+#define STM32F4_ADCX_CR2		0x08
+#define STM32F4_ADCX_SMPR1		0x0C
+#define STM32F4_ADCX_SMPR2		0x10
+#define STM32F4_ADCX_HTR		0x24
+#define STM32F4_ADCX_LTR		0x28
+#define STM32F4_ADCX_SQR1		0x2C
+#define STM32F4_ADCX_SQR2		0x30
+#define STM32F4_ADCX_SQR3		0x34
+#define STM32F4_ADCX_JSQR		0x38
+#define STM32F4_ADCX_JDR1		0x3C
+#define STM32F4_ADCX_JDR2		0x40
+#define STM32F4_ADCX_JDR3		0x44
+#define STM32F4_ADCX_JDR4		0x48
+#define STM32F4_ADCX_DR			0x4C
+
+/* STM32 - Master & slave registers (common for all instances: 1, 2 & 3) */
+#define STM32F4_ADC_CSR			(STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32F4_ADC_CCR			(STM32_ADCX_COMN_OFFSET + 0x04)
+#define STM32F4_ADC_CDR			(STM32_ADCX_COMN_OFFSET + 0x08)
+
+/* STM32F4_ADCX_SR - bit fields */
+#define STM32F4_OVR			BIT(5)
+#define STM32F4_STRT			BIT(4)
+#define STM32F4_JSTRT			BIT(3)
+#define STM32F4_JEOC			BIT(2)
+#define STM32F4_EOC			BIT(1)
+#define STM32F4_AWD			BIT(0)
+
+/* STM32F4_ADCX_CR1 - bit fields */
+#define STM32F4_OVRIE			BIT(26)
+#define STM32F4_RES_SHIFT		24
+#define STM32F4_RES_MASK		GENMASK(25, 24)
+#define STM32F4_AWDEN			BIT(23)
+#define STM32F4_JAWDEN			BIT(22)
+#define STM32F4_DISCNUM_SHIFT		13
+#define STM32F4_DISCNUM_MASK		GENMASK(15, 13)
+#define STM32F4_JDISCEN			BIT(12)
+#define STM32F4_DISCEN			BIT(11)
+#define STM32F4_JAUTO			BIT(10)
+#define STM32F4_AWDSGL			BIT(9)
+#define STM32F4_SCAN			BIT(8)
+#define STM32F4_JEOCIE			BIT(7)
+#define STM32F4_AWDIE			BIT(6)
+#define STM32F4_EOCIE			BIT(5)
+#define STM32F4_AWDCH_SHIFT		0
+#define STM32F4_AWDCH_MASK		GENMASK(4, 0)
+
+/* STM32F4_ADCX_CR2 - bit fields */
+#define STM32F4_SWSTART			BIT(30)
+#define STM32F4_EXTEN_SHIFT		28
+#define STM32F4_EXTEN_MASK		GENMASK(29, 28)
+#define STM32F4_EXTSEL_SHIFT		24
+#define STM32F4_EXTSEL_MASK		GENMASK(27, 24)
+#define STM32F4_JSWSTART		BIT(22)
+#define STM32F4_JEXTEN_SHIFT		20
+#define STM32F4_JEXTEN_MASK		GENMASK(21, 20)
+#define STM32F4_JEXTSEL_SHIFT		16
+#define STM32F4_JEXTSEL_MASK		GENMASK(19, 16)
+#define STM32F4_ALIGN			BIT(11)
+#define STM32F4_EOCS			BIT(10)
+#define STM32F4_DDS			BIT(9)
+#define STM32F4_DMA			BIT(8)
+#define STM32F4_CONT			BIT(1)
+#define STM32F4_ADON			BIT(0)
+
+/* STM32F4_ADCX_SMPR1 - bit fields */
+#define STM32F4_SMP18_SHIFT		24
+#define STM32F4_SMP18_MASK		GENMASK(26, 24)
+#define STM32F4_SMP17_SHIFT		21
+#define STM32F4_SMP17_MASK		GENMASK(23, 21)
+#define STM32F4_SMP16_SHIFT		18
+#define STM32F4_SMP16_MASK		GENMASK(20, 18)
+#define STM32F4_SMP15_SHIFT		15
+#define STM32F4_SMP15_MASK		GENMASK(17, 15)
+#define STM32F4_SMP14_SHIFT		12
+#define STM32F4_SMP14_MASK		GENMASK(14, 12)
+#define STM32F4_SMP13_SHIFT		9
+#define STM32F4_SMP13_MASK		GENMASK(11, 9)
+#define STM32F4_SMP12_SHIFT		6
+#define STM32F4_SMP12_MASK		GENMASK(8, 6)
+#define STM32F4_SMP11_SHIFT		3
+#define STM32F4_SMP11_MASK		GENMASK(5, 3)
+#define STM32F4_SMP10_SHIFT		0
+#define STM32F4_SMP10_MASK		GENMASK(2, 0)
+
+/* STM32F4_ADCX_SMPR2 - bit fields */
+#define STM32F4_SMP9_SHIFT		27
+#define STM32F4_SMP9_MASK		GENMASK(29, 27)
+#define STM32F4_SMP8_SHIFT		24
+#define STM32F4_SMP8_MASK		GENMASK(26, 24)
+#define STM32F4_SMP7_SHIFT		21
+#define STM32F4_SMP7_MASK		GENMASK(23, 21)
+#define STM32F4_SMP6_SHIFT		18
+#define STM32F4_SMP6_MASK		GENMASK(20, 18)
+#define STM32F4_SMP5_SHIFT		15
+#define STM32F4_SMP5_MASK		GENMASK(17, 15)
+#define STM32F4_SMP4_SHIFT		12
+#define STM32F4_SMP4_MASK		GENMASK(14, 12)
+#define STM32F4_SMP3_SHIFT		9
+#define STM32F4_SMP3_MASK		GENMASK(11, 9)
+#define STM32F4_SMP2_SHIFT		6
+#define STM32F4_SMP2_MASK		GENMASK(8, 6)
+#define STM32F4_SMP1_SHIFT		3
+#define STM32F4_SMP1_MASK		GENMASK(5, 3)
+#define STM32F4_SMP0_SHIFT		0
+#define STM32F4_SMP0_MASK		GENMASK(2, 0)
+enum stm32f4_adc_smpr {
+	STM32F4_SMPR_3_CK_CYCLES,
+	STM32F4_SMPR_15_CK_CYCLES,
+	STM32F4_SMPR_28_CK_CYCLES,
+	STM32F4_SMPR_56_CK_CYCLES,
+	STM32F4_SMPR_84_CK_CYCLES,
+	STM32F4_SMPR_112_CK_CYCLES,
+	STM32F4_SMPR_144_CK_CYCLES,
+	STM32F4_SMPR_480_CK_CYCLES,
+};
+
+/* STM32F4_ADCX_SQR1 - bit fields */
+#define STM32F4_L_SHIFT			20
+#define STM32F4_L_MASK			GENMASK(23, 20)
+#define STM32F4_SQ16_SHIFT		15
+#define STM32F4_SQ16_MASK		GENMASK(19, 15)
+#define STM32F4_SQ15_SHIFT		10
+#define STM32F4_SQ15_MASK		GENMASK(14, 10)
+#define STM32F4_SQ14_SHIFT		5
+#define STM32F4_SQ14_MASK		GENMASK(9, 5)
+#define STM32F4_SQ13_SHIFT		0
+#define STM32F4_SQ13_MASK		GENMASK(4, 0)
+
+/* STM32F4_ADCX_SQR2 - bit fields */
+#define STM32F4_SQ12_SHIFT		25
+#define STM32F4_SQ12_MASK		GENMASK(29, 25)
+#define STM32F4_SQ11_SHIFT		20
+#define STM32F4_SQ11_MASK		GENMASK(24, 20)
+#define STM32F4_SQ10_SHIFT		15
+#define STM32F4_SQ10_MASK		GENMASK(19, 15)
+#define STM32F4_SQ9_SHIFT		10
+#define STM32F4_SQ9_MASK		GENMASK(14, 10)
+#define STM32F4_SQ8_SHIFT		5
+#define STM32F4_SQ8_MASK		GENMASK(9, 5)
+#define STM32F4_SQ7_SHIFT		0
+#define STM32F4_SQ7_MASK		GENMASK(4, 0)
+
+/* STM32F4_ADCX_SQR3 - bit fields */
+#define STM32F4_SQ6_SHIFT		25
+#define STM32F4_SQ6_MASK		GENMASK(29, 25)
+#define STM32F4_SQ5_SHIFT		20
+#define STM32F4_SQ5_MASK		GENMASK(24, 20)
+#define STM32F4_SQ4_SHIFT		15
+#define STM32F4_SQ4_MASK		GENMASK(19, 15)
+#define STM32F4_SQ3_SHIFT		10
+#define STM32F4_SQ3_MASK		GENMASK(14, 10)
+#define STM32F4_SQ2_SHIFT		5
+#define STM32F4_SQ2_MASK		GENMASK(9, 5)
+#define STM32F4_SQ1_SHIFT		0
+#define STM32F4_SQ1_MASK		GENMASK(4, 0)
+
+/* STM32F4_ADCX_JSQR - bit fields */
+#define STM32F4_JL_SHIFT		20
+#define STM32F4_JL_MASK			GENMASK(21, 20)
+#define STM32F4_JSQ4_SHIFT		15
+#define STM32F4_JSQ4_MASK		GENMASK(19, 15)
+#define STM32F4_JSQ3_SHIFT		10
+#define STM32F4_JSQ3_MASK		GENMASK(14, 10)
+#define STM32F4_JSQ2_SHIFT		5
+#define STM32F4_JSQ2_MASK		GENMASK(9, 5)
+#define STM32F4_JSQ1_SHIFT		0
+#define STM32F4_JSQ1_MASK		GENMASK(4, 0)
+
+/* STM32F4_ADC_CCR - bit fields */
+#define STM32F4_ADC_ADCPRE_SHIFT	16
+#define STM32F4_ADC_ADCPRE_MASK		GENMASK(17, 16)
+
+/*
+ * stm32 ADC1, ADC2 & ADC3 are tightly coupled and may be used in multi mode
+ * Define here all inputs for all ADC instances
+ */
+static const struct stm32_adc_chan_spec stm32f4_adc1_channels[] = {
+	/* master ADC1 */
+	{ IIO_VOLTAGE, 0, "in0" },
+	{ IIO_VOLTAGE, 1, "in1" },
+	{ IIO_VOLTAGE, 2, "in2" },
+	{ IIO_VOLTAGE, 3, "in3" },
+	{ IIO_VOLTAGE, 4, "in4" },
+	{ IIO_VOLTAGE, 5, "in5" },
+	{ IIO_VOLTAGE, 6, "in6" },
+	{ IIO_VOLTAGE, 7, "in7" },
+	{ IIO_VOLTAGE, 8, "in8" },
+	{ IIO_VOLTAGE, 9, "in9" },
+	{ IIO_VOLTAGE, 10, "in10" },
+	{ IIO_VOLTAGE, 11, "in11" },
+	{ IIO_VOLTAGE, 12, "in12" },
+	{ IIO_VOLTAGE, 13, "in13" },
+	{ IIO_VOLTAGE, 14, "in14" },
+	{ IIO_VOLTAGE, 15, "in15" },
+	/* internal analog sources available on input 16 to 18 */
+	{ IIO_VOLTAGE, 16, "in16" },
+	{ IIO_VOLTAGE, 17, "in17" },
+	{ IIO_VOLTAGE, 18, "in18" },
+};
+
+static const struct stm32_adc_chan_spec stm32f4_adc23_channels[] = {
+	/* slave ADC2 /	ADC3 */
+	{ IIO_VOLTAGE, 0, "in0" },
+	{ IIO_VOLTAGE, 1, "in1" },
+	{ IIO_VOLTAGE, 2, "in2" },
+	{ IIO_VOLTAGE, 3, "in3" },
+	{ IIO_VOLTAGE, 4, "in4" },
+	{ IIO_VOLTAGE, 5, "in5" },
+	{ IIO_VOLTAGE, 6, "in6" },
+	{ IIO_VOLTAGE, 7, "in7" },
+	{ IIO_VOLTAGE, 8, "in8" },
+	{ IIO_VOLTAGE, 9, "in9" },
+	{ IIO_VOLTAGE, 10, "in10" },
+	{ IIO_VOLTAGE, 11, "in11" },
+	{ IIO_VOLTAGE, 12, "in12" },
+	{ IIO_VOLTAGE, 13, "in13" },
+	{ IIO_VOLTAGE, 14, "in14" },
+	{ IIO_VOLTAGE, 15, "in15" },
+};
+
+/* Triggers for regular channels */
+static const struct stm32_adc_trig_info stm32f4_adc_ext_triggers[] = {
+	{ STM32_EXT0, "TIM1_CH1" },
+	{ STM32_EXT1, "TIM1_CH2" },
+	{ STM32_EXT2, "TIM1_CH3" },
+	{ STM32_EXT3, "TIM2_CH2" },
+	{ STM32_EXT4, "TIM2_CH3" },
+	{ STM32_EXT5, "TIM2_CH4" },
+	{ STM32_EXT6, "TIM2_TRGO" },
+	{ STM32_EXT7, "TIM3_CH1" },
+	{ STM32_EXT8, "TIM3_TRGO" },
+	{ STM32_EXT9, "TIM4_CH4" },
+	{ STM32_EXT10, "TIM5_CH1" },
+	{ STM32_EXT11, "TIM5_CH2" },
+	{ STM32_EXT12, "TIM5_CH3" },
+	{ STM32_EXT13, "TIM8_CH1" },
+	{ STM32_EXT14, "TIM8_TRGO" },
+	{ STM32_EXT15, "EXTI_11" },
+	{},
+};
+
+/* Triggers for injected channels */
+static const struct stm32_adc_trig_info  stm32f4_adc_jext_triggers[] = {
+	{ STM32_JEXT0, "TIM1_CH4" },
+	{ STM32_JEXT1, "TIM1_TRGO" },
+	{ STM32_JEXT2, "TIM2_CH1" },
+	{ STM32_JEXT3, "TIM2_TRGO" },
+	{ STM32_JEXT4, "TIM3_CH2" },
+	{ STM32_JEXT5, "TIM3_CH4" },
+	{ STM32_JEXT6, "TIM4_CH1" },
+	{ STM32_JEXT7, "TIM4_CH2" },
+	{ STM32_JEXT8, "TIM4_CH3" },
+	{ STM32_JEXT9, "TIM4_TRGO" },
+	{ STM32_JEXT10, "TIM5_CH4" },
+	{ STM32_JEXT11, "TIM5_TRGO" },
+	{ STM32_JEXT12, "TIM8_CH2" },
+	{ STM32_JEXT13, "TIM8_CH3" },
+	{ STM32_JEXT14, "TIM8_CH4" },
+	{ STM32_JEXT15, "EXTI_15" },
+	{},
+};
+
+static const struct stm32_adc_info stm32f4_adc_info[] = {
+	{
+		.name = "adc1-master",
+		.reg = 0x0,
+		.channels = stm32f4_adc1_channels,
+		.max_channels = ARRAY_SIZE(stm32f4_adc1_channels),
+	},
+	{
+		.name = "adc2-slave",
+		.reg = 0x100,
+		.channels = stm32f4_adc23_channels,
+		.max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
+	},
+	{
+		.name = "adc3-slave",
+		.reg = 0x200,
+		.channels = stm32f4_adc23_channels,
+		.max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
+	},
+	{},
+};
+
+/**
+ * stm32f4_sqr_regs - describe regular sequence registers
+ * - L: sequence len (register & bit field)
+ * - SQ1..SQ16: sequence entries (register & bit field)
+ */
+static const struct stm32_adc_regs stm32f4_sqr_regs[STM32_ADC_MAX_SQ + 1] = {
+	/* L: len bit field description to be kept as first element */
+	{ STM32F4_ADCX_SQR1, STM32F4_L_MASK, STM32F4_L_SHIFT },
+	/* SQ1..SQ16 registers & bit fields */
+	{ STM32F4_ADCX_SQR3, STM32F4_SQ1_MASK, STM32F4_SQ1_SHIFT },
+	{ STM32F4_ADCX_SQR3, STM32F4_SQ2_MASK, STM32F4_SQ2_SHIFT },
+	{ STM32F4_ADCX_SQR3, STM32F4_SQ3_MASK, STM32F4_SQ3_SHIFT },
+	{ STM32F4_ADCX_SQR3, STM32F4_SQ4_MASK, STM32F4_SQ4_SHIFT },
+	{ STM32F4_ADCX_SQR3, STM32F4_SQ5_MASK, STM32F4_SQ5_SHIFT },
+	{ STM32F4_ADCX_SQR3, STM32F4_SQ6_MASK, STM32F4_SQ6_SHIFT },
+	{ STM32F4_ADCX_SQR2, STM32F4_SQ7_MASK, STM32F4_SQ7_SHIFT },
+	{ STM32F4_ADCX_SQR2, STM32F4_SQ8_MASK, STM32F4_SQ8_SHIFT },
+	{ STM32F4_ADCX_SQR2, STM32F4_SQ9_MASK, STM32F4_SQ9_SHIFT },
+	{ STM32F4_ADCX_SQR2, STM32F4_SQ10_MASK, STM32F4_SQ10_SHIFT },
+	{ STM32F4_ADCX_SQR2, STM32F4_SQ11_MASK, STM32F4_SQ11_SHIFT },
+	{ STM32F4_ADCX_SQR2, STM32F4_SQ12_MASK, STM32F4_SQ12_SHIFT },
+	{ STM32F4_ADCX_SQR1, STM32F4_SQ13_MASK, STM32F4_SQ13_SHIFT },
+	{ STM32F4_ADCX_SQR1, STM32F4_SQ14_MASK, STM32F4_SQ14_SHIFT },
+	{ STM32F4_ADCX_SQR1, STM32F4_SQ15_MASK, STM32F4_SQ15_SHIFT },
+	{ STM32F4_ADCX_SQR1, STM32F4_SQ16_MASK, STM32F4_SQ16_SHIFT },
+};
+
+/**
+ * stm32f4_jsqr_reg - describe injected sequence register:
+ * - JL: injected sequence len
+ * - JSQ4..SQ1: sequence entries
+ * When JL == 3, ADC converts JSQ1, JSQ2, JSQ3, JSQ4
+ * When JL == 2, ADC converts JSQ2, JSQ3, JSQ4
+ * When JL == 1, ADC converts JSQ3, JSQ4
+ * When JL == 0, ADC converts JSQ4
+ */
+static const struct stm32_adc_regs stm32f4_jsqr_reg[STM32_ADC_MAX_JSQ + 1] = {
+	/* JL: len bit field description to be kept as first element */
+	{STM32F4_ADCX_JSQR, STM32F4_JL_MASK, STM32F4_JL_SHIFT},
+	/* JSQ4..JSQ1 registers & bit fields */
+	{STM32F4_ADCX_JSQR, STM32F4_JSQ4_MASK, STM32F4_JSQ4_SHIFT},
+	{STM32F4_ADCX_JSQR, STM32F4_JSQ3_MASK, STM32F4_JSQ3_SHIFT},
+	{STM32F4_ADCX_JSQR, STM32F4_JSQ2_MASK, STM32F4_JSQ2_SHIFT},
+	{STM32F4_ADCX_JSQR, STM32F4_JSQ1_MASK, STM32F4_JSQ1_SHIFT},
+};
+
+static const struct stm32_adc_trig_reginfo stm32f4_adc_trig_reginfo = {
+	.reg = STM32F4_ADCX_CR2,
+	.exten_mask = STM32F4_EXTEN_MASK,
+	.exten_shift = STM32F4_EXTEN_SHIFT,
+	.extsel_mask = STM32F4_EXTSEL_MASK,
+	.extsel_shift = STM32F4_EXTSEL_SHIFT,
+};
+
+static const struct stm32_adc_trig_reginfo stm32f4_adc_jtrig_reginfo = {
+	.reg = STM32F4_ADCX_CR2,
+	.exten_mask = STM32F4_JEXTEN_MASK,
+	.exten_shift = STM32F4_JEXTEN_SHIFT,
+	.extsel_mask = STM32F4_JEXTSEL_MASK,
+	.extsel_shift = STM32F4_JEXTSEL_SHIFT,
+};
+
+static const struct stm32_adc_reginfo stm32f4_adc_reginfo = {
+	.isr = STM32F4_ADCX_SR,
+	.eoc = STM32F4_EOC,
+	.jeoc = STM32F4_JEOC,
+	.ier = STM32F4_ADCX_CR1,
+	.eocie = STM32F4_EOCIE,
+	.jeocie = STM32F4_JEOCIE,
+	.dr = STM32F4_ADCX_DR,
+	.jdr = {
+		STM32F4_ADCX_JDR1,
+		STM32F4_ADCX_JDR2,
+		STM32F4_ADCX_JDR3,
+		STM32F4_ADCX_JDR4,
+	},
+	.sqr_regs = stm32f4_sqr_regs,
+	.jsqr_reg = stm32f4_jsqr_reg,
+	.trig_reginfo = &stm32f4_adc_trig_reginfo,
+	.jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
+};
+
+static bool stm32f4_adc_is_started(struct stm32_adc *adc)
+{
+	u32 val = stm32_adc_readl(adc, STM32F4_ADCX_CR2) & STM32F4_ADON;
+
+	return !!val;
+}
+
+static bool stm32f4_adc_regular_started(struct stm32_adc *adc)
+{
+	u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_STRT;
+
+	return !!val;
+}
+
+static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
+{
+	u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_JSTRT;
+
+	return !!val;
+}
+
+/**
+ * stm32f4_adc_start_conv() - Start regular or injected conversions
+ * @adc: stm32 adc instance
+ *
+ * Start single conversions for regular or injected channels.
+ */
+static int stm32f4_adc_start_conv(struct stm32_adc *adc)
+{
+	u32 trig_msk, start_msk;
+
+	dev_dbg(adc->common->dev, "%s %s\n", __func__,
+		adc->injected ? "injected" : "regular");
+
+	stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
+
+	if (!stm32f4_adc_is_started(adc)) {
+		stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
+				   STM32F4_EOCS | STM32F4_ADON);
+
+		/* Wait for Power-up time (tSTAB from datasheet) */
+		usleep_range(2, 3);
+	}
+
+	if (adc->injected) {
+		trig_msk = STM32F4_JEXTEN_MASK;
+		start_msk = STM32F4_JSWSTART;
+	} else {
+		trig_msk = STM32F4_EXTEN_MASK;
+		start_msk = STM32F4_SWSTART;
+	}
+
+	/* Software start ? (e.g. trigger detection disabled ?) */
+	if (!(stm32_adc_readl(adc, STM32F4_ADCX_CR2) & trig_msk))
+		stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, start_msk);
+
+	return 0;
+}
+
+static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
+{
+	u32 val;
+
+	dev_dbg(adc->common->dev, "%s %s\n", __func__,
+		adc->injected ? "injected" : "regular");
+
+	/* First disable trigger for either regular or injected channels */
+	if (adc->injected) {
+		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_JEXTEN_MASK);
+		stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_JSTRT);
+	} else {
+		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
+		stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_STRT);
+	}
+
+	/* Disable adc when all triggered conversion have been disabled */
+	val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
+	val &= STM32F4_EXTEN_MASK | STM32F4_JEXTEN_MASK;
+	if (!val) {
+		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
+		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
+	}
+
+	return 0;
+}
+
+/* ADC internal common clock prescaler division ratios */
+static int stm32f4_pclk_div[] = {2, 4, 6, 8};
+
+/**
+ * stm32f4_adc_clk_sel() - Select ADC common clock prescaler
+ * @adc: stm32 adc instance
+ * Select clock prescaler used for analog conversions.
+ */
+static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
+{
+	struct stm32_adc_common *common = adc->common;
+	unsigned long rate;
+	u32 val;
+	int i;
+
+	/* Common prescaler is set only once, when 1st ADC instance starts */
+	list_for_each_entry(adc, &common->adc_list, adc_list)
+		if (stm32f4_adc_is_started(adc))
+			return 0;
+
+	rate = clk_get_rate(common->aclk);
+	for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
+		if ((rate / stm32f4_pclk_div[i]) <=
+		    common->data->max_clock_rate)
+			break;
+	}
+	if (i >= ARRAY_SIZE(stm32f4_pclk_div))
+		return -EINVAL;
+
+	val = stm32_adc_common_readl(common, STM32F4_ADC_CCR);
+	val &= ~STM32F4_ADC_ADCPRE_MASK;
+	val |= i << STM32F4_ADC_ADCPRE_SHIFT;
+	stm32_adc_common_writel(common, STM32F4_ADC_CCR, val);
+
+	dev_dbg(common->dev, "Using analog clock source at %ld kHz\n",
+		rate / (stm32f4_pclk_div[i] * 1000));
+
+	return 0;
+}
+
+static const struct stm32_adc_ops stm32f4_adc_ops = {
+	.adc_info = stm32f4_adc_info,
+	.ext_triggers = stm32f4_adc_ext_triggers,
+	.jext_triggers = stm32f4_adc_jext_triggers,
+	.adc_reginfo = &stm32f4_adc_reginfo,
+	.highres = 12,
+	.max_clock_rate = 36000000,
+	.clk_sel = stm32f4_adc_clk_sel,
+	.start_conv = stm32f4_adc_start_conv,
+	.stop_conv = stm32f4_adc_stop_conv,
+	.is_started = stm32f4_adc_is_started,
+	.regular_started = stm32f4_adc_regular_started,
+	.injected_started = stm32f4_adc_injected_started,
+};
+
+static const struct of_device_id stm32f4_adc_of_match[] = {
+	{ .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_ops},
+	{},
+};
+MODULE_DEVICE_TABLE(of, stm32f4_adc_of_match);
+
+static struct platform_driver stm32f4_adc_driver = {
+	.probe = stm32_adc_probe,
+	.remove = stm32_adc_remove,
+	.driver = {
+		.name = "stm32f4-adc",
+		.of_match_table = stm32f4_adc_of_match,
+	},
+};
+
+module_platform_driver(stm32f4_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32F4 ADC driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 03/10] iio: adc: stm32: add optional dma support
  2016-10-25 16:25 [PATCH 00/10] Add support for STM32 ADC Fabrice Gasnier
  2016-10-25 16:25 ` [PATCH 01/10] Documentation: dt-bindings: Document STM32 ADC DT bindings Fabrice Gasnier
  2016-10-25 16:25 ` [PATCH 02/10] iio: adc: Add stm32 support Fabrice Gasnier
@ 2016-10-25 16:25 ` Fabrice Gasnier
  2016-10-30 15:34   ` Jonathan Cameron
  2016-10-25 16:25 ` [PATCH 04/10] iio: adc: stm32: add optional support for exti gpios Fabrice Gasnier
                   ` (6 subsequent siblings)
  9 siblings, 1 reply; 20+ messages in thread
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier

Add optional DMA support to STM32 ADC.
Use dma cyclic mode with at least two periods.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 drivers/iio/adc/stm32/Kconfig       |   2 +
 drivers/iio/adc/stm32/stm32-adc.c   | 222 ++++++++++++++++++++++++++++++++----
 drivers/iio/adc/stm32/stm32-adc.h   |  15 +++
 drivers/iio/adc/stm32/stm32f4-adc.c |  20 +++-
 4 files changed, 235 insertions(+), 24 deletions(-)

diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
index 245d037..5554ac8 100644
--- a/drivers/iio/adc/stm32/Kconfig
+++ b/drivers/iio/adc/stm32/Kconfig
@@ -8,6 +8,7 @@ config STM32_ADC
 	select REGULATOR_FIXED_VOLTAGE
 	select IIO_BUFFER
 	select IIO_TRIGGERED_BUFFER
+	select IRQ_WORK
 	help
 	  Say yes here to build the driver for the STMicroelectronics
 	  STM32 analog-to-digital converter (ADC).
@@ -18,6 +19,7 @@ config STM32_ADC
 config STM32F4_ADC
 	tristate "STMicroelectronics STM32F4 adc"
 	depends on ARCH_STM32 || COMPILE_TEST
+	depends on HAS_DMA
 	depends on OF
 	select STM32_ADC
 	help
diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 1e0850d..25d0307 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -21,6 +21,7 @@
 
 #include <linux/clk.h>
 #include <linux/debugfs.h>
+#include <linux/dma-mapping.h>
 #include <linux/iio/iio.h>
 #include <linux/iio/buffer.h>
 #include <linux/iio/events.h>
@@ -371,6 +372,34 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
 	return ret;
 }
 
+static int stm32_adc_residue(struct stm32_adc *adc)
+{
+	struct dma_tx_state state;
+	enum dma_status status;
+
+	if (!adc->rx_buf)
+		return 0;
+
+	status = dmaengine_tx_status(adc->dma_chan,
+				     adc->dma_chan->cookie,
+				     &state);
+	if (status == DMA_IN_PROGRESS) {
+		/* Residue is size in bytes from end of buffer */
+		int i = adc->rx_buf_sz - state.residue;
+		int size;
+
+		/* Return available bytes */
+		if (i >= adc->bufi)
+			size = i - adc->bufi;
+		else
+			size = adc->rx_buf_sz - adc->bufi + i;
+
+		return size;
+	}
+
+	return 0;
+}
+
 /**
  * stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
  */
@@ -394,8 +423,8 @@ static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
 	stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
 	status &= mask;
 
-	/* Regular data */
-	if (status & reginfo->eoc) {
+	/* Regular data (when dma isn't used) */
+	if ((status & reginfo->eoc) && (!adc->rx_buf)) {
 		adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
 		if (iio_buffer_enabled(indio_dev)) {
 			adc->bufi++;
@@ -553,29 +582,154 @@ static int stm32_adc_validate_device(struct iio_trigger *trig,
 	return indio != indio_dev ? -EINVAL : 0;
 }
 
-static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
-				       bool state)
+static void stm32_adc_dma_irq_work(struct irq_work *work)
 {
-	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct stm32_adc *adc = container_of(work, struct stm32_adc, work);
+	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+	/**
+	 * iio_trigger_poll calls generic_handle_irq(). So, it requires hard
+	 * irq context, and cannot be called directly from dma callback,
+	 * dma cb has to schedule this work instead.
+	 */
+	iio_trigger_poll(indio_dev->trig);
+}
+
+static void stm32_adc_dma_buffer_done(void *data)
+{
+	struct iio_dev *indio_dev = data;
 	struct stm32_adc *adc = iio_priv(indio_dev);
-	int ret;
 
-	if (state) {
-		/* Reset adc buffer index */
-		adc->bufi = 0;
+	/* invoques iio_trigger_poll() from hard irq context */
+	irq_work_queue(&adc->work);
+}
+
+static int stm32_adc_buffer_alloc_dma_start(struct iio_dev *indio_dev)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	const struct stm32_adc_reginfo *reginfo =
+		adc->common->data->adc_reginfo;
+	struct dma_async_tx_descriptor *desc;
+	struct dma_slave_config config;
+	dma_cookie_t cookie;
+	int ret, size, watermark;
 
+	/* Reset adc buffer index */
+	adc->bufi = 0;
+
+	if (!adc->dma_chan) {
 		/* Allocate adc buffer */
 		adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
 		if (!adc->buffer)
 			return -ENOMEM;
 
+		return 0;
+	}
+
+	/*
+	 * Allocate at least twice the buffer size for dma cyclic transfers, so
+	 * we can work with at least two dma periods. There should be :
+	 * - always one buffer (period) dma is working on
+	 * - one buffer (period) driver can push with iio_trigger_poll().
+	 */
+	size = indio_dev->buffer->bytes_per_datum * indio_dev->buffer->length;
+	size = max(indio_dev->scan_bytes * 2, size);
+
+	adc->rx_buf = dma_alloc_coherent(adc->common->dev, PAGE_ALIGN(size),
+					 &adc->rx_dma_buf,
+					 GFP_KERNEL);
+	if (!adc->rx_buf)
+		return -ENOMEM;
+	adc->rx_buf_sz = size;
+	watermark = indio_dev->buffer->bytes_per_datum
+		* indio_dev->buffer->watermark;
+	watermark = max(indio_dev->scan_bytes, watermark);
+	watermark = rounddown(watermark, indio_dev->scan_bytes);
+
+	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__, size,
+		watermark);
+
+	/* Configure DMA channel to read data register */
+	memset(&config, 0, sizeof(config));
+	config.src_addr = (dma_addr_t)adc->common->phys_base;
+	config.src_addr += adc->offset + reginfo->dr;
+	config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+	ret = dmaengine_slave_config(adc->dma_chan, &config);
+	if (ret)
+		goto config_err;
+
+	/* Prepare a DMA cyclic transaction */
+	desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
+					 adc->rx_dma_buf,
+					 size, watermark,
+					 DMA_DEV_TO_MEM,
+					 DMA_PREP_INTERRUPT);
+	if (!desc) {
+		ret = -ENODEV;
+		goto config_err;
+	}
+
+	desc->callback = stm32_adc_dma_buffer_done;
+	desc->callback_param = indio_dev;
+
+	cookie = dmaengine_submit(desc);
+	if (dma_submit_error(cookie)) {
+		ret = dma_submit_error(cookie);
+		goto config_err;
+	}
+
+	/* Issue pending DMA requests */
+	dma_async_issue_pending(adc->dma_chan);
+
+	return 0;
+
+config_err:
+	dma_free_coherent(adc->common->dev, PAGE_ALIGN(size), adc->rx_buf,
+			  adc->rx_dma_buf);
+
+	return ret;
+}
+
+static void stm32_adc_dma_stop_buffer_free(struct iio_dev *indio_dev)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+
+	if (!adc->dma_chan) {
+		kfree(adc->buffer);
+	} else {
+		dmaengine_terminate_all(adc->dma_chan);
+		irq_work_sync(&adc->work);
+		dma_free_coherent(adc->common->dev, PAGE_ALIGN(adc->rx_buf_sz),
+				  adc->rx_buf, adc->rx_dma_buf);
+		adc->rx_buf = NULL;
+	}
+
+	adc->buffer = NULL;
+}
+
+static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
+				       bool state)
+{
+	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	int ret;
+
+	if (state) {
+		ret = stm32_adc_buffer_alloc_dma_start(indio_dev);
+		if (ret) {
+			dev_err(&indio_dev->dev, "alloc failed\n");
+			return ret;
+		}
+
 		ret = stm32_adc_set_trig(indio_dev, trig);
 		if (ret) {
 			dev_err(&indio_dev->dev, "Can't set trigger\n");
 			goto err_buffer_free;
 		}
 
-		stm32_adc_conv_irq_enable(adc);
+		if (!adc->dma_chan)
+			stm32_adc_conv_irq_enable(adc);
 
 		ret = stm32_adc_start_conv(adc);
 		if (ret) {
@@ -589,25 +743,25 @@ static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
 			return ret;
 		}
 
-		stm32_adc_conv_irq_disable(adc);
+		if (!adc->dma_chan)
+			stm32_adc_conv_irq_disable(adc);
 
 		ret = stm32_adc_set_trig(indio_dev, NULL);
 		if (ret)
 			dev_warn(&indio_dev->dev, "Can't clear trigger\n");
 
-		kfree(adc->buffer);
-		adc->buffer = NULL;
+		stm32_adc_dma_stop_buffer_free(indio_dev);
 	}
 
 	return 0;
 
 err_irq_trig_disable:
-	stm32_adc_conv_irq_disable(adc);
+	if (!adc->dma_chan)
+		stm32_adc_conv_irq_disable(adc);
 	stm32_adc_set_trig(indio_dev, NULL);
 
 err_buffer_free:
-	kfree(adc->buffer);
-	adc->buffer = NULL;
+	stm32_adc_dma_stop_buffer_free(indio_dev);
 
 	return ret;
 }
@@ -624,17 +778,30 @@ static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
 	struct iio_dev *indio_dev = pf->indio_dev;
 	struct stm32_adc *adc = iio_priv(indio_dev);
 
-	dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
-
-	/* reset buffer index */
-	adc->bufi = 0;
-	iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
-					   pf->timestamp);
+	if (!adc->dma_chan) {
+		adc->bufi = 0;
+		iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
+						   pf->timestamp);
+	} else {
+		int residue = stm32_adc_residue(adc);
+
+		while (residue >= indio_dev->scan_bytes) {
+			adc->buffer = (u16 *)&adc->rx_buf[adc->bufi];
+			iio_push_to_buffers_with_timestamp(indio_dev,
+							   adc->buffer,
+							   pf->timestamp);
+			residue -= indio_dev->scan_bytes;
+			adc->bufi += indio_dev->scan_bytes;
+			if (adc->bufi >= adc->rx_buf_sz)
+				adc->bufi = 0;
+		}
+	}
 
 	iio_trigger_notify_done(indio_dev->trig);
 
 	/* re-enable eoc irq */
-	stm32_adc_conv_irq_enable(adc);
+	if (!adc->dma_chan)
+		stm32_adc_conv_irq_enable(adc);
 
 	return IRQ_HANDLED;
 }
@@ -827,6 +994,10 @@ static int stm32_adc_register(struct stm32_adc_common *common,
 	if (ret)
 		goto err_clk_disable;
 
+	adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
+	if (adc->dma_chan)
+		init_irq_work(&adc->work, stm32_adc_dma_irq_work);
+
 	ret = iio_triggered_buffer_setup(indio_dev,
 					 &iio_pollfunc_store_time,
 					 &stm32_adc_trigger_handler,
@@ -850,6 +1021,8 @@ static int stm32_adc_register(struct stm32_adc_common *common,
 	iio_triggered_buffer_cleanup(indio_dev);
 
 err_trig_unregister:
+	if (adc->dma_chan)
+		dma_release_channel(adc->dma_chan);
 	stm32_adc_trig_unregister(indio_dev);
 
 err_clk_disable:
@@ -870,6 +1043,8 @@ static void stm32_adc_unregister(struct stm32_adc *adc)
 	iio_device_unregister(indio_dev);
 	iio_triggered_buffer_cleanup(indio_dev);
 	stm32_adc_trig_unregister(indio_dev);
+	if (adc->dma_chan)
+		dma_release_channel(adc->dma_chan);
 	if (adc->clk) {
 		clk_disable_unprepare(adc->clk);
 		clk_put(adc->clk);
@@ -900,6 +1075,7 @@ int stm32_adc_probe(struct platform_device *pdev)
 	common->base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(common->base))
 		return PTR_ERR(common->base);
+	common->phys_base = res->start;
 
 	common->data = match->data;
 	common->dev = &pdev->dev;
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index 0be603c..01a0dda 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -22,6 +22,9 @@
 #ifndef __STM32_ADC_H
 #define __STM32_ADC_H
 
+#include <linux/dmaengine.h>
+#include <linux/irq_work.h>
+
 /*
  * STM32 - ADC global register map
  * ________________________________________________________
@@ -276,6 +279,11 @@ struct stm32_adc_ops {
  * @num_conv:		expected number of scan conversions
  * @injected:		use injected channels on this adc
  * @lock:		spinlock
+ * @work:		irq work used to call trigger poll routine
+ * @dma_chan:		dma channel
+ * @rx_buf:		dma rx buffer cpu address
+ * @rx_dma_buf:		dma rx buffer bus address
+ * @rx_buf_sz:		dma rx buffer size
  * @clk:		optional adc clock, for this adc instance
  * @calib:		optional calibration data
  * @en:			emulates enabled state on some stm32 adc
@@ -293,6 +301,11 @@ struct stm32_adc {
 	int			num_conv;
 	bool			injected;
 	spinlock_t		lock;		/* interrupt lock */
+	struct irq_work		work;
+	struct dma_chan		*dma_chan;
+	u8			*rx_buf;
+	dma_addr_t		rx_dma_buf;
+	int			rx_buf_sz;
 	struct clk		*clk;
 	void			*calib;
 	bool			en;
@@ -302,6 +315,7 @@ struct stm32_adc {
  * struct stm32_adc_common - private data of ADC driver, common to all
  * ADC instances (ADC block)
  * @dev:		device for this controller
+ * @phys_base:		control registers base physical addr
  * @base:		control registers base cpu addr
  * @irq:		Common irq line for all adc instances
  * @data:		STM32 dependent data from compatible
@@ -313,6 +327,7 @@ struct stm32_adc {
  */
 struct stm32_adc_common {
 	struct device			*dev;
+	phys_addr_t			phys_base;
 	void __iomem			*base;
 	int				irq;
 	const struct stm32_adc_ops	*data;
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
index 147fe9c..4d7a2a8 100644
--- a/drivers/iio/adc/stm32/stm32f4-adc.c
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -437,16 +437,30 @@ static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
  * @adc: stm32 adc instance
  *
  * Start single conversions for regular or injected channels.
+ * Also take care of normal or DMA mode. DMA is used in circular mode for
+ * regular conversions, in IIO buffer modes. Rely on rx_buf as raw
+ * read doesn't use dma, but direct DR read.
  */
 static int stm32f4_adc_start_conv(struct stm32_adc *adc)
 {
-	u32 trig_msk, start_msk;
+	u32 val, trig_msk, start_msk;
+	unsigned long flags;
 
 	dev_dbg(adc->common->dev, "%s %s\n", __func__,
 		adc->injected ? "injected" : "regular");
 
 	stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
 
+	if (!adc->injected) {
+		spin_lock_irqsave(&adc->lock, flags);
+		val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
+		val &= ~(STM32F4_DMA | STM32F4_DDS);
+		if (adc->rx_buf)
+			val |= STM32F4_DMA | STM32F4_DDS;
+		stm32_adc_writel(adc, STM32F4_ADCX_CR2, val);
+		spin_unlock_irqrestore(&adc->lock, flags);
+	}
+
 	if (!stm32f4_adc_is_started(adc)) {
 		stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
 				   STM32F4_EOCS | STM32F4_ADON);
@@ -494,6 +508,10 @@ static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
 		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
 	}
 
+	if (!adc->injected)
+		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2,
+				   STM32F4_DMA | STM32F4_DDS);
+
 	return 0;
 }
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 04/10] iio: adc: stm32: add optional support for exti gpios
  2016-10-25 16:25 [PATCH 00/10] Add support for STM32 ADC Fabrice Gasnier
                   ` (2 preceding siblings ...)
  2016-10-25 16:25 ` [PATCH 03/10] iio: adc: stm32: add optional dma support Fabrice Gasnier
@ 2016-10-25 16:25 ` Fabrice Gasnier
  2016-10-30 14:35   ` Jonathan Cameron
  2016-10-25 16:25 ` [PATCH 05/10] iio: adc: stm32: add trigger polarity ext attr Fabrice Gasnier
                   ` (5 subsequent siblings)
  9 siblings, 1 reply; 20+ messages in thread
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier

STM32 ADC may use EXTi signals routed internally as trigger source
for conversions. Configure them as interrupt to configure this path
in HW to adc.
Note: interrupt handler isn't required here, and corresponding interrupt
can be kept masked at exti controller level.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 drivers/iio/adc/stm32/stm32-adc.c | 45 +++++++++++++++++++++++++++++++++++++++
 drivers/iio/adc/stm32/stm32-adc.h |  3 +++
 2 files changed, 48 insertions(+)

diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 25d0307..1a13450 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -482,6 +482,12 @@ static irqreturn_t stm32_adc_common_isr(int irq, void *data)
 	return ret;
 }
 
+static irqreturn_t stm32_adc_exti_handler(int irq, void *data)
+{
+	/* Exti handler should not be invoqued, and is not used */
+	return IRQ_HANDLED;
+}
+
 /**
  * stm32_adc_validate_trigger() - validate trigger for stm32 adc
  * @indio_dev: IIO device
@@ -1051,6 +1057,41 @@ static void stm32_adc_unregister(struct stm32_adc *adc)
 	}
 }
 
+static int stm32_adc_exti_probe(struct stm32_adc_common *common)
+{
+	int i, irq, ret;
+
+	common->gpios = devm_gpiod_get_array_optional(common->dev, NULL,
+						      GPIOD_IN);
+	if (!common->gpios)
+		return 0;
+
+	for (i = 0; i < common->gpios->ndescs; i++) {
+		irq = gpiod_to_irq(common->gpios->desc[i]);
+		if (irq < 0) {
+			dev_err(common->dev, "gpio %d to irq failed\n", i);
+			return irq;
+		}
+
+		ret = devm_request_irq(common->dev, irq, stm32_adc_exti_handler,
+				       0, dev_name(common->dev), common);
+		if (ret) {
+			dev_err(common->dev, "request IRQ %d failed\n", irq);
+			return ret;
+		}
+
+		/*
+		 * gpios are configured as interrupts, so exti trigger path is
+		 * configured in HW. But getting interrupts when starting
+		 * conversions is unused here, so mask it in on exti
+		 * controller by default.
+		 */
+		disable_irq(irq);
+	}
+
+	return 0;
+}
+
 int stm32_adc_probe(struct platform_device *pdev)
 {
 	struct device_node *np = pdev->dev.of_node, *child;
@@ -1131,6 +1172,10 @@ int stm32_adc_probe(struct platform_device *pdev)
 		goto err_clk_disable;
 	}
 
+	ret = stm32_adc_exti_probe(common);
+	if (ret)
+		goto err_clk_disable;
+
 	/* Parse adc child nodes to retrieve master/slave instances data */
 	for_each_available_child_of_node(np, child) {
 		ret = stm32_adc_register(common, child);
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index 01a0dda..fe3568b 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -23,6 +23,7 @@
 #define __STM32_ADC_H
 
 #include <linux/dmaengine.h>
+#include <linux/gpio/consumer.h>
 #include <linux/irq_work.h>
 
 /*
@@ -323,6 +324,7 @@ struct stm32_adc {
  * @aclk:		common clock for the analog circuitry
  * @vref:		regulator reference
  * @vref_mv:		vref voltage (mv)
+ * @gpio_descs:		gpio descriptor used to configure EXTi triggers
  * @lock:		mutex
  */
 struct stm32_adc_common {
@@ -335,6 +337,7 @@ struct stm32_adc_common {
 	struct clk			*aclk;
 	struct regulator		*vref;
 	int				vref_mv;
+	struct gpio_descs		*gpios;
 	struct mutex			lock;	/* read_raw lock */
 };
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 05/10] iio: adc: stm32: add trigger polarity ext attr
  2016-10-25 16:25 [PATCH 00/10] Add support for STM32 ADC Fabrice Gasnier
                   ` (3 preceding siblings ...)
  2016-10-25 16:25 ` [PATCH 04/10] iio: adc: stm32: add optional support for exti gpios Fabrice Gasnier
@ 2016-10-25 16:25 ` Fabrice Gasnier
  2016-10-30 14:33   ` Jonathan Cameron
  2016-10-25 16:25 ` [PATCH 06/10] iio: adc: stm32: add ext attrs to configure sampling time Fabrice Gasnier
                   ` (4 subsequent siblings)
  9 siblings, 1 reply; 20+ messages in thread
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier

Add trigger polarity extended attribute.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 drivers/iio/adc/stm32/stm32-adc.c   | 41 ++++++++++++++++++++++++++++++++++++-
 drivers/iio/adc/stm32/stm32-adc.h   |  4 ++++
 drivers/iio/adc/stm32/stm32f4-adc.c | 12 +++++++++++
 3 files changed, 56 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 1a13450..9b4b459 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -207,7 +207,11 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
 
 		/* trigger source */
 		extsel = trig_info[ret].extsel;
-		exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
+
+		/* default to rising edge if no polarity */
+		if (adc->exten == STM32_EXTEN_SWTRIG)
+			adc->exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
+		exten = adc->exten;
 	}
 
 	spin_lock_irqsave(&adc->lock, flags);
@@ -221,6 +225,40 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
 	return 0;
 }
 
+static int stm32_adc_set_trig_pol(struct iio_dev *indio_dev,
+				  const struct iio_chan_spec *chan,
+				  unsigned int type)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+
+	adc->exten = type;
+
+	return 0;
+}
+
+static int stm32_adc_get_trig_pol(struct iio_dev *indio_dev,
+				  const struct iio_chan_spec *chan)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+
+	return adc->exten;
+}
+
+static const char * const stm32_trig_pol_items[] = {
+	[STM32_EXTEN_SWTRIG] = "swtrig",
+	[STM32_EXTEN_HWTRIG_RISING_EDGE] = "rising-edge",
+	[STM32_EXTEN_HWTRIG_FALLING_EDGE] = "falling-edge",
+	[STM32_EXTEN_HWTRIG_BOTH_EDGES] = "both-edges",
+};
+
+const struct iio_enum stm32_adc_trig_pol = {
+	.items = stm32_trig_pol_items,
+	.num_items = ARRAY_SIZE(stm32_trig_pol_items),
+	.get = stm32_adc_get_trig_pol,
+	.set = stm32_adc_set_trig_pol,
+};
+EXPORT_SYMBOL_GPL(stm32_adc_trig_pol);
+
 /**
  * stm32_adc_conv_irq_enable() - Unmask end of conversion irq
  * @adc: stm32 adc instance
@@ -893,6 +931,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
 	chan->scan_type.realbits = adc->common->data->highres;
 	chan->scan_type.storagebits = STM32_STORAGEBITS;
 	chan->scan_type.shift = 0;
+	chan->ext_info = adc->common->data->ext_info;
 }
 
 static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index fe3568b..6c9b70d 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -234,6 +234,7 @@ struct stm32_adc_reginfo {
  * @ext_triggers:	Reference to trigger info for regular channels
  * @jext_triggers:	Reference to trigger info for injected channels
  * @adc_reginfo:	stm32 ADC registers description
+ * @ext_info:		Extended channel info
  * @highres:		Max resolution
  * @max_clock_rate:	Max input clock rate
  * @clk_sel:		routine to select common clock and prescaler
@@ -251,6 +252,7 @@ struct stm32_adc_ops {
 	const struct stm32_adc_trig_info *ext_triggers;
 	const struct stm32_adc_trig_info *jext_triggers;
 	const struct stm32_adc_reginfo *adc_reginfo;
+	const struct iio_chan_spec_ext_info *ext_info;
 	int highres;
 	unsigned long max_clock_rate;
 	int (*clk_sel)(struct stm32_adc *adc);
@@ -273,6 +275,7 @@ struct stm32_adc_ops {
  * @id:			ADC instance number (e.g. adc 1, 2 or 3)
  * @offset:		ADC instance register offset in ADC block
  * @max_channels:	Max channels number for this ADC.
+ * @exten:		External trigger config (enable/polarity)
  * @extrig_list:	External trigger list (for regular channel)
  * @completion:		end of single conversion completion
  * @buffer:		data buffer
@@ -295,6 +298,7 @@ struct stm32_adc {
 	int			id;
 	int			offset;
 	int			max_channels;
+	enum stm32_adc_exten	exten;
 	struct list_head	extrig_list;
 	struct completion	completion;
 	u16			*buffer;
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
index 4d7a2a8..e033a68 100644
--- a/drivers/iio/adc/stm32/stm32f4-adc.c
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -555,11 +555,23 @@ static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
 	return 0;
 }
 
+static const struct iio_chan_spec_ext_info stm32f4_adc_ext_info[] = {
+	IIO_ENUM("trigger_pol", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
+	{
+		.name = "trigger_pol_available",
+		.shared = IIO_SHARED_BY_ALL,
+		.read = iio_enum_available_read,
+		.private = (uintptr_t)&stm32_adc_trig_pol,
+	},
+	{},
+};
+
 static const struct stm32_adc_ops stm32f4_adc_ops = {
 	.adc_info = stm32f4_adc_info,
 	.ext_triggers = stm32f4_adc_ext_triggers,
 	.jext_triggers = stm32f4_adc_jext_triggers,
 	.adc_reginfo = &stm32f4_adc_reginfo,
+	.ext_info = stm32f4_adc_ext_info,
 	.highres = 12,
 	.max_clock_rate = 36000000,
 	.clk_sel = stm32f4_adc_clk_sel,
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 06/10] iio: adc: stm32: add ext attrs to configure sampling time
  2016-10-25 16:25 [PATCH 00/10] Add support for STM32 ADC Fabrice Gasnier
                   ` (4 preceding siblings ...)
  2016-10-25 16:25 ` [PATCH 05/10] iio: adc: stm32: add trigger polarity ext attr Fabrice Gasnier
@ 2016-10-25 16:25 ` Fabrice Gasnier
  2016-10-30 14:21   ` Jonathan Cameron
  2016-10-25 16:25 ` [PATCH 07/10] ARM: configs: stm32: enable IIO ADC driver Fabrice Gasnier
                   ` (3 subsequent siblings)
  9 siblings, 1 reply; 20+ messages in thread
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier

Add per channel "smpr" IIO extended attribute, to allow sampling
time configuration.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 drivers/iio/adc/stm32/stm32-adc.c   | 75 +++++++++++++++++++++++++++++++++++++
 drivers/iio/adc/stm32/stm32-adc.h   | 25 +++++++++++++
 drivers/iio/adc/stm32/stm32f4-adc.c | 48 ++++++++++++++++++++++++
 3 files changed, 148 insertions(+)

diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
index 9b4b459..1681f75 100644
--- a/drivers/iio/adc/stm32/stm32-adc.c
+++ b/drivers/iio/adc/stm32/stm32-adc.c
@@ -38,6 +38,61 @@
 #include "stm32-adc.h"
 
 /**
+ * stm32_adc_conf_smp() - Configure sampling time for each channel
+ * @indio_dev: IIO device
+ * @scan_mask: channels to be converted
+ */
+static int stm32_adc_conf_smp(struct iio_dev *indio_dev,
+			      const unsigned long *scan_mask)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	const struct stm32_adc_regs *smp =
+		adc->common->data->adc_reginfo->smpr_regs;
+	struct stm32_adc_chan *stm32_chan;
+	const struct iio_chan_spec *chan;
+	u32 bit, val;
+	int i;
+
+	for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
+		chan = indio_dev->channels + bit;
+		stm32_chan = to_stm32_chan(adc, chan);
+		i = chan->channel;
+
+		if (i >= adc->max_channels)
+			return -EINVAL;
+
+		val = stm32_adc_readl(adc, smp[i].reg);
+		val &= ~smp[i].mask;
+		val |= (stm32_chan->smpr << smp[i].shift) & smp[i].mask;
+		stm32_adc_writel(adc, smp[i].reg, val);
+	}
+
+	return 0;
+}
+
+int stm32_adc_set_smpr(struct iio_dev *indio_dev,
+		       const struct iio_chan_spec *chan, unsigned int smpr)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	struct stm32_adc_chan *stm32_chan = to_stm32_chan(adc, chan);
+
+	stm32_chan->smpr = smpr;
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_set_smpr);
+
+int stm32_adc_get_smpr(struct iio_dev *indio_dev,
+		       const struct iio_chan_spec *chan)
+{
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	struct stm32_adc_chan *stm32_chan = to_stm32_chan(adc, chan);
+
+	return stm32_chan->smpr;
+}
+EXPORT_SYMBOL_GPL(stm32_adc_get_smpr);
+
+/**
  * stm32_adc_conf_scan_seq() - Build regular or injected channels scan sequence
  * @indio_dev: IIO device
  * @scan_mask: channels to be converted
@@ -126,6 +181,12 @@ static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
 		return ret;
 	}
 
+	ret = stm32_adc_conf_smp(indio_dev, scan_mask);
+	if (ret) {
+		dev_err(&indio_dev->dev, "Failed to configure samp time\n");
+		goto err_dis;
+	}
+
 	ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
 	if (ret) {
 		dev_err(&indio_dev->dev, "Failed to configure sequence\n");
@@ -938,6 +999,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
 				  const struct stm32_adc_info *adc_info)
 {
 	struct stm32_adc *adc = iio_priv(indio_dev);
+	struct stm32_adc_common *common = adc->common;
 	struct device_node *node = indio_dev->dev.of_node;
 	struct property *prop;
 	const __be32 *cur;
@@ -945,6 +1007,19 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
 	int scan_index = 0, num_channels = 0;
 	u32 val;
 
+	if (!common->stm32_chans[adc->id]) {
+		/* Allocate extended attributes structure for an instance */
+		struct stm32_adc_chan *stm32_chans;
+
+		stm32_chans = devm_kcalloc(&indio_dev->dev,
+					   adc_info->max_channels,
+					   sizeof(*stm32_chans), GFP_KERNEL);
+		if (!stm32_chans)
+			return -ENOMEM;
+
+		common->stm32_chans[adc->id] = stm32_chans;
+	}
+
 	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
 		num_channels++;
 
diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
index 6c9b70d..8cf1d5c 100644
--- a/drivers/iio/adc/stm32/stm32-adc.h
+++ b/drivers/iio/adc/stm32/stm32-adc.h
@@ -143,6 +143,14 @@ struct stm32_adc_chan_spec {
 };
 
 /**
+ * struct stm32_adc_chan - Extended specifications of stm32 adc channels
+ * @smpr: per channel sampling time selection
+ */
+struct stm32_adc_chan {
+	unsigned smpr:3;
+};
+
+/**
  * struct stm32_adc_trig_info - ADC trigger info
  * @extsel:		trigger selection for regular or injected
  * @name:		name of the trigger, corresponding to its source
@@ -206,6 +214,7 @@ struct stm32_adc_trig_reginfo {
  * @jdr:		injected data registers offsets
  * @sqr_regs:		Regular sequence registers description
  * @jsqr_reg:		Injected sequence register description
+ * @smpr_regs:		Sampling time registers description
  * @trig_reginfo:	regular trigger control registers description
  * @jtrig_reginfo:	injected trigger control registers description
  */
@@ -220,6 +229,7 @@ struct stm32_adc_reginfo {
 	u32 jdr[4];
 	const struct stm32_adc_regs *sqr_regs;
 	const struct stm32_adc_regs *jsqr_reg;
+	const struct stm32_adc_regs *smpr_regs;
 	const struct stm32_adc_trig_reginfo *trig_reginfo;
 	const struct stm32_adc_trig_reginfo *jtrig_reginfo;
 };
@@ -328,6 +338,7 @@ struct stm32_adc {
  * @aclk:		common clock for the analog circuitry
  * @vref:		regulator reference
  * @vref_mv:		vref voltage (mv)
+ * @stm32_chans:	stm32 channels extended specification data
  * @gpio_descs:		gpio descriptor used to configure EXTi triggers
  * @lock:		mutex
  */
@@ -341,11 +352,21 @@ struct stm32_adc_common {
 	struct clk			*aclk;
 	struct regulator		*vref;
 	int				vref_mv;
+	struct stm32_adc_chan		*stm32_chans[STM32_ADC_ID_MAX];
 	struct gpio_descs		*gpios;
 	struct mutex			lock;	/* read_raw lock */
 };
 
 /* Helper routines */
+static inline struct stm32_adc_chan *to_stm32_chan(struct stm32_adc *adc,
+						   const struct iio_chan_spec
+						   *chan)
+{
+	struct stm32_adc_chan *stm32_chans = adc->common->stm32_chans[adc->id];
+
+	return &stm32_chans[chan->channel];
+}
+
 static inline int stm32_adc_start_conv(struct stm32_adc *adc)
 {
 	return adc->common->data->start_conv(adc);
@@ -458,6 +479,10 @@ static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
 
 /* STM32 common extended attributes */
 extern const struct iio_enum stm32_adc_trig_pol;
+int stm32_adc_set_smpr(struct iio_dev *indio_dev,
+		       const struct iio_chan_spec *chan, unsigned int smpr);
+int stm32_adc_get_smpr(struct iio_dev *indio_dev,
+		       const struct iio_chan_spec *chan);
 int stm32_adc_probe(struct platform_device *pdev);
 int stm32_adc_remove(struct platform_device *pdev);
 
diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
index e033a68..e0e6211 100644
--- a/drivers/iio/adc/stm32/stm32f4-adc.c
+++ b/drivers/iio/adc/stm32/stm32f4-adc.c
@@ -330,6 +330,32 @@ enum stm32f4_adc_smpr {
 };
 
 /**
+ * stm32f4_smpr_regs[] - describe sampling time registers & bit fields
+ * Sorted so it can be indexed by channel number.
+ */
+static const struct stm32_adc_regs stm32f4_smpr_regs[] = {
+	{ STM32F4_ADCX_SMPR2, STM32F4_SMP0_MASK, STM32F4_SMP0_SHIFT },
+	{ STM32F4_ADCX_SMPR2, STM32F4_SMP1_MASK, STM32F4_SMP1_SHIFT },
+	{ STM32F4_ADCX_SMPR2, STM32F4_SMP2_MASK, STM32F4_SMP2_SHIFT },
+	{ STM32F4_ADCX_SMPR2, STM32F4_SMP3_MASK, STM32F4_SMP3_SHIFT },
+	{ STM32F4_ADCX_SMPR2, STM32F4_SMP4_MASK, STM32F4_SMP4_SHIFT },
+	{ STM32F4_ADCX_SMPR2, STM32F4_SMP5_MASK, STM32F4_SMP5_SHIFT },
+	{ STM32F4_ADCX_SMPR2, STM32F4_SMP6_MASK, STM32F4_SMP6_SHIFT },
+	{ STM32F4_ADCX_SMPR2, STM32F4_SMP7_MASK, STM32F4_SMP7_SHIFT },
+	{ STM32F4_ADCX_SMPR2, STM32F4_SMP8_MASK, STM32F4_SMP8_SHIFT },
+	{ STM32F4_ADCX_SMPR2, STM32F4_SMP9_MASK, STM32F4_SMP9_SHIFT },
+	{ STM32F4_ADCX_SMPR1, STM32F4_SMP10_MASK, STM32F4_SMP10_SHIFT },
+	{ STM32F4_ADCX_SMPR1, STM32F4_SMP11_MASK, STM32F4_SMP11_SHIFT },
+	{ STM32F4_ADCX_SMPR1, STM32F4_SMP12_MASK, STM32F4_SMP12_SHIFT },
+	{ STM32F4_ADCX_SMPR1, STM32F4_SMP13_MASK, STM32F4_SMP13_SHIFT },
+	{ STM32F4_ADCX_SMPR1, STM32F4_SMP14_MASK, STM32F4_SMP14_SHIFT },
+	{ STM32F4_ADCX_SMPR1, STM32F4_SMP15_MASK, STM32F4_SMP15_SHIFT },
+	{ STM32F4_ADCX_SMPR1, STM32F4_SMP16_MASK, STM32F4_SMP16_SHIFT },
+	{ STM32F4_ADCX_SMPR1, STM32F4_SMP17_MASK, STM32F4_SMP17_SHIFT },
+	{ STM32F4_ADCX_SMPR1, STM32F4_SMP18_MASK, STM32F4_SMP18_SHIFT },
+};
+
+/**
  * stm32f4_sqr_regs - describe regular sequence registers
  * - L: sequence len (register & bit field)
  * - SQ1..SQ16: sequence entries (register & bit field)
@@ -407,6 +433,7 @@ enum stm32f4_adc_smpr {
 	},
 	.sqr_regs = stm32f4_sqr_regs,
 	.jsqr_reg = stm32f4_jsqr_reg,
+	.smpr_regs = stm32f4_smpr_regs,
 	.trig_reginfo = &stm32f4_adc_trig_reginfo,
 	.jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
 };
@@ -555,7 +582,28 @@ static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
 	return 0;
 }
 
+/* stm32f4_smpr_items : Channel-wise programmable sampling time */
+static const char * const stm32f4_smpr_items[] = {
+	[STM32F4_SMPR_3_CK_CYCLES] = "3_cycles",
+	[STM32F4_SMPR_15_CK_CYCLES] = "15_cycles",
+	[STM32F4_SMPR_28_CK_CYCLES] = "28_cycles",
+	[STM32F4_SMPR_56_CK_CYCLES] = "56_cycles",
+	[STM32F4_SMPR_84_CK_CYCLES] = "84_cycles",
+	[STM32F4_SMPR_112_CK_CYCLES] = "112_cycles",
+	[STM32F4_SMPR_144_CK_CYCLES] = "144_cycles",
+	[STM32F4_SMPR_480_CK_CYCLES] = "480_cycles",
+};
+
+static const struct iio_enum stm32f4_smpr = {
+	.items = stm32f4_smpr_items,
+	.num_items = ARRAY_SIZE(stm32f4_smpr_items),
+	.get = stm32_adc_get_smpr,
+	.set = stm32_adc_set_smpr,
+};
+
 static const struct iio_chan_spec_ext_info stm32f4_adc_ext_info[] = {
+	IIO_ENUM("smpr", IIO_SEPARATE, &stm32f4_smpr),
+	IIO_ENUM_AVAILABLE("smpr", &stm32f4_smpr),
 	IIO_ENUM("trigger_pol", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
 	{
 		.name = "trigger_pol_available",
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 07/10] ARM: configs: stm32: enable IIO ADC driver
  2016-10-25 16:25 [PATCH 00/10] Add support for STM32 ADC Fabrice Gasnier
                   ` (5 preceding siblings ...)
  2016-10-25 16:25 ` [PATCH 06/10] iio: adc: stm32: add ext attrs to configure sampling time Fabrice Gasnier
@ 2016-10-25 16:25 ` Fabrice Gasnier
  2016-10-25 16:25 ` [PATCH 08/10] ARM: dts: stm32f429: Add adc support Fabrice Gasnier
                   ` (2 subsequent siblings)
  9 siblings, 0 replies; 20+ messages in thread
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 arch/arm/configs/stm32_defconfig | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig
index 1e5ec2a..5a5f50c 100644
--- a/arch/arm/configs/stm32_defconfig
+++ b/arch/arm/configs/stm32_defconfig
@@ -57,6 +57,8 @@ CONFIG_LEDS_TRIGGERS=y
 CONFIG_LEDS_TRIGGER_HEARTBEAT=y
 CONFIG_DMADEVICES=y
 CONFIG_STM32_DMA=y
+CONFIG_IIO=y
+CONFIG_STM32F4_ADC=y
 # CONFIG_FILE_LOCKING is not set
 # CONFIG_DNOTIFY is not set
 # CONFIG_INOTIFY_USER is not set
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 08/10] ARM: dts: stm32f429: Add adc support
  2016-10-25 16:25 [PATCH 00/10] Add support for STM32 ADC Fabrice Gasnier
                   ` (6 preceding siblings ...)
  2016-10-25 16:25 ` [PATCH 07/10] ARM: configs: stm32: enable IIO ADC driver Fabrice Gasnier
@ 2016-10-25 16:25 ` Fabrice Gasnier
  2016-10-25 16:25 ` [PATCH 09/10] ARM: dts: stm32f429: enable adc on eval board Fabrice Gasnier
  2016-10-25 16:25 ` [PATCH 10/10] ARM: dts: stm32f429: Add dma support to adc Fabrice Gasnier
  9 siblings, 0 replies; 20+ messages in thread
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier

Add adc support & pinctrl analog phandle (adc3_in8) to stm32f429.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 arch/arm/boot/dts/stm32f429.dtsi | 62 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 62 insertions(+)

diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 336ee4f..3fcd941 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -172,6 +172,62 @@
 			status = "disabled";
 		};
 
+		adc: adc@40012000 {
+			compatible = "st,stm32f4-adc";
+			reg = <0x40012000 0x400>;
+			interrupts = <18>;
+			clocks = <&rcc 0 168>;
+			clock-names = "adc";
+			status = "disabled";
+
+			#address-cells = <1>;
+			#size-cells = <0>;
+			adc1: adc1-master@0 {
+				#io-channel-cells = <1>;
+				reg = <0x0>;
+				clocks = <&rcc 0 168>;
+				status = "disabled";
+			};
+
+			jadc1: adc1-injected@0 {
+				#io-channel-cells = <1>;
+				reg = <0x0>;
+				clocks = <&rcc 0 168>;
+				st,injected;
+				status = "disabled";
+			};
+
+			adc2: adc2-slave@100 {
+				#io-channel-cells = <1>;
+				reg = <0x100>;
+				clocks = <&rcc 0 169>;
+				status = "disabled";
+			};
+
+			jadc2: adc2-injected@100 {
+				#io-channel-cells = <1>;
+				reg = <0x100>;
+				clocks = <&rcc 0 169>;
+				st,injected;
+				status = "disabled";
+			};
+
+			adc3: adc3-slave@200 {
+				#io-channel-cells = <1>;
+				reg = <0x200>;
+				clocks = <&rcc 0 170>;
+				status = "disabled";
+			};
+
+			jadc3: adc3-injected@200 {
+				#io-channel-cells = <1>;
+				reg = <0x200>;
+				clocks = <&rcc 0 170>;
+				st,injected;
+				status = "disabled";
+			};
+		};
+
 		syscfg: system-config@40013800 {
 			compatible = "syscon";
 			reg = <0x40013800 0x400>;
@@ -332,6 +388,12 @@
 					slew-rate = <2>;
 				};
 			};
+
+			adc3_in8_pin: adc@0 {
+				pins {
+					pinmux = <STM32F429_PF10_FUNC_ANALOG>;
+				};
+			};
 		};
 
 		rcc: rcc@40023810 {
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 09/10] ARM: dts: stm32f429: enable adc on eval board
  2016-10-25 16:25 [PATCH 00/10] Add support for STM32 ADC Fabrice Gasnier
                   ` (7 preceding siblings ...)
  2016-10-25 16:25 ` [PATCH 08/10] ARM: dts: stm32f429: Add adc support Fabrice Gasnier
@ 2016-10-25 16:25 ` Fabrice Gasnier
  2016-10-25 16:25 ` [PATCH 10/10] ARM: dts: stm32f429: Add dma support to adc Fabrice Gasnier
  9 siblings, 0 replies; 20+ messages in thread
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier

Enable analog to digital converter on stm32f429i-eval board.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 arch/arm/boot/dts/stm32429i-eval.dts | 30 ++++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/arch/arm/boot/dts/stm32429i-eval.dts b/arch/arm/boot/dts/stm32429i-eval.dts
index 6bfc595..0e8c900 100644
--- a/arch/arm/boot/dts/stm32429i-eval.dts
+++ b/arch/arm/boot/dts/stm32429i-eval.dts
@@ -65,6 +65,20 @@
 		serial0 = &usart1;
 	};
 
+	regulators {
+		compatible = "simple-bus";
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		reg_vref: regulator@0 {
+			compatible = "regulator-fixed";
+			reg = <0>;
+			regulator-name = "vref";
+			regulator-min-microvolt = <3300000>;
+			regulator-max-microvolt = <3300000>;
+		};
+	};
+
 	leds {
 		compatible = "gpio-leds";
 		green {
@@ -123,3 +137,19 @@
 	pinctrl-names = "default";
 	status = "okay";
 };
+
+&adc {
+	pinctrl-names = "default";
+	pinctrl-0 = <&adc3_in8_pin>;
+	vref-supply = <&reg_vref>;
+	status = "okay";
+	adc3: adc3-slave@200 {
+		st,adc-channels = <8>;
+		status = "okay";
+	};
+
+	jadc3: adc3-injected@200 {
+		st,adc-channels = <8>;
+		status = "okay";
+	};
+};
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* [PATCH 10/10] ARM: dts: stm32f429: Add dma support to adc
  2016-10-25 16:25 [PATCH 00/10] Add support for STM32 ADC Fabrice Gasnier
                   ` (8 preceding siblings ...)
  2016-10-25 16:25 ` [PATCH 09/10] ARM: dts: stm32f429: enable adc on eval board Fabrice Gasnier
@ 2016-10-25 16:25 ` Fabrice Gasnier
  9 siblings, 0 replies; 20+ messages in thread
From: Fabrice Gasnier @ 2016-10-25 16:25 UTC (permalink / raw)
  To: linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: jic23, linux, robh+dt, mark.rutland, mcoquelin.stm32,
	alexandre.torgue, lars, knaack.h, pmeerw, fabrice.gasnier

Configure STM32 ADC to use dma by default.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 arch/arm/boot/dts/stm32f429.dtsi | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 3fcd941..a00f7a6 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -186,6 +186,8 @@
 				#io-channel-cells = <1>;
 				reg = <0x0>;
 				clocks = <&rcc 0 168>;
+				dmas = <&dma2 0 0 0x400 0x0>;
+				dma-names = "rx";
 				status = "disabled";
 			};
 
@@ -201,6 +203,8 @@
 				#io-channel-cells = <1>;
 				reg = <0x100>;
 				clocks = <&rcc 0 169>;
+				dmas = <&dma2 3 1 0x400 0x0>;
+				dma-names = "rx";
 				status = "disabled";
 			};
 
@@ -216,6 +220,8 @@
 				#io-channel-cells = <1>;
 				reg = <0x200>;
 				clocks = <&rcc 0 170>;
+				dmas = <&dma2 1 2 0x400 0x0>;
+				dma-names = "rx";
 				status = "disabled";
 			};
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 20+ messages in thread

* Re: [PATCH 06/10] iio: adc: stm32: add ext attrs to configure sampling time
  2016-10-25 16:25 ` [PATCH 06/10] iio: adc: stm32: add ext attrs to configure sampling time Fabrice Gasnier
@ 2016-10-30 14:21   ` Jonathan Cameron
  0 siblings, 0 replies; 20+ messages in thread
From: Jonathan Cameron @ 2016-10-30 14:21 UTC (permalink / raw)
  To: Fabrice Gasnier, linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: linux, robh+dt, mark.rutland, mcoquelin.stm32, alexandre.torgue,
	lars, knaack.h, pmeerw

On 25/10/16 17:25, Fabrice Gasnier wrote:
> Add per channel "smpr" IIO extended attribute, to allow sampling
> time configuration.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
First thing is that any attributes need to be documented in
Documentation/ABI/testing/sysfs-bus-iio-*

Secondly this feels rather like it should be possible to use standard ABI for
this. The documentation manages to successfully skip passed the use of
any standard ADC terminology. I'm not actually sure what its
doing when it talks about sampling time... Does this device even have an
inbuilt track and hold?  Presumably but who knows...  Ah, the document
'How to get the best ADC accuracy in STM32FX ... ' gives some details in the
section on how to deal with high impedance sources.  Worth adding a reference
to that for this.

Arguably the 'right' value for these is dependent on the local electrical
properties, so should be described in the device tree rather than tweaked
from userspace.

If you do want to do still want to provide userspace control then it
needs to fit nicely within the IIO ABI.  Now arguably it's 'sort of'
integration time but only via a nasty non linear relationship so lets
not abuse that but rather work up something ADC specific.

As such we would be looking at something like.

in_voltageX_track_time and the units should be seconds (it'll be a pain, but
we need to end up with a generic ABI so it has to be in standard units).
It might make sense to add this to the core info_mask element list and
handle it that way.  I'm still dubious that this isn't really a hardware
question that should never be exposed to userspace in the first place...
You will need to convince me ;)

A few more specific comments inline.
> ---
>  drivers/iio/adc/stm32/stm32-adc.c   | 75 +++++++++++++++++++++++++++++++++++++
>  drivers/iio/adc/stm32/stm32-adc.h   | 25 +++++++++++++
>  drivers/iio/adc/stm32/stm32f4-adc.c | 48 ++++++++++++++++++++++++
>  3 files changed, 148 insertions(+)
> 
> diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
> index 9b4b459..1681f75 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.c
> +++ b/drivers/iio/adc/stm32/stm32-adc.c
> @@ -38,6 +38,61 @@
>  #include "stm32-adc.h"
>  
>  /**
> + * stm32_adc_conf_smp() - Configure sampling time for each channel
> + * @indio_dev: IIO device
> + * @scan_mask: channels to be converted
> + */
> +static int stm32_adc_conf_smp(struct iio_dev *indio_dev,
> +			      const unsigned long *scan_mask)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	const struct stm32_adc_regs *smp =
> +		adc->common->data->adc_reginfo->smpr_regs;
> +	struct stm32_adc_chan *stm32_chan;
> +	const struct iio_chan_spec *chan;
> +	u32 bit, val;
> +	int i;
> +
> +	for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
> +		chan = indio_dev->channels + bit;
> +		stm32_chan = to_stm32_chan(adc, chan);
> +		i = chan->channel;
> +
> +		if (i >= adc->max_channels)
> +			return -EINVAL;
> +
> +		val = stm32_adc_readl(adc, smp[i].reg);
> +		val &= ~smp[i].mask;
> +		val |= (stm32_chan->smpr << smp[i].shift) & smp[i].mask;
> +		stm32_adc_writel(adc, smp[i].reg, val);
> +	}
> +
> +	return 0;
> +}
> +
> +int stm32_adc_set_smpr(struct iio_dev *indio_dev,
> +		       const struct iio_chan_spec *chan, unsigned int smpr)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	struct stm32_adc_chan *stm32_chan = to_stm32_chan(adc, chan);
> +
> +	stm32_chan->smpr = smpr;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(stm32_adc_set_smpr);
> +
> +int stm32_adc_get_smpr(struct iio_dev *indio_dev,
> +		       const struct iio_chan_spec *chan)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	struct stm32_adc_chan *stm32_chan = to_stm32_chan(adc, chan);
> +
> +	return stm32_chan->smpr;
> +}
> +EXPORT_SYMBOL_GPL(stm32_adc_get_smpr);
> +
> +/**
>   * stm32_adc_conf_scan_seq() - Build regular or injected channels scan sequence
>   * @indio_dev: IIO device
>   * @scan_mask: channels to be converted
> @@ -126,6 +181,12 @@ static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
>  		return ret;
>  	}
>  
> +	ret = stm32_adc_conf_smp(indio_dev, scan_mask);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Failed to configure samp time\n");
> +		goto err_dis;
> +	}
> +
>  	ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
>  	if (ret) {
>  		dev_err(&indio_dev->dev, "Failed to configure sequence\n");
> @@ -938,6 +999,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
>  				  const struct stm32_adc_info *adc_info)
>  {
>  	struct stm32_adc *adc = iio_priv(indio_dev);
> +	struct stm32_adc_common *common = adc->common;
>  	struct device_node *node = indio_dev->dev.of_node;
>  	struct property *prop;
>  	const __be32 *cur;
> @@ -945,6 +1007,19 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
>  	int scan_index = 0, num_channels = 0;
>  	u32 val;
>  
> +	if (!common->stm32_chans[adc->id]) {
> +		/* Allocate extended attributes structure for an instance */
> +		struct stm32_adc_chan *stm32_chans;
> +
> +		stm32_chans = devm_kcalloc(&indio_dev->dev,
> +					   adc_info->max_channels,
> +					   sizeof(*stm32_chans), GFP_KERNEL);
> +		if (!stm32_chans)
> +			return -ENOMEM;
> +
> +		common->stm32_chans[adc->id] = stm32_chans;
> +	}
> +
>  	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
>  		num_channels++;
>  
> diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
> index 6c9b70d..8cf1d5c 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.h
> +++ b/drivers/iio/adc/stm32/stm32-adc.h
> @@ -143,6 +143,14 @@ struct stm32_adc_chan_spec {
>  };
>  
>  /**
> + * struct stm32_adc_chan - Extended specifications of stm32 adc channels
> + * @smpr: per channel sampling time selection
> + */
> +struct stm32_adc_chan {
> +	unsigned smpr:3;
> +};
> +
> +/**
>   * struct stm32_adc_trig_info - ADC trigger info
>   * @extsel:		trigger selection for regular or injected
>   * @name:		name of the trigger, corresponding to its source
> @@ -206,6 +214,7 @@ struct stm32_adc_trig_reginfo {
>   * @jdr:		injected data registers offsets
>   * @sqr_regs:		Regular sequence registers description
>   * @jsqr_reg:		Injected sequence register description
> + * @smpr_regs:		Sampling time registers description
>   * @trig_reginfo:	regular trigger control registers description
>   * @jtrig_reginfo:	injected trigger control registers description
>   */
> @@ -220,6 +229,7 @@ struct stm32_adc_reginfo {
>  	u32 jdr[4];
>  	const struct stm32_adc_regs *sqr_regs;
>  	const struct stm32_adc_regs *jsqr_reg;
> +	const struct stm32_adc_regs *smpr_regs;
>  	const struct stm32_adc_trig_reginfo *trig_reginfo;
>  	const struct stm32_adc_trig_reginfo *jtrig_reginfo;
>  };
> @@ -328,6 +338,7 @@ struct stm32_adc {
>   * @aclk:		common clock for the analog circuitry
>   * @vref:		regulator reference
>   * @vref_mv:		vref voltage (mv)
> + * @stm32_chans:	stm32 channels extended specification data
>   * @gpio_descs:		gpio descriptor used to configure EXTi triggers
>   * @lock:		mutex
>   */
> @@ -341,11 +352,21 @@ struct stm32_adc_common {
>  	struct clk			*aclk;
>  	struct regulator		*vref;
>  	int				vref_mv;
> +	struct stm32_adc_chan		*stm32_chans[STM32_ADC_ID_MAX];
>  	struct gpio_descs		*gpios;
>  	struct mutex			lock;	/* read_raw lock */
>  };
>  
>  /* Helper routines */
> +static inline struct stm32_adc_chan *to_stm32_chan(struct stm32_adc *adc,
> +						   const struct iio_chan_spec
> +						   *chan)
> +{
> +	struct stm32_adc_chan *stm32_chans = adc->common->stm32_chans[adc->id];
> +
> +	return &stm32_chans[chan->channel];
> +}
> +
>  static inline int stm32_adc_start_conv(struct stm32_adc *adc)
>  {
>  	return adc->common->data->start_conv(adc);
> @@ -458,6 +479,10 @@ static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
>  
>  /* STM32 common extended attributes */
>  extern const struct iio_enum stm32_adc_trig_pol;
> +int stm32_adc_set_smpr(struct iio_dev *indio_dev,
> +		       const struct iio_chan_spec *chan, unsigned int smpr);
> +int stm32_adc_get_smpr(struct iio_dev *indio_dev,
> +		       const struct iio_chan_spec *chan);
>  int stm32_adc_probe(struct platform_device *pdev);
>  int stm32_adc_remove(struct platform_device *pdev);
>  
> diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
> index e033a68..e0e6211 100644
> --- a/drivers/iio/adc/stm32/stm32f4-adc.c
> +++ b/drivers/iio/adc/stm32/stm32f4-adc.c
> @@ -330,6 +330,32 @@ enum stm32f4_adc_smpr {
>  };
>  
>  /**
> + * stm32f4_smpr_regs[] - describe sampling time registers & bit fields
> + * Sorted so it can be indexed by channel number.
> + */
> +static const struct stm32_adc_regs stm32f4_smpr_regs[] = {
> +	{ STM32F4_ADCX_SMPR2, STM32F4_SMP0_MASK, STM32F4_SMP0_SHIFT },
> +	{ STM32F4_ADCX_SMPR2, STM32F4_SMP1_MASK, STM32F4_SMP1_SHIFT },
> +	{ STM32F4_ADCX_SMPR2, STM32F4_SMP2_MASK, STM32F4_SMP2_SHIFT },
> +	{ STM32F4_ADCX_SMPR2, STM32F4_SMP3_MASK, STM32F4_SMP3_SHIFT },
> +	{ STM32F4_ADCX_SMPR2, STM32F4_SMP4_MASK, STM32F4_SMP4_SHIFT },
> +	{ STM32F4_ADCX_SMPR2, STM32F4_SMP5_MASK, STM32F4_SMP5_SHIFT },
> +	{ STM32F4_ADCX_SMPR2, STM32F4_SMP6_MASK, STM32F4_SMP6_SHIFT },
> +	{ STM32F4_ADCX_SMPR2, STM32F4_SMP7_MASK, STM32F4_SMP7_SHIFT },
> +	{ STM32F4_ADCX_SMPR2, STM32F4_SMP8_MASK, STM32F4_SMP8_SHIFT },
> +	{ STM32F4_ADCX_SMPR2, STM32F4_SMP9_MASK, STM32F4_SMP9_SHIFT },
> +	{ STM32F4_ADCX_SMPR1, STM32F4_SMP10_MASK, STM32F4_SMP10_SHIFT },
> +	{ STM32F4_ADCX_SMPR1, STM32F4_SMP11_MASK, STM32F4_SMP11_SHIFT },
> +	{ STM32F4_ADCX_SMPR1, STM32F4_SMP12_MASK, STM32F4_SMP12_SHIFT },
> +	{ STM32F4_ADCX_SMPR1, STM32F4_SMP13_MASK, STM32F4_SMP13_SHIFT },
> +	{ STM32F4_ADCX_SMPR1, STM32F4_SMP14_MASK, STM32F4_SMP14_SHIFT },
> +	{ STM32F4_ADCX_SMPR1, STM32F4_SMP15_MASK, STM32F4_SMP15_SHIFT },
> +	{ STM32F4_ADCX_SMPR1, STM32F4_SMP16_MASK, STM32F4_SMP16_SHIFT },
> +	{ STM32F4_ADCX_SMPR1, STM32F4_SMP17_MASK, STM32F4_SMP17_SHIFT },
> +	{ STM32F4_ADCX_SMPR1, STM32F4_SMP18_MASK, STM32F4_SMP18_SHIFT },
> +};
> +
> +/**
>   * stm32f4_sqr_regs - describe regular sequence registers
>   * - L: sequence len (register & bit field)
>   * - SQ1..SQ16: sequence entries (register & bit field)
> @@ -407,6 +433,7 @@ enum stm32f4_adc_smpr {
>  	},
>  	.sqr_regs = stm32f4_sqr_regs,
>  	.jsqr_reg = stm32f4_jsqr_reg,
> +	.smpr_regs = stm32f4_smpr_regs,
>  	.trig_reginfo = &stm32f4_adc_trig_reginfo,
>  	.jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
>  };
> @@ -555,7 +582,28 @@ static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
>  	return 0;
>  }
>  
> +/* stm32f4_smpr_items : Channel-wise programmable sampling time */
> +static const char * const stm32f4_smpr_items[] = {
> +	[STM32F4_SMPR_3_CK_CYCLES] = "3_cycles",
> +	[STM32F4_SMPR_15_CK_CYCLES] = "15_cycles",
> +	[STM32F4_SMPR_28_CK_CYCLES] = "28_cycles",
> +	[STM32F4_SMPR_56_CK_CYCLES] = "56_cycles",
> +	[STM32F4_SMPR_84_CK_CYCLES] = "84_cycles",
> +	[STM32F4_SMPR_112_CK_CYCLES] = "112_cycles",
> +	[STM32F4_SMPR_144_CK_CYCLES] = "144_cycles",
> +	[STM32F4_SMPR_480_CK_CYCLES] = "480_cycles",
This is a whole level of nasty magic numbers.  Always take real numeric
values and either provide an _available attribute listing the options,
or if appropriate round to the next best (probably up here).

Units shouldn't be in cycles as that has no meaning without a deep and
dirty knowledge of what clocks are being fed to the device.  Stuff that
is at least tricky for userspace to work out.  THis is a time, so it
should be in seconds.


> +};
> +
> +static const struct iio_enum stm32f4_smpr = {
> +	.items = stm32f4_smpr_items,
> +	.num_items = ARRAY_SIZE(stm32f4_smpr_items),
> +	.get = stm32_adc_get_smpr,
> +	.set = stm32_adc_set_smpr,
> +};
> +
>  static const struct iio_chan_spec_ext_info stm32f4_adc_ext_info[] = {
> +	IIO_ENUM("smpr", IIO_SEPARATE, &stm32f4_smpr),
> +	IIO_ENUM_AVAILABLE("smpr", &stm32f4_smpr),
>  	IIO_ENUM("trigger_pol", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
>  	{
>  		.name = "trigger_pol_available",
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 05/10] iio: adc: stm32: add trigger polarity ext attr
  2016-10-25 16:25 ` [PATCH 05/10] iio: adc: stm32: add trigger polarity ext attr Fabrice Gasnier
@ 2016-10-30 14:33   ` Jonathan Cameron
  0 siblings, 0 replies; 20+ messages in thread
From: Jonathan Cameron @ 2016-10-30 14:33 UTC (permalink / raw)
  To: Fabrice Gasnier, linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: linux, robh+dt, mark.rutland, mcoquelin.stm32, alexandre.torgue,
	lars, knaack.h, pmeerw

On 25/10/16 17:25, Fabrice Gasnier wrote:
> Add trigger polarity extended attribute.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
ABI always needs documenting.  Add docs also makes it easy to review
without diving into the code.

This will also need a convincing argument for why it doesn't belong in the
device tree.

J
> ---
>  drivers/iio/adc/stm32/stm32-adc.c   | 41 ++++++++++++++++++++++++++++++++++++-
>  drivers/iio/adc/stm32/stm32-adc.h   |  4 ++++
>  drivers/iio/adc/stm32/stm32f4-adc.c | 12 +++++++++++
>  3 files changed, 56 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
> index 1a13450..9b4b459 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.c
> +++ b/drivers/iio/adc/stm32/stm32-adc.c
> @@ -207,7 +207,11 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
>  
>  		/* trigger source */
>  		extsel = trig_info[ret].extsel;
> -		exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
> +
> +		/* default to rising edge if no polarity */
> +		if (adc->exten == STM32_EXTEN_SWTRIG)
> +			adc->exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
> +		exten = adc->exten;
>  	}
>  
>  	spin_lock_irqsave(&adc->lock, flags);
> @@ -221,6 +225,40 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
>  	return 0;
>  }
>  
> +static int stm32_adc_set_trig_pol(struct iio_dev *indio_dev,
> +				  const struct iio_chan_spec *chan,
> +				  unsigned int type)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +
> +	adc->exten = type;
> +
> +	return 0;
> +}
> +
> +static int stm32_adc_get_trig_pol(struct iio_dev *indio_dev,
> +				  const struct iio_chan_spec *chan)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +
> +	return adc->exten;
> +}
> +
> +static const char * const stm32_trig_pol_items[] = {
> +	[STM32_EXTEN_SWTRIG] = "swtrig",
> +	[STM32_EXTEN_HWTRIG_RISING_EDGE] = "rising-edge",
> +	[STM32_EXTEN_HWTRIG_FALLING_EDGE] = "falling-edge",
> +	[STM32_EXTEN_HWTRIG_BOTH_EDGES] = "both-edges",
> +};
> +
> +const struct iio_enum stm32_adc_trig_pol = {
> +	.items = stm32_trig_pol_items,
> +	.num_items = ARRAY_SIZE(stm32_trig_pol_items),
> +	.get = stm32_adc_get_trig_pol,
> +	.set = stm32_adc_set_trig_pol,
> +};
> +EXPORT_SYMBOL_GPL(stm32_adc_trig_pol);
> +
>  /**
>   * stm32_adc_conv_irq_enable() - Unmask end of conversion irq
>   * @adc: stm32 adc instance
> @@ -893,6 +931,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
>  	chan->scan_type.realbits = adc->common->data->highres;
>  	chan->scan_type.storagebits = STM32_STORAGEBITS;
>  	chan->scan_type.shift = 0;
> +	chan->ext_info = adc->common->data->ext_info;
>  }
>  
>  static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
> diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
> index fe3568b..6c9b70d 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.h
> +++ b/drivers/iio/adc/stm32/stm32-adc.h
> @@ -234,6 +234,7 @@ struct stm32_adc_reginfo {
>   * @ext_triggers:	Reference to trigger info for regular channels
>   * @jext_triggers:	Reference to trigger info for injected channels
>   * @adc_reginfo:	stm32 ADC registers description
> + * @ext_info:		Extended channel info
>   * @highres:		Max resolution
>   * @max_clock_rate:	Max input clock rate
>   * @clk_sel:		routine to select common clock and prescaler
> @@ -251,6 +252,7 @@ struct stm32_adc_ops {
>  	const struct stm32_adc_trig_info *ext_triggers;
>  	const struct stm32_adc_trig_info *jext_triggers;
>  	const struct stm32_adc_reginfo *adc_reginfo;
> +	const struct iio_chan_spec_ext_info *ext_info;
>  	int highres;
>  	unsigned long max_clock_rate;
>  	int (*clk_sel)(struct stm32_adc *adc);
> @@ -273,6 +275,7 @@ struct stm32_adc_ops {
>   * @id:			ADC instance number (e.g. adc 1, 2 or 3)
>   * @offset:		ADC instance register offset in ADC block
>   * @max_channels:	Max channels number for this ADC.
> + * @exten:		External trigger config (enable/polarity)
>   * @extrig_list:	External trigger list (for regular channel)
>   * @completion:		end of single conversion completion
>   * @buffer:		data buffer
> @@ -295,6 +298,7 @@ struct stm32_adc {
>  	int			id;
>  	int			offset;
>  	int			max_channels;
> +	enum stm32_adc_exten	exten;
>  	struct list_head	extrig_list;
>  	struct completion	completion;
>  	u16			*buffer;
> diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
> index 4d7a2a8..e033a68 100644
> --- a/drivers/iio/adc/stm32/stm32f4-adc.c
> +++ b/drivers/iio/adc/stm32/stm32f4-adc.c
> @@ -555,11 +555,23 @@ static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
>  	return 0;
>  }
>  
> +static const struct iio_chan_spec_ext_info stm32f4_adc_ext_info[] = {
> +	IIO_ENUM("trigger_pol", IIO_SHARED_BY_ALL, &stm32_adc_trig_pol),
> +	{
> +		.name = "trigger_pol_available",
> +		.shared = IIO_SHARED_BY_ALL,
> +		.read = iio_enum_available_read,
> +		.private = (uintptr_t)&stm32_adc_trig_pol,
> +	},
> +	{},
> +};
> +
>  static const struct stm32_adc_ops stm32f4_adc_ops = {
>  	.adc_info = stm32f4_adc_info,
>  	.ext_triggers = stm32f4_adc_ext_triggers,
>  	.jext_triggers = stm32f4_adc_jext_triggers,
>  	.adc_reginfo = &stm32f4_adc_reginfo,
> +	.ext_info = stm32f4_adc_ext_info,
>  	.highres = 12,
>  	.max_clock_rate = 36000000,
>  	.clk_sel = stm32f4_adc_clk_sel,
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 04/10] iio: adc: stm32: add optional support for exti gpios
  2016-10-25 16:25 ` [PATCH 04/10] iio: adc: stm32: add optional support for exti gpios Fabrice Gasnier
@ 2016-10-30 14:35   ` Jonathan Cameron
  0 siblings, 0 replies; 20+ messages in thread
From: Jonathan Cameron @ 2016-10-30 14:35 UTC (permalink / raw)
  To: Fabrice Gasnier, linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: linux, robh+dt, mark.rutland, mcoquelin.stm32, alexandre.torgue,
	lars, knaack.h, pmeerw

On 25/10/16 17:25, Fabrice Gasnier wrote:
> STM32 ADC may use EXTi signals routed internally as trigger source
> for conversions. Configure them as interrupt to configure this path
> in HW to adc.
> Note: interrupt handler isn't required here, and corresponding interrupt
> can be kept masked at exti controller level.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> ---
>  drivers/iio/adc/stm32/stm32-adc.c | 45 +++++++++++++++++++++++++++++++++++++++
>  drivers/iio/adc/stm32/stm32-adc.h |  3 +++
>  2 files changed, 48 insertions(+)
> 
> diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
> index 25d0307..1a13450 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.c
> +++ b/drivers/iio/adc/stm32/stm32-adc.c
> @@ -482,6 +482,12 @@ static irqreturn_t stm32_adc_common_isr(int irq, void *data)
>  	return ret;
>  }
>  
> +static irqreturn_t stm32_adc_exti_handler(int irq, void *data)
> +{
> +	/* Exti handler should not be invoqued, and is not used */
invoked.
> +	return IRQ_HANDLED;
> +}
> +
>  /**
>   * stm32_adc_validate_trigger() - validate trigger for stm32 adc
>   * @indio_dev: IIO device
> @@ -1051,6 +1057,41 @@ static void stm32_adc_unregister(struct stm32_adc *adc)
>  	}
>  }
>  
> +static int stm32_adc_exti_probe(struct stm32_adc_common *common)
> +{
> +	int i, irq, ret;
> +
> +	common->gpios = devm_gpiod_get_array_optional(common->dev, NULL,
> +						      GPIOD_IN);
> +	if (!common->gpios)
> +		return 0;
> +
> +	for (i = 0; i < common->gpios->ndescs; i++) {
> +		irq = gpiod_to_irq(common->gpios->desc[i]);
> +		if (irq < 0) {
> +			dev_err(common->dev, "gpio %d to irq failed\n", i);
> +			return irq;
> +		}
> +
> +		ret = devm_request_irq(common->dev, irq, stm32_adc_exti_handler,
> +				       0, dev_name(common->dev), common);
> +		if (ret) {
> +			dev_err(common->dev, "request IRQ %d failed\n", irq);
> +			return ret;
> +		}
> +
> +		/*
> +		 * gpios are configured as interrupts, so exti trigger path is
> +		 * configured in HW. But getting interrupts when starting
> +		 * conversions is unused here, so mask it in on exti
> +		 * controller by default.
> +		 */
> +		disable_irq(irq);
> +	}
> +
> +	return 0;
> +}
> +
>  int stm32_adc_probe(struct platform_device *pdev)
>  {
>  	struct device_node *np = pdev->dev.of_node, *child;
> @@ -1131,6 +1172,10 @@ int stm32_adc_probe(struct platform_device *pdev)
>  		goto err_clk_disable;
>  	}
>  
> +	ret = stm32_adc_exti_probe(common);
> +	if (ret)
> +		goto err_clk_disable;
> +
>  	/* Parse adc child nodes to retrieve master/slave instances data */
>  	for_each_available_child_of_node(np, child) {
>  		ret = stm32_adc_register(common, child);
> diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
> index 01a0dda..fe3568b 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.h
> +++ b/drivers/iio/adc/stm32/stm32-adc.h
> @@ -23,6 +23,7 @@
>  #define __STM32_ADC_H
>  
>  #include <linux/dmaengine.h>
> +#include <linux/gpio/consumer.h>
>  #include <linux/irq_work.h>
>  
>  /*
> @@ -323,6 +324,7 @@ struct stm32_adc {
>   * @aclk:		common clock for the analog circuitry
>   * @vref:		regulator reference
>   * @vref_mv:		vref voltage (mv)
> + * @gpio_descs:		gpio descriptor used to configure EXTi triggers
>   * @lock:		mutex
>   */
>  struct stm32_adc_common {
> @@ -335,6 +337,7 @@ struct stm32_adc_common {
>  	struct clk			*aclk;
>  	struct regulator		*vref;
>  	int				vref_mv;
> +	struct gpio_descs		*gpios;
>  	struct mutex			lock;	/* read_raw lock */
>  };
>  
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 02/10] iio: adc: Add stm32 support
  2016-10-25 16:25 ` [PATCH 02/10] iio: adc: Add stm32 support Fabrice Gasnier
@ 2016-10-30 15:27   ` Jonathan Cameron
  2016-11-03  8:20     ` Fabrice Gasnier
  0 siblings, 1 reply; 20+ messages in thread
From: Jonathan Cameron @ 2016-10-30 15:27 UTC (permalink / raw)
  To: Fabrice Gasnier, linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: linux, robh+dt, mark.rutland, mcoquelin.stm32, alexandre.torgue,
	lars, knaack.h, pmeerw

On 25/10/16 17:25, Fabrice Gasnier wrote:
> This patch adds support for STMicroelectronics STM32 MCU's analog to
> digital converter.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Hi Fabrice,

Sometimes I hate SoC ADCs.  For some reason the hardware designers seem to
try and throw everything and the kitchen sink at them.  Discontinuous mode
as an example in this device.  Not seen that particular piece of fun before
and glad to see you haven't 'yet' tried to support it!

Anyhow, the complexity of the hardware leads to an initially complex driver.
My first thought it that this would be easier to follow / review if we
built it up in smaller steps.   Perhaps ditch the injected channel support
entirely in the first instance.  I also wonder if you don't need to support
that whole thing (injected sampling) as another iio device entirely using the
same channels.  That's kind of what it is from a data flow point of view
(we've had arbitary sequencers before with priorities - don't think anyone
ever decided the pain was worth supporting the complexity, but right answer
has always been multiple IIO devices). 

You also have at least one layer of abstraction in here that serves no
current purpose.  Please clear that out for now. It'll make the code
shorter and easier to follow.  If/when other parts are introduced then
is the time to do that transistion to having the abstraction.

My first thought on the double / tripple adc handling is that you'd be better
off handling them as 3 separate devices then doing some 'unusual' trigger
handling to support the weird sequencing.  Guessing you thought about that?
If so could you lay out your reasoning for the single driver instance approach.
I'm not arguing against it btw, merely want to understand your reasoning!

It would be tricky given one set of channels are selectable over 3 devices
and there are constraints to enforce (not sampling same channel on two ADCs
at the same time) but not impossible...  Perhaps what you have here is
indeed simpler!

Whilst it's been a nasty job to review, I'm guessing writing it was
much worse ;)  Pretty good starting point though might take a little while
to pin down the remaining questions on how best to handle this particular
monster.

Jonathan
> ---
>  drivers/iio/adc/Kconfig             |   2 +
>  drivers/iio/adc/Makefile            |   1 +
>  drivers/iio/adc/stm32/Kconfig       |  34 ++
>  drivers/iio/adc/stm32/Makefile      |   4 +
>  drivers/iio/adc/stm32/stm32-adc.c   | 999 ++++++++++++++++++++++++++++++++++++
>  drivers/iio/adc/stm32/stm32-adc.h   | 442 ++++++++++++++++
>  drivers/iio/adc/stm32/stm32f4-adc.c | 574 +++++++++++++++++++++
>  7 files changed, 2056 insertions(+)
>  create mode 100644 drivers/iio/adc/stm32/Kconfig
>  create mode 100644 drivers/iio/adc/stm32/Makefile
>  create mode 100644 drivers/iio/adc/stm32/stm32-adc.c
>  create mode 100644 drivers/iio/adc/stm32/stm32-adc.h
>  create mode 100644 drivers/iio/adc/stm32/stm32f4-adc.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 7edcf32..5c96a55 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -583,4 +583,6 @@ config XILINX_XADC
>  	  The driver can also be build as a module. If so, the module will be called
>  	  xilinx-xadc.
>  
> +source "drivers/iio/adc/stm32/Kconfig"
> +
>  endmenu
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 7a40c04..a9dbf3a 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -13,6 +13,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
>  obj-$(CONFIG_AD7793) += ad7793.o
>  obj-$(CONFIG_AD7887) += ad7887.o
>  obj-$(CONFIG_AD799X) += ad799x.o
> +obj-$(CONFIG_ARCH_STM32) += stm32/
>  obj-$(CONFIG_AT91_ADC) += at91_adc.o
>  obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
>  obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
> diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
> new file mode 100644
> index 0000000..245d037
> --- /dev/null
> +++ b/drivers/iio/adc/stm32/Kconfig
> @@ -0,0 +1,34 @@
> +#
> +# STM32 familly ADC drivers
> +#
> +
> +config STM32_ADC
> +	tristate
> +	select REGULATOR
> +	select REGULATOR_FIXED_VOLTAGE
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	  Say yes here to build the driver for the STMicroelectronics
> +	  STM32 analog-to-digital converter (ADC).
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called stm32-adc.
> +
> +config STM32F4_ADC
> +	tristate "STMicroelectronics STM32F4 adc"
> +	depends on ARCH_STM32 || COMPILE_TEST
> +	depends on OF
> +	select STM32_ADC
> +	help
> +	  Say yes here to build support for STMicroelectronics stm32f4 Analog
> +	  to Digital Converter (ADC).
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called stm32f4-adc.
> +
> +config STM32_ADC_DEBUG
> +	bool "Enable debug for stm32 ADC drivers"
> +	depends on STM32_ADC
> +	help
> +	  Say "yes" to enable debug messages, on stm32 ADC drivers.
> diff --git a/drivers/iio/adc/stm32/Makefile b/drivers/iio/adc/stm32/Makefile
> new file mode 100644
> index 0000000..83e8154
> --- /dev/null
> +++ b/drivers/iio/adc/stm32/Makefile
> @@ -0,0 +1,4 @@
> +# Core
> +subdir-ccflags-$(CONFIG_STM32_ADC_DEBUG) := -DDEBUG
> +obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> +obj-$(CONFIG_STM32F4_ADC) += stm32f4-adc.o
> diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
> new file mode 100644
> index 0000000..1e0850d
> --- /dev/null
> +++ b/drivers/iio/adc/stm32/stm32-adc.c
> @@ -0,0 +1,999 @@
> +/*
> + * This file is part of STM32 ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Fabrice Gasnier <fabrice.gasnier@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/clk.h>
> +#include <linux/debugfs.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/events.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/slab.h>
> +#include "stm32-adc.h"
> +
> +/**
> + * stm32_adc_conf_scan_seq() - Build regular or injected channels scan sequence
> + * @indio_dev: IIO device
> + * @scan_mask: channels to be converted
> + *
> + * Conversion sequence :
> + * Configure ADC scan sequence based on selected channels in scan_mask.
> + * Add channels to SQR or JSQR registers, from scan_mask LSB to MSB, then
> + * program sequence len.
> + *
> + * Note: This can be done only when ADC enabled and no conversion is ongoing.
> + */
> +static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
> +				   const unsigned long *scan_mask)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	const struct stm32_adc_regs *sq;
> +	const struct iio_chan_spec *chan;
> +	u32 val, bit;
> +	int sq_max, i = 0;
> +
> +	if (!stm32_adc_is_enabled(adc))
> +		return -EBUSY;
> +
> +	if (adc->injected) {
> +		if (stm32_adc_injected_started(adc))
> +			return -EBUSY;
> +
> +		sq = adc->common->data->adc_reginfo->jsqr_reg;
> +		sq_max = STM32_ADC_MAX_JSQ;
> +	} else {
> +		if (stm32_adc_regular_started(adc))
> +			return -EBUSY;
> +
> +		sq = adc->common->data->adc_reginfo->sqr_regs;
> +		sq_max = STM32_ADC_MAX_SQ;
> +	}
> +
> +	/* Build sequence for regular or injected channels */
> +	for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
> +		chan = indio_dev->channels + bit;
> +		/*
> +		 * Assign one channel per SQ/JSQ entry in regular/injected
> +		 * sequence, starting with SQ1/JSQ1.
> +		 */
> +		i++;
> +		if (i > sq_max)
> +			return -EINVAL;
> +
> +		dev_dbg(adc->common->dev, "%s chan %d to %s%d\n",
> +			__func__, chan->channel, adc->injected ? "JSQ" : "SQ",
> +			i);
> +
> +		val = stm32_adc_readl(adc, sq[i].reg);
> +		val &= ~sq[i].mask;
> +		val |= (chan->channel << sq[i].shift) & sq[i].mask;
> +		stm32_adc_writel(adc, sq[i].reg, val);
> +	}
> +
> +	if (!i)
> +		return -EINVAL;
> +
> +	/* Sequence len */
> +	val = stm32_adc_readl(adc, sq[0].reg);
> +	val &= ~sq[0].mask;
> +	val |= ((i - 1) << sq[0].shift) & sq[0].mask;
> +	stm32_adc_writel(adc, sq[0].reg, val);
> +
> +	return 0;
> +}
> +
> +static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
> +			       const unsigned long *scan_mask)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	int ret;
> +
> +	ret = stm32_adc_clk_sel(adc);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Clock sel failed\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_adc_enable(adc);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Failed to enable adc\n");
> +		return ret;
> +	}
> +
> +	ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Failed to configure sequence\n");
> +		goto err_dis;
> +	}
It's horrible but to end up in the 'obvious' state I'd disable the adc
again assuming that doesn't kill the stuff that is configured.
> +
> +	return 0;
> +
> +err_dis:
> +	stm32_adc_disable(adc);
> +
> +	return ret;
> +}
> +
> +/**
> + * stm32_adc_get_trig_index() - Get trigger index
> + * @indio_dev: IIO device
> + * @trig: trigger
> + *
> + * Returns trigger index, if trig matches one of the triggers registered by
> + * stm32 adc driver, -EINVAL otherwise.
> + */
> +static int stm32_adc_get_trig_index(struct iio_dev *indio_dev,
> +				    struct iio_trigger *trig)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	struct iio_trigger *tr;
> +	int i = 0;
> +
> +	list_for_each_entry(tr, &adc->extrig_list, alloc_list) {
> +		if (tr == trig)
> +			return i;
> +		i++;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +/**
> + * stm32_adc_set_trig() - Set a regular or injected trigger
> + * @indio_dev: IIO device
> + * @trig: IIO trigger
> + *
> + * Set trigger source/polarity (e.g. SW, or HW with polarity) :
> + * - if HW trigger disabled (e.g. trig == NULL, conversion launched by sw)
> + * - if HW trigger enabled, set source & polarity (trigger_pol / jtrigger_pol)
> + *
> + * Note: must be called when ADC is enabled
> + */
> +static int stm32_adc_set_trig(struct iio_dev *indio_dev,
> +			      struct iio_trigger *trig)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	const struct stm32_adc_trig_info *trig_info;
> +	const struct stm32_adc_trig_reginfo *reginfo;
> +	u32 val, extsel = 0, exten = STM32_EXTEN_SWTRIG;
> +	unsigned long flags;
> +	int ret;
> +
> +	if (!stm32_adc_is_enabled(adc))
> +		return -EBUSY;
> +
> +	if (adc->injected) {
> +		if (stm32_adc_injected_started(adc))
> +			return -EBUSY;
> +		reginfo = adc->common->data->adc_reginfo->jtrig_reginfo;
> +		trig_info = adc->common->data->jext_triggers;
> +	} else {
> +		if (stm32_adc_regular_started(adc))
> +			return -EBUSY;
> +		reginfo = adc->common->data->adc_reginfo->trig_reginfo;
> +		trig_info = adc->common->data->ext_triggers;
> +	}
> +
> +	if (trig) {
> +		ret = stm32_adc_get_trig_index(indio_dev, trig);
> +		if (ret < 0)
> +			return ret;
> +
> +		/* trigger source */
> +		extsel = trig_info[ret].extsel;
> +		exten = STM32_EXTEN_HWTRIG_RISING_EDGE;
> +	}
> +
> +	spin_lock_irqsave(&adc->lock, flags);
> +	val = stm32_adc_readl(adc, reginfo->reg);
> +	val &= ~(reginfo->exten_mask | reginfo->extsel_mask);
> +	val |= (exten << reginfo->exten_shift) & reginfo->exten_mask;
> +	val |= (extsel << reginfo->extsel_shift) & reginfo->extsel_mask;
> +	stm32_adc_writel(adc, reginfo->reg, val);
> +	spin_unlock_irqrestore(&adc->lock, flags);
> +
> +	return 0;
> +}
> +
> +/**
> + * stm32_adc_conv_irq_enable() - Unmask end of conversion irq
> + * @adc: stm32 adc instance
> + *
> + * Unmask either eoc or jeoc, depending on injected configuration.
> + */
> +static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
> +{
> +	const struct stm32_adc_reginfo *reginfo =
> +		adc->common->data->adc_reginfo;
> +	u32 mask;
> +
> +	if (adc->injected)
> +		mask = reginfo->jeocie;
> +	else
> +		mask = reginfo->eocie;
> +
> +	stm32_adc_set_bits(adc, reginfo->ier, mask);
> +}
> +
> +/**
> + * stm32_adc_conv_irq_disable() - Mask end of conversion irq
> + * @adc: stm32 adc instance
> + *
> + * Mask either eoc or jeoc, depending on injected configuration.
> + */
> +static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
> +{
> +	const struct stm32_adc_reginfo *reginfo =
> +		adc->common->data->adc_reginfo;
> +	u32 mask;
> +
> +	if (adc->injected)
> +		mask = reginfo->jeocie;
> +	else
> +		mask = reginfo->eocie;
> +
> +	stm32_adc_clr_bits(adc, reginfo->ier, mask);
> +}
> +
> +/**
> + * stm32_adc_single_conv() - perform a single conversion
> + * @indio_dev: IIO device
> + * @chan: IIO channel
> + * @result: conversion result
> + *
> + * The function performs a single conversion on a given channel, by
> + * by:
> + * - creating scan mask with only one channel
> + * - using SW trigger
> + * - then start single conv
> + */
> +static int stm32_adc_single_conv(struct iio_dev *indio_dev,
> +				 const struct iio_chan_spec *chan,
> +				 int *val)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	unsigned long *scan_mask;
> +	long timeout;
> +	u16 result;
> +	int ret;
> +
> +	scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
> +			    GFP_KERNEL);
This is known maximum length... I'd just avoid the complexity of allocating
it like this - a comment would do the job to say it is the right length.
> +	if (!scan_mask)
> +		return -ENOMEM;
> +
> +	set_bit(chan->scan_index, scan_mask);
> +
> +	reinit_completion(&adc->completion);
> +
> +	adc->bufi = 0;
> +	adc->num_conv = 1;
> +	adc->buffer = &result;
> +
> +	ret = stm32_adc_conf_scan(indio_dev, scan_mask);
> +	if (ret)
> +		goto free;
> +
> +	/* No HW trigger: conversion can be launched in SW */
> +	ret = stm32_adc_set_trig(indio_dev, NULL);
Put it back again afterwards?  Otherwise some nasty race conditions look
likely to me.. (userspace sets trigger and is about to enable the buffer
when along comes this code and changes it underneath).
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Can't set SW trigger\n");
> +		goto adc_disable;
> +	}
> +
> +	stm32_adc_conv_irq_enable(adc);
> +
> +	ret = stm32_adc_start_conv(adc);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "Failed to start single conv\n");
> +		goto irq_disable;
> +	}
> +
> +	timeout = wait_for_completion_interruptible_timeout(
> +					&adc->completion, STM32_ADC_TIMEOUT);
> +	if (timeout == 0) {
> +		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
> +		ret = -ETIMEDOUT;
> +	} else if (timeout < 0) {
> +		dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
> +		ret = -EINTR;
> +	} else {
> +		*val = result & STM32_RESULT_MASK;
> +		ret = IIO_VAL_INT;
> +	}
> +
> +	if (stm32_adc_stop_conv(adc))
> +		dev_err(&indio_dev->dev, "stop failed\n");
> +
> +irq_disable:
> +	stm32_adc_conv_irq_disable(adc);
> +
> +adc_disable:
> +	stm32_adc_disable(adc);
> +
> +free:
> +	kfree(scan_mask);
> +	adc->buffer = NULL;
> +
> +	return ret;
> +}
> +
> +static int stm32_adc_read_raw(struct iio_dev *indio_dev,
> +			      struct iio_chan_spec const *chan,
> +			      int *val, int *val2, long mask)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	int ret = -EINVAL;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = iio_device_claim_direct_mode(indio_dev);
> +		if (ret)
> +			return ret;
> +		if (chan->type == IIO_VOLTAGE)
> +			ret = stm32_adc_single_conv(indio_dev, chan, val);
> +		iio_device_release_direct_mode(indio_dev);
> +		break;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = adc->common->vref_mv;
> +		*val2 = chan->scan_type.realbits;
> +		ret = IIO_VAL_FRACTIONAL_LOG2;
> +		break;
> +	default:
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +/**
> + * stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
As this is kernel doc please document the parameter as well.  Otherwise
we'll get a pile of warnings!
> + */
> +static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
> +{
> +	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +	const struct stm32_adc_reginfo *reginfo =
> +		adc->common->data->adc_reginfo;
> +	u32 mask, clr_mask, status = stm32_adc_readl(adc, reginfo->isr);
> +
> +	if (adc->injected) {
> +		mask = reginfo->jeoc;
> +		clr_mask = mask;
> +	} else {
> +		mask = reginfo->eoc;
> +		/* don't clear 'eoc' as it is cleared when reading 'dr' */
> +		clr_mask = 0;
> +	}
> +
> +	/* clear irq */
> +	stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
Want to do this in the non injected case? it's a noop isn't it?

> +	status &= mask;
> +
> +	/* Regular data */
> +	if (status & reginfo->eoc) {
Hmm.. this is a little bit of 'missuse' of the standard trigger architecture
but as long as it's restricted to just this device I don't suppose we need
to care.  Only reason we need it is to provide control of 'which' hardware
trigger is being used.

Guessing the DMA will almost always be turned on and will make this oddity
effectively disappear.
> +		adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
> +		if (iio_buffer_enabled(indio_dev)) {
> +			adc->bufi++;
> +			if (adc->bufi >= adc->num_conv) {
> +				stm32_adc_conv_irq_disable(adc);
> +				iio_trigger_poll(indio_dev->trig);
> +			}
> +		} else {
> +			complete(&adc->completion);
> +		}
> +	}
> +
> +	/* Injected data */
> +	if (status & reginfo->jeoc) {
> +		int i;
> +
> +		for (i = 0; i < adc->num_conv; i++) {
> +			adc->buffer[i] = stm32_adc_readl(adc, reginfo->jdr[i]);
> +			adc->bufi++;
> +		}
> +
> +		if (iio_buffer_enabled(indio_dev)) {
> +			stm32_adc_conv_irq_disable(adc);
> +			iio_trigger_poll(indio_dev->trig);
> +		} else {
> +			complete(&adc->completion);
> +		}
> +	}
> +
> +	/*
> +	 * In case end of conversion flags have been handled, this has been
> +	 * handled for this ADC instance
> +	 */
> +	if (status)
> +		return IRQ_HANDLED;
> +
> +	/* This adc instance didn't trigger this interrupt */
> +	return IRQ_NONE;
> +}
> +
> +/**
> + * stm32_adc_common_isr() - Common isr for the whole ADC block
> + *
> + * There is one IRQ for all ADCs in ADC block, check all instances.
> + */
> +static irqreturn_t stm32_adc_common_isr(int irq, void *data)
> +{
> +	struct stm32_adc_common *common = data;
> +	irqreturn_t ret = IRQ_NONE;
> +	struct stm32_adc *adc;
> +
> +	list_for_each_entry(adc, &common->adc_list, adc_list)
> +		ret |= stm32_adc_isr(adc);
Hmm.. ret |= is rather fragile.  Preferable to make the handling of NONE
vs IRQ_HANDLED explicit.

If you were to split the driver up as I suggested might make sense above,
then this would be done with an irq chip in a top level device (effectively
a very simple mfd).
> +
> +	return ret;
> +}
> +
> +/**
> + * stm32_adc_validate_trigger() - validate trigger for stm32 adc
> + * @indio_dev: IIO device
> + * @trig: new trigger
> + *
> + * Returns: 0 if trig matches one of the triggers registered by stm32 adc
> + * driver, -EINVAL otherwise.
> + */
> +static int stm32_adc_validate_trigger(struct iio_dev *indio_dev,
> +				      struct iio_trigger *trig)
> +{
> +	return stm32_adc_get_trig_index(indio_dev, trig) < 0 ? -EINVAL : 0;
> +}
> +
> +static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
> +				      const unsigned long *scan_mask)
I'm glad you kept this relatively simple compared to some of the
'fun' the hardware is capable of. Very wise!
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	int ret;
> +	u32 bit;
> +
> +	adc->num_conv = 0;
> +	for_each_set_bit(bit, scan_mask, indio_dev->masklength)
> +		adc->num_conv++;
> +
> +	ret = stm32_adc_conf_scan(indio_dev, scan_mask);
> +	if (ret)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
> +			      const struct of_phandle_args *iiospec)
> +{
> +	int i;
> +
> +	for (i = 0; i < indio_dev->num_channels; i++)
> +		if (indio_dev->channels[i].channel == iiospec->args[0])
> +			return i;
> +
> +	return -EINVAL;
> +}
> +
> +/**
> + * stm32_adc_debugfs_reg_access - read or write register value
> + *
> + * To read a value from an ADC register:
> + *   echo [ADC reg offset] > direct_reg_access
> + *   cat direct_reg_access
> + *
> + * To write a value in a ADC register:
> + *   echo [ADC_reg_offset] [value] > direct_reg_access
> + */
> +static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
> +					unsigned reg, unsigned writeval,
> +					unsigned *readval)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +
> +	if (!readval)
> +		stm32_adc_writel(adc, reg, writeval);
> +	else
> +		*readval = stm32_adc_readl(adc, reg);
> +
> +	return 0;
> +}
> +
> +static const struct iio_info stm32_adc_iio_info = {
> +	.read_raw = stm32_adc_read_raw,
> +	.validate_trigger = stm32_adc_validate_trigger,
> +	.update_scan_mode = stm32_adc_update_scan_mode,
> +	.debugfs_reg_access = stm32_adc_debugfs_reg_access,
> +	.of_xlate = stm32_adc_of_xlate,
> +	.driver_module = THIS_MODULE,
> +};
> +
> +static int stm32_adc_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +
> +	stm32_adc_disable(adc);
This is a surprise as postdisbale should balance preenable...
Ah, you have update scan mode enabling the adc.  If you can balance it
better by moving that to preenable please do as it is more 'obviously' correct.
> +
> +	return 0;
> +}
> +
> +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
> +	.postenable = &iio_triggered_buffer_postenable,
> +	.predisable = &iio_triggered_buffer_predisable,
> +	.postdisable = &stm32_adc_buffer_postdisable,
> +};
> +
> +static int stm32_adc_validate_device(struct iio_trigger *trig,
> +				     struct iio_dev *indio_dev)
> +{
> +	struct iio_dev *indio = iio_trigger_get_drvdata(trig);
> +
> +	return indio != indio_dev ? -EINVAL : 0;
> +}
> +
> +static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
> +				       bool state)
> +{
> +	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	int ret;
> +
> +	if (state) {
> +		/* Reset adc buffer index */
> +		adc->bufi = 0;
> +
> +		/* Allocate adc buffer */
> +		adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
I'd be more cynical.  It's not that big a memory allocation at worst.
Just put a big enough buffer in your adc structure and don't bother doing
it dynamically.

If you didn't want to do it, it should be in the preenable callback rather
than the trigger state one (for semantic reasons rather than because it's a
bug)
> +		if (!adc->buffer)
> +			return -ENOMEM;
> +
> +		ret = stm32_adc_set_trig(indio_dev, trig);
> +		if (ret) {
> +			dev_err(&indio_dev->dev, "Can't set trigger\n");
> +			goto err_buffer_free;
> +		}
> +
> +		stm32_adc_conv_irq_enable(adc);
> +
> +		ret = stm32_adc_start_conv(adc);
> +		if (ret) {
> +			dev_err(&indio_dev->dev, "Failed to start\n");
> +			goto err_irq_trig_disable;
> +		}
> +	} else {
> +		ret = stm32_adc_stop_conv(adc);
> +		if (ret < 0) {
> +			dev_err(&indio_dev->dev, "Failed to stop\n");
> +			return ret;
> +		}
> +
> +		stm32_adc_conv_irq_disable(adc);
> +
> +		ret = stm32_adc_set_trig(indio_dev, NULL);
> +		if (ret)
> +			dev_warn(&indio_dev->dev, "Can't clear trigger\n");
> +
> +		kfree(adc->buffer);
> +		adc->buffer = NULL;
> +	}
> +
> +	return 0;
> +
> +err_irq_trig_disable:
> +	stm32_adc_conv_irq_disable(adc);
> +	stm32_adc_set_trig(indio_dev, NULL);
> +
> +err_buffer_free:
> +	kfree(adc->buffer);
> +	adc->buffer = NULL;
> +
> +	return ret;
> +}
> +
> +static const struct iio_trigger_ops stm32_adc_trigger_ops = {
> +	.owner = THIS_MODULE,
> +	.validate_device = stm32_adc_validate_device,
> +	.set_trigger_state = stm32_adc_set_trigger_state,
> +};
> +
> +static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +
> +	dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
> +
> +	/* reset buffer index */
> +	adc->bufi = 0;
> +	iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
> +					   pf->timestamp);
> +
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	/* re-enable eoc irq */
> +	stm32_adc_conv_irq_enable(adc);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void stm32_adc_trig_unregister(struct iio_dev *indio_dev)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	struct iio_trigger *trig, *_t;
> +
> +	list_for_each_entry_safe(trig, _t, &adc->extrig_list, alloc_list) {
> +		iio_trigger_unregister(trig);
> +		list_del(&trig->alloc_list);
> +	}
> +}
> +
I'd like a bit of documentation on this and a few of the other more
complex functions.  Here it wasn't immediately obvious to me that it
was registering a large set of triggers.  Also, silly question but
do you have any means of controlling the various timer setups from userspace?

There have been numerous discussions over the years on having a generic
timer subsystem, but if anything got written it passed me by. I have a couple
of boards where it would be handy but never had the time to do more than
talk about it ;)
> +static int stm32_adc_trig_register(struct iio_dev *indio_dev)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	struct stm32_adc_common *common = adc->common;
> +	const struct stm32_adc_trig_info *ext = common->data->ext_triggers;
> +	struct iio_trigger *trig;
> +	int i, ret = 0;
> +
> +	if (adc->injected)
> +		ext = common->data->jext_triggers;
> +	else
> +		ext = common->data->ext_triggers;
> +
> +	for (i = 0; ext && ext[i].name; i++) {
> +		trig = devm_iio_trigger_alloc(common->dev, "%s_%s%d_%s",
> +					      indio_dev->name,
> +					      adc->injected ? "jext" : "ext",
> +					      ext[i].extsel, ext[i].name);
> +		if (!trig) {
> +			dev_err(common->dev, "trig %s_%s%d_%s alloc failed\n",
> +				indio_dev->name,
> +				adc->injected ? "jext" : "ext",
> +				ext[i].extsel, ext[i].name);
> +			ret = -ENOMEM;
> +			goto err;
> +		}
> +
> +		trig->dev.parent = common->dev;
> +		trig->ops = &stm32_adc_trigger_ops;
> +		iio_trigger_set_drvdata(trig, indio_dev);
> +
> +		ret = iio_trigger_register(trig);
> +		if (ret) {
> +			dev_err(common->dev,
> +				"trig %s_%s%d_%s register failed\n",
> +				indio_dev->name,
> +				adc->injected ? "jext" : "ext",
> +				ext[i].extsel, ext[i].name);
> +			goto err;
> +		}
> +
> +		list_add_tail(&trig->alloc_list, &adc->extrig_list);
> +	}
> +
> +	return 0;
> +err:
> +	stm32_adc_trig_unregister(indio_dev);
> +
> +	return ret;
> +}
> +
> +static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
> +				    struct iio_chan_spec *chan,
> +				    const struct stm32_adc_chan_spec *channel,
> +				    int scan_index)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +
> +	chan->type = channel->type;
> +	chan->channel = channel->channel;
> +	chan->datasheet_name = channel->name;
> +	chan->extend_name = channel->name;
> +	chan->scan_index = scan_index;
> +	chan->indexed = 1;
> +	chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
> +	chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
> +	chan->scan_type.sign = 'u';
> +	chan->scan_type.realbits = adc->common->data->highres;
> +	chan->scan_type.storagebits = STM32_STORAGEBITS;
This is one of those cases where actually I'd argue just having the number
here and not under a define would be clearer!  So just put 16 here.
> +	chan->scan_type.shift = 0;
Should be unneeded.  Shift of 0 is the obvious default so no info provided
to readers of the code either really.
> +}
> +
> +static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
> +				  const struct stm32_adc_info *adc_info)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	struct device_node *node = indio_dev->dev.of_node;
> +	struct property *prop;
> +	const __be32 *cur;
> +	struct iio_chan_spec *channels;
> +	int scan_index = 0, num_channels = 0;
> +	u32 val;
> +
> +	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
> +		num_channels++;
> +
> +	channels = devm_kcalloc(&indio_dev->dev, num_channels,
> +				sizeof(struct iio_chan_spec), GFP_KERNEL);
> +	if (!channels)
> +		return -ENOMEM;
> +
> +	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
> +		stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
> +					&adc_info->channels[val],
> +					scan_index);
> +		scan_index++;
> +	}
> +
> +	adc->max_channels = adc_info->max_channels;
> +	indio_dev->num_channels = scan_index;
> +	indio_dev->channels = channels;
> +
> +	return 0;
> +}
> +
> +static int stm32_adc_register(struct stm32_adc_common *common,
> +			      struct device_node *child)
> +{
> +	struct iio_dev *indio_dev;
> +	struct stm32_adc *adc;
> +	int i, ret;
> +	u32 reg;
> +
> +	ret = of_property_read_u32(child, "reg", &reg);
> +	if (ret != 0) {
> +		dev_err(common->dev, "missing reg property\n");
> +		return -EINVAL;
> +	}
> +
> +	for (i = 0; common->data->adc_info[i].channels; i++)
> +		if (common->data->adc_info[i].reg == reg)
> +			break;
> +
> +	if (i >= STM32_ADC_ID_MAX || !common->data->adc_info[i].channels) {
> +		dev_err(common->dev, "bad adc reg offset\n");
> +		return -ENOENT;
> +	}
> +
> +	indio_dev = devm_iio_device_alloc(common->dev, sizeof(*adc));
> +	if (!indio_dev) {
> +		dev_err(common->dev, "iio device allocation failed\n");
> +		return -ENOMEM;
> +	}
> +
> +	adc = iio_priv(indio_dev);
> +	adc->id = i;
> +	adc->offset = reg;
> +	adc->common = common;
> +	INIT_LIST_HEAD(&adc->extrig_list);
> +	spin_lock_init(&adc->lock);
> +	init_completion(&adc->completion);
> +
> +	if (child->name)
> +		indio_dev->name = child->name;
> +	else
> +		indio_dev->name = common->data->adc_info[i].name;
> +	indio_dev->dev.parent = common->dev;
> +	indio_dev->dev.of_node = child;
> +	indio_dev->info = &stm32_adc_iio_info;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	if (of_property_read_bool(child, "st,injected")) {
> +		dev_dbg(common->dev, "%s Configured to use injected\n",
> +			indio_dev->name);
> +		adc->injected = true;
> +	}
> +
> +	adc->clk = of_clk_get(child, 0);
> +	if (IS_ERR(adc->clk)) {
> +		adc->clk = NULL;
> +		dev_dbg(common->dev, "No child clk found\n");
> +	} else {
> +		ret = clk_prepare_enable(adc->clk);
> +		if (ret < 0)
> +			goto err_clk_put;
> +	}
> +
> +	ret = stm32_adc_chan_of_init(indio_dev, &common->data->adc_info[i]);
> +	if (ret < 0) {
> +		dev_err(common->dev, "iio channels init failed\n");
> +		goto err_clk_disable;
> +	}
> +
> +	ret = stm32_adc_trig_register(indio_dev);
> +	if (ret)
> +		goto err_clk_disable;
> +
> +	ret = iio_triggered_buffer_setup(indio_dev,
> +					 &iio_pollfunc_store_time,
> +					 &stm32_adc_trigger_handler,
> +					 &iio_triggered_buffer_setup_ops);
> +	if (ret) {
> +		dev_err(common->dev, "buffer setup failed\n");
> +		goto err_trig_unregister;
> +	}
> +
> +	ret = iio_device_register(indio_dev);
> +	if (ret) {
> +		dev_err(common->dev, "iio dev register failed\n");
> +		goto err_buffer_cleanup;
> +	}
> +
> +	list_add_tail(&adc->adc_list, &common->adc_list);
> +
> +	return 0;
> +
> +err_buffer_cleanup:
> +	iio_triggered_buffer_cleanup(indio_dev);
> +
> +err_trig_unregister:
> +	stm32_adc_trig_unregister(indio_dev);
> +
> +err_clk_disable:
> +	if (adc->clk)
> +		clk_disable_unprepare(adc->clk);
> +
> +err_clk_put:
> +	if (adc->clk)
> +		clk_put(adc->clk);
> +
> +	return ret;
> +}
> +
> +static void stm32_adc_unregister(struct stm32_adc *adc)
> +{
> +	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +
> +	iio_device_unregister(indio_dev);
> +	iio_triggered_buffer_cleanup(indio_dev);
> +	stm32_adc_trig_unregister(indio_dev);
> +	if (adc->clk) {
> +		clk_disable_unprepare(adc->clk);
> +		clk_put(adc->clk);
> +	}
> +}
> +
> +int stm32_adc_probe(struct platform_device *pdev)
> +{
> +	struct device_node *np = pdev->dev.of_node, *child;
> +	struct device *dev = &pdev->dev;
> +	const struct of_device_id *match;
> +	struct stm32_adc_common *common;
> +	struct stm32_adc *adc;
> +	struct resource *res;
> +	int ret;
> +
> +	match = of_match_device(dev->driver->of_match_table, &pdev->dev);
> +	if (!match || !match->data) {
> +		dev_err(&pdev->dev, "compatible data not provided\n");
How would we have instantiated this if there was not a suitable match?
As such what does this check give us? (confused!)
> +		return -EINVAL;
> +	}
> +
> +	common = devm_kzalloc(&pdev->dev, sizeof(*common), GFP_KERNEL);
> +	if (!common)
> +		return -ENOMEM;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	common->base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(common->base))
> +		return PTR_ERR(common->base);
> +
> +	common->data = match->data;
> +	common->dev = &pdev->dev;
> +	platform_set_drvdata(pdev, common);
> +	mutex_init(&common->lock);
> +	INIT_LIST_HEAD(&common->adc_list);
> +
> +	common->vref = devm_regulator_get(&pdev->dev, "vref");
> +	if (IS_ERR(common->vref)) {
> +		ret = PTR_ERR(common->vref);
> +		dev_err(&pdev->dev, "vref get failed, %d\n", ret);
> +		return ret;
> +	}
> +
> +	ret = regulator_enable(common->vref);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "vref enable failed\n");
> +		return ret;
> +	}
> +
> +	ret = regulator_get_voltage(common->vref);
> +	if (ret < 0) {
> +		dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
> +		goto err_regulator_disable;
> +	}
> +	common->vref_mv = ret / 1000;
> +	dev_dbg(&pdev->dev, "vref+=%dmV\n", common->vref_mv);
> +
> +	common->aclk = devm_clk_get(&pdev->dev, "adc");
> +	if (IS_ERR(common->aclk)) {
> +		ret = PTR_ERR(common->aclk);
> +		dev_err(&pdev->dev, "Can't get 'adc' clock\n");
> +		goto err_regulator_disable;
> +	}
> +
> +	ret = clk_prepare_enable(common->aclk);
> +	if (ret < 0) {
> +		dev_err(common->dev, "adc clk enable failed\n");
> +		goto err_regulator_disable;
> +	}
> +
> +	common->irq = platform_get_irq(pdev, 0);
> +	if (common->irq < 0) {
> +		dev_err(&pdev->dev, "failed to get irq\n");
> +		ret = common->irq;
> +		goto err_clk_disable;
> +	}
> +
> +	ret = devm_request_irq(&pdev->dev, common->irq,	stm32_adc_common_isr,
> +			       0, pdev->name, common);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to request irq\n");
> +		goto err_clk_disable;
> +	}
> +
> +	/* Parse adc child nodes to retrieve master/slave instances data */
> +	for_each_available_child_of_node(np, child) {
> +		ret = stm32_adc_register(common, child);
> +		if (ret)
> +			goto err_unregister;
> +	}
> +
> +	dev_info(&pdev->dev, "registered\n");
No benefit in this info being provided (it's obvious, device just turned up
in sysfs :) So drop it.
> +
> +	return 0;
> +
> +err_unregister:
> +	list_for_each_entry(adc, &common->adc_list, adc_list)
> +		stm32_adc_unregister(adc);
> +
> +err_clk_disable:
> +	clk_disable_unprepare(common->aclk);
> +
> +err_regulator_disable:
> +	regulator_disable(common->vref);
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(stm32_adc_probe);
> +
> +int stm32_adc_remove(struct platform_device *pdev)
> +{
> +	struct stm32_adc_common *common = platform_get_drvdata(pdev);
> +	struct stm32_adc *adc;
> +
> +	list_for_each_entry(adc, &common->adc_list, adc_list)
> +		stm32_adc_unregister(adc);
> +	clk_disable_unprepare(common->aclk);
> +	regulator_disable(common->vref);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(stm32_adc_remove);
> +
> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
> new file mode 100644
> index 0000000..0be603c
> --- /dev/null
> +++ b/drivers/iio/adc/stm32/stm32-adc.h
> @@ -0,0 +1,442 @@
> +/*
> + * This file is part of STM32 ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Fabrice Gasnier <fabrice.gasnier@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/>.
> + */
> +
> +#ifndef __STM32_ADC_H
> +#define __STM32_ADC_H
> +
> +/*
> + * STM32 - ADC global register map
> + * ________________________________________________________
> + * | Offset |                 Register                    |
> + * --------------------------------------------------------
> + * | 0x000  |                Master ADC1                  |
> + * --------------------------------------------------------
> + * | 0x100  |                Slave ADC2                   |
> + * --------------------------------------------------------
> + * | 0x200  |                Slave ADC3                   |
> + * --------------------------------------------------------
> + * | 0x300  |         Master & Slave common regs          |
> + * --------------------------------------------------------
> + */
> +#define STM32_ADCX_COMN_OFFSET		0x300
> +#define STM32_ADC_ID_MAX		3
> +#define STM32_ADC_MAX_SQ		16	/* SQ1..SQ16 */
> +#define STM32_ADC_MAX_JSQ		4	/* JSQ1..JSQ4 */
> +
> +/* STM32 value masks */
> +#define STM32_RESULT_MASK	GENMASK(15, 0)
> +#define STM32_STORAGEBITS	16
> +
> +/* External trigger enable for regular or injected channels (exten/jexten) */
> +enum stm32_adc_exten {
> +	STM32_EXTEN_SWTRIG,
> +	STM32_EXTEN_HWTRIG_RISING_EDGE,
> +	STM32_EXTEN_HWTRIG_FALLING_EDGE,
> +	STM32_EXTEN_HWTRIG_BOTH_EDGES,
> +};
> +
> +enum stm32_adc_extsel {
> +	STM32_EXT0,
> +	STM32_EXT1,
> +	STM32_EXT2,
> +	STM32_EXT3,
> +	STM32_EXT4,
> +	STM32_EXT5,
> +	STM32_EXT6,
> +	STM32_EXT7,
> +	STM32_EXT8,
> +	STM32_EXT9,
> +	STM32_EXT10,
> +	STM32_EXT11,
> +	STM32_EXT12,
> +	STM32_EXT13,
> +	STM32_EXT14,
> +	STM32_EXT15,
> +	STM32_EXT16,
> +	STM32_EXT17,
> +	STM32_EXT18,
> +	STM32_EXT19,
> +	STM32_EXT20,
> +	STM32_EXT21,
> +	STM32_EXT22,
> +	STM32_EXT23,
> +	STM32_EXT24,
> +	STM32_EXT25,
> +	STM32_EXT26,
> +	STM32_EXT27,
> +	STM32_EXT28,
> +	STM32_EXT29,
> +	STM32_EXT30,
> +	STM32_EXT31,
> +};
> +
> +enum stm32_adc_jextsel {
> +	STM32_JEXT0,
> +	STM32_JEXT1,
> +	STM32_JEXT2,
> +	STM32_JEXT3,
> +	STM32_JEXT4,
> +	STM32_JEXT5,
> +	STM32_JEXT6,
> +	STM32_JEXT7,
> +	STM32_JEXT8,
> +	STM32_JEXT9,
> +	STM32_JEXT10,
> +	STM32_JEXT11,
> +	STM32_JEXT12,
> +	STM32_JEXT13,
> +	STM32_JEXT14,
> +	STM32_JEXT15,
> +	STM32_JEXT16,
> +	STM32_JEXT17,
> +	STM32_JEXT18,
> +	STM32_JEXT19,
> +	STM32_JEXT20,
> +	STM32_JEXT21,
> +	STM32_JEXT22,
> +	STM32_JEXT23,
> +	STM32_JEXT24,
> +	STM32_JEXT25,
> +	STM32_JEXT26,
> +	STM32_JEXT27,
> +	STM32_JEXT28,
> +	STM32_JEXT29,
> +	STM32_JEXT30,
> +	STM32_JEXT31,
> +};
> +
> +#define	STM32_ADC_TIMEOUT_US	100000
> +#define	STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
> +
> +/**
> + * struct stm32_adc_chan_spec - specification of stm32 adc channel
> + * @type:	IIO channel type
> + * @channel:	channel number (single ended)
> + * @name:	channel name (single ended)
> + */
> +struct stm32_adc_chan_spec {
> +	enum iio_chan_type	type;
> +	int			channel;
> +	const char		*name;
> +};
> +
> +/**
> + * struct stm32_adc_trig_info - ADC trigger info
> + * @extsel:		trigger selection for regular or injected
> + * @name:		name of the trigger, corresponding to its source
> + */
> +struct stm32_adc_trig_info {
> +	u32 extsel;
> +	const char *name;
> +};
> +
> +/**
> + * struct stm32_adc_info - stm32 ADC, per instance config data
> + * @name:		default name for this instance (like "adc1")
> + * @reg:		reg offset for this instance (e.g. 0x0 for adc1...)
> + * @channels:		Reference to stm32 channels spec
> + * @max_channels:	Number of single ended channels
> + */
> +struct stm32_adc_info {
> +	const char *name;
> +	u32 reg;
> +	const struct stm32_adc_chan_spec *channels;
> +	int max_channels;
> +};
> +
> +/**
> + * stm32_adc_regs - stm32 ADC misc registers & bitfield desc
> + * @reg:		register offset
> + * @mask:		bitfield mask
> + * @shift:		left shift
> + */
> +struct stm32_adc_regs {
> +	int reg;
> +	int mask;
> +	int shift;
> +};
> +
> +/**
> + * stm32_adc_trig_reginfo - stm32 ADC trigger control registers description
> + * @reg:		trigger control register offset (exten/jexten)
> + * @exten_mask:		external trigger en/polarity mask in @reg
> + * @exten_shift:	external trigger en/polarity shift in @reg
> + * @extsel_mask:	external trigger source mask in @reg
> + * @extsel_shift:	external trigger source shift in @reg
> + */
> +struct stm32_adc_trig_reginfo {
> +	u32 reg;
> +	u32 exten_mask;
> +	u32 exten_shift;
> +	u32 extsel_mask;
> +	u32 extsel_shift;
> +};
> +
> +/**
> + * struct stm32_adc_reginfo - stm32 ADC registers description
> + * @isr:		interrupt status register offset
> + * @eoc:		end of conversion mask in @isr
> + * @jeoc:		end of injected conversion sequence mask in @isr
> + * @ier:		interrupt enable register offset
> + * @eocie:		end of conversion interrupt enable mask in @ier
> + * @jeocie:		end of injected conversion sequence interrupt en mask
> + * @dr:			data register offset
> + * @jdr:		injected data registers offsets
> + * @sqr_regs:		Regular sequence registers description
> + * @jsqr_reg:		Injected sequence register description
> + * @trig_reginfo:	regular trigger control registers description
> + * @jtrig_reginfo:	injected trigger control registers description
> + */
> +struct stm32_adc_reginfo {
> +	u32 isr;
> +	u32 eoc;
> +	u32 jeoc;
> +	u32 ier;
> +	u32 eocie;
> +	u32 jeocie;
> +	u32 dr;
> +	u32 jdr[4];
> +	const struct stm32_adc_regs *sqr_regs;
> +	const struct stm32_adc_regs *jsqr_reg;
> +	const struct stm32_adc_trig_reginfo *trig_reginfo;
> +	const struct stm32_adc_trig_reginfo *jtrig_reginfo;
> +};
> +
> +struct stm32_adc;
> +
> +/**
> + * struct stm32_adc_ops - stm32 ADC, compatible dependent data
> + * - stm32 ADC may work as single ADC, or as tightly coupled master/slave ADCs.
> + *
> + * @adc_info:		Array spec for stm32 adc master/slaves instances
> + * @ext_triggers:	Reference to trigger info for regular channels
> + * @jext_triggers:	Reference to trigger info for injected channels
> + * @adc_reginfo:	stm32 ADC registers description
> + * @highres:		Max resolution
> + * @max_clock_rate:	Max input clock rate
> + * @clk_sel:		routine to select common clock and prescaler
> + * @start_conv:		routine to start conversions
> + * @stop_conv:		routine to stop conversions
> + * @is_started:		routine to get adc 'started' state
> + * @regular_started	routine to check regular conversions status
> + * @injected_started	routine to check injected conversions status
> + * @enable:		optional routine to enable stm32 adc
> + * @disable:		optional routine to disable stm32 adc
> + * @is_enabled		reports enabled state
> + */
This is a big chunk of abstraction that seems excessive at the moment.
I'd rather see it introduced only just before it's actually used..
(I'm guessing it's intended for support of similar parts?)

Right now it just makes the driver harder to review.
> +struct stm32_adc_ops {
> +	const struct stm32_adc_info *adc_info;
> +	const struct stm32_adc_trig_info *ext_triggers;
> +	const struct stm32_adc_trig_info *jext_triggers;
> +	const struct stm32_adc_reginfo *adc_reginfo;
> +	int highres;
> +	unsigned long max_clock_rate;
> +	int (*clk_sel)(struct stm32_adc *adc);
> +	int (*start_conv)(struct stm32_adc *adc);
> +	int (*stop_conv)(struct stm32_adc *adc);
> +	bool (*is_started)(struct stm32_adc *adc);
> +	bool (*regular_started)(struct stm32_adc *adc);
> +	bool (*injected_started)(struct stm32_adc *adc);
> +	int (*enable)(struct stm32_adc *adc);
> +	void (*disable)(struct stm32_adc *adc);
> +	bool (*is_enabled)(struct stm32_adc *adc);
> +};
> +
> +struct stm32_adc_common;
> +
> +/**
> + * struct stm32_adc - private data of each ADC IIO instance
> + * @common:		reference to ADC block common data
> + * @adc_list:		current ADC entry in common ADC list
> + * @id:			ADC instance number (e.g. adc 1, 2 or 3)
> + * @offset:		ADC instance register offset in ADC block
> + * @max_channels:	Max channels number for this ADC.
> + * @extrig_list:	External trigger list (for regular channel)
> + * @completion:		end of single conversion completion
> + * @buffer:		data buffer
> + * @bufi:		data buffer index
> + * @num_conv:		expected number of scan conversions
> + * @injected:		use injected channels on this adc
> + * @lock:		spinlock
> + * @clk:		optional adc clock, for this adc instance
> + * @calib:		optional calibration data
> + * @en:			emulates enabled state on some stm32 adc
> + */
> +struct stm32_adc {
> +	struct stm32_adc_common	*common;
> +	struct list_head	adc_list;
> +	int			id;
> +	int			offset;
> +	int			max_channels;
> +	struct list_head	extrig_list;
> +	struct completion	completion;
> +	u16			*buffer;
> +	int			bufi;
> +	int			num_conv;
> +	bool			injected;
> +	spinlock_t		lock;		/* interrupt lock */
> +	struct clk		*clk;
> +	void			*calib;
> +	bool			en;
> +};
> +
> +/**
> + * struct stm32_adc_common - private data of ADC driver, common to all
> + * ADC instances (ADC block)
> + * @dev:		device for this controller
> + * @base:		control registers base cpu addr
> + * @irq:		Common irq line for all adc instances
> + * @data:		STM32 dependent data from compatible
> + * @adc_list:		list of all stm32 ADC in this ADC block
> + * @aclk:		common clock for the analog circuitry
> + * @vref:		regulator reference
> + * @vref_mv:		vref voltage (mv)
> + * @lock:		mutex
> + */
> +struct stm32_adc_common {
> +	struct device			*dev;
> +	void __iomem			*base;
> +	int				irq;
> +	const struct stm32_adc_ops	*data;
> +	struct list_head		adc_list;
> +	struct clk			*aclk;
> +	struct regulator		*vref;
> +	int				vref_mv;
> +	struct mutex			lock;	/* read_raw lock */
> +};
> +
> +/* Helper routines */
> +static inline int stm32_adc_start_conv(struct stm32_adc *adc)
> +{
> +	return adc->common->data->start_conv(adc);
> +}
> +
> +static inline int stm32_adc_stop_conv(struct stm32_adc *adc)
> +{
> +	return adc->common->data->stop_conv(adc);
> +}
> +
> +static inline bool stm32_adc_is_started(struct stm32_adc *adc)
> +{
> +	return adc->common->data->is_started(adc);
> +}
> +
> +static inline bool stm32_adc_regular_started(struct stm32_adc *adc)
> +{
> +	return adc->common->data->regular_started(adc);
> +}
> +
> +static inline bool stm32_adc_injected_started(struct stm32_adc *adc)
> +{
> +	return adc->common->data->injected_started(adc);
> +}
> +
> +static inline bool stm32_adc_clk_sel(struct stm32_adc *adc)
> +{
> +	return adc->common->data->clk_sel(adc);
> +}
> +
> +static inline int stm32_adc_enable(struct stm32_adc *adc)
> +{
> +	if (adc->common->data->enable)
> +		return adc->common->data->enable(adc);
> +
> +	adc->en = true;
> +
> +	return 0;
> +}
> +
> +static inline bool stm32_adc_is_enabled(struct stm32_adc *adc)
> +{
> +	if (adc->common->data->is_enabled)
> +		return adc->common->data->is_enabled(adc);
> +	else
> +		return adc->en;
> +}
> +
> +static inline void stm32_adc_disable(struct stm32_adc *adc)
> +{
> +	/* Check there is no regular or injected on-going conversions */
> +	if (stm32_adc_is_started(adc))
> +		return;
> +
> +	if (adc->common->data->disable)
> +		adc->common->data->disable(adc);
> +	else
> +		adc->en = false;
> +}
> +
> +/* STM32 ADC registers access routines */
> +static inline u32 stm32_adc_common_readl(struct stm32_adc_common *com, u32 reg)
> +{
> +	u32 val = readl_relaxed(com->base + reg);
> +
> +	return val;
> +}
> +
> +static inline void stm32_adc_common_writel(struct stm32_adc_common *com,
> +					   u32 reg, u32 val)
> +{
> +	writel_relaxed(val, com->base + reg);
> +}
> +
> +static inline u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
> +{
> +	u32 val = readl_relaxed(adc->common->base + adc->offset + reg);
> +
> +	return val;
> +}
> +
> +#define stm32_adc_readl_addr(addr)	stm32_adc_readl(adc, addr)
> +
> +#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \
> +	readx_poll_timeout(stm32_adc_readl_addr, reg, val, \
> +			   cond, sleep_us, timeout_us)
> +
> +static inline void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
> +{
> +	writel_relaxed(val, adc->common->base + adc->offset + reg);
> +}
> +
> +static inline void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&adc->lock, flags);
> +	stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
> +	spin_unlock_irqrestore(&adc->lock, flags);
> +}
> +
> +static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
> +{
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&adc->lock, flags);
> +	stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
> +	spin_unlock_irqrestore(&adc->lock, flags);
> +}
> +
> +/* STM32 common extended attributes */
> +extern const struct iio_enum stm32_adc_trig_pol;
> +int stm32_adc_probe(struct platform_device *pdev);
> +int stm32_adc_remove(struct platform_device *pdev);
> +
> +#endif
> diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
> new file mode 100644
> index 0000000..147fe9c
> --- /dev/null
> +++ b/drivers/iio/adc/stm32/stm32f4-adc.c
> @@ -0,0 +1,574 @@
> +/*
> + * This file is part of STM32F4 ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Fabrice Gasnier <fabrice.gasnier@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/clk.h>
> +#include <linux/delay.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/platform_device.h>
> +#include "stm32-adc.h"
> +
> +/*
> + * STM32F4 - ADC global register map
> + * ________________________________________________________
> + * | Offset |                 Register                    |
> + * --------------------------------------------------------
> + * | 0x000  |                Master ADC1                  |
> + * --------------------------------------------------------
> + * | 0x100  |                Slave ADC2                   |
> + * --------------------------------------------------------
> + * | 0x200  |                Slave ADC3                   |
> + * --------------------------------------------------------
> + * | 0x300  |         Master & Slave common regs          |
> + * --------------------------------------------------------
> + */
> +
> +/* STM32F4 - Registers for each ADC instance */
> +#define STM32F4_ADCX_SR			0x00
> +#define STM32F4_ADCX_CR1		0x04
> +#define STM32F4_ADCX_CR2		0x08
> +#define STM32F4_ADCX_SMPR1		0x0C
> +#define STM32F4_ADCX_SMPR2		0x10
> +#define STM32F4_ADCX_HTR		0x24
> +#define STM32F4_ADCX_LTR		0x28
> +#define STM32F4_ADCX_SQR1		0x2C
> +#define STM32F4_ADCX_SQR2		0x30
> +#define STM32F4_ADCX_SQR3		0x34
> +#define STM32F4_ADCX_JSQR		0x38
> +#define STM32F4_ADCX_JDR1		0x3C
> +#define STM32F4_ADCX_JDR2		0x40
> +#define STM32F4_ADCX_JDR3		0x44
> +#define STM32F4_ADCX_JDR4		0x48
> +#define STM32F4_ADCX_DR			0x4C
> +
> +/* STM32 - Master & slave registers (common for all instances: 1, 2 & 3) */
> +#define STM32F4_ADC_CSR			(STM32_ADCX_COMN_OFFSET + 0x00)
> +#define STM32F4_ADC_CCR			(STM32_ADCX_COMN_OFFSET + 0x04)
> +#define STM32F4_ADC_CDR			(STM32_ADCX_COMN_OFFSET + 0x08)
> +
> +/* STM32F4_ADCX_SR - bit fields */
> +#define STM32F4_OVR			BIT(5)
> +#define STM32F4_STRT			BIT(4)
> +#define STM32F4_JSTRT			BIT(3)
> +#define STM32F4_JEOC			BIT(2)
> +#define STM32F4_EOC			BIT(1)
> +#define STM32F4_AWD			BIT(0)
> +
> +/* STM32F4_ADCX_CR1 - bit fields */
> +#define STM32F4_OVRIE			BIT(26)
> +#define STM32F4_RES_SHIFT		24
> +#define STM32F4_RES_MASK		GENMASK(25, 24)
> +#define STM32F4_AWDEN			BIT(23)
> +#define STM32F4_JAWDEN			BIT(22)
> +#define STM32F4_DISCNUM_SHIFT		13
> +#define STM32F4_DISCNUM_MASK		GENMASK(15, 13)
> +#define STM32F4_JDISCEN			BIT(12)
> +#define STM32F4_DISCEN			BIT(11)
> +#define STM32F4_JAUTO			BIT(10)
> +#define STM32F4_AWDSGL			BIT(9)
> +#define STM32F4_SCAN			BIT(8)
> +#define STM32F4_JEOCIE			BIT(7)
> +#define STM32F4_AWDIE			BIT(6)
> +#define STM32F4_EOCIE			BIT(5)
> +#define STM32F4_AWDCH_SHIFT		0
> +#define STM32F4_AWDCH_MASK		GENMASK(4, 0)
> +
> +/* STM32F4_ADCX_CR2 - bit fields */
> +#define STM32F4_SWSTART			BIT(30)
> +#define STM32F4_EXTEN_SHIFT		28
> +#define STM32F4_EXTEN_MASK		GENMASK(29, 28)
> +#define STM32F4_EXTSEL_SHIFT		24
> +#define STM32F4_EXTSEL_MASK		GENMASK(27, 24)
> +#define STM32F4_JSWSTART		BIT(22)
> +#define STM32F4_JEXTEN_SHIFT		20
> +#define STM32F4_JEXTEN_MASK		GENMASK(21, 20)
> +#define STM32F4_JEXTSEL_SHIFT		16
> +#define STM32F4_JEXTSEL_MASK		GENMASK(19, 16)
> +#define STM32F4_ALIGN			BIT(11)
> +#define STM32F4_EOCS			BIT(10)
> +#define STM32F4_DDS			BIT(9)
> +#define STM32F4_DMA			BIT(8)
> +#define STM32F4_CONT			BIT(1)
> +#define STM32F4_ADON			BIT(0)
> +
> +/* STM32F4_ADCX_SMPR1 - bit fields */
> +#define STM32F4_SMP18_SHIFT		24
> +#define STM32F4_SMP18_MASK		GENMASK(26, 24)
> +#define STM32F4_SMP17_SHIFT		21
> +#define STM32F4_SMP17_MASK		GENMASK(23, 21)
> +#define STM32F4_SMP16_SHIFT		18
> +#define STM32F4_SMP16_MASK		GENMASK(20, 18)
> +#define STM32F4_SMP15_SHIFT		15
> +#define STM32F4_SMP15_MASK		GENMASK(17, 15)
> +#define STM32F4_SMP14_SHIFT		12
> +#define STM32F4_SMP14_MASK		GENMASK(14, 12)
> +#define STM32F4_SMP13_SHIFT		9
> +#define STM32F4_SMP13_MASK		GENMASK(11, 9)
> +#define STM32F4_SMP12_SHIFT		6
> +#define STM32F4_SMP12_MASK		GENMASK(8, 6)
> +#define STM32F4_SMP11_SHIFT		3
> +#define STM32F4_SMP11_MASK		GENMASK(5, 3)
> +#define STM32F4_SMP10_SHIFT		0
> +#define STM32F4_SMP10_MASK		GENMASK(2, 0)
> +
> +/* STM32F4_ADCX_SMPR2 - bit fields */
> +#define STM32F4_SMP9_SHIFT		27
> +#define STM32F4_SMP9_MASK		GENMASK(29, 27)
> +#define STM32F4_SMP8_SHIFT		24
> +#define STM32F4_SMP8_MASK		GENMASK(26, 24)
> +#define STM32F4_SMP7_SHIFT		21
> +#define STM32F4_SMP7_MASK		GENMASK(23, 21)
> +#define STM32F4_SMP6_SHIFT		18
> +#define STM32F4_SMP6_MASK		GENMASK(20, 18)
> +#define STM32F4_SMP5_SHIFT		15
> +#define STM32F4_SMP5_MASK		GENMASK(17, 15)
> +#define STM32F4_SMP4_SHIFT		12
> +#define STM32F4_SMP4_MASK		GENMASK(14, 12)
> +#define STM32F4_SMP3_SHIFT		9
> +#define STM32F4_SMP3_MASK		GENMASK(11, 9)
> +#define STM32F4_SMP2_SHIFT		6
> +#define STM32F4_SMP2_MASK		GENMASK(8, 6)
> +#define STM32F4_SMP1_SHIFT		3
> +#define STM32F4_SMP1_MASK		GENMASK(5, 3)
> +#define STM32F4_SMP0_SHIFT		0
> +#define STM32F4_SMP0_MASK		GENMASK(2, 0)
> +enum stm32f4_adc_smpr {
> +	STM32F4_SMPR_3_CK_CYCLES,
> +	STM32F4_SMPR_15_CK_CYCLES,
> +	STM32F4_SMPR_28_CK_CYCLES,
> +	STM32F4_SMPR_56_CK_CYCLES,
> +	STM32F4_SMPR_84_CK_CYCLES,
> +	STM32F4_SMPR_112_CK_CYCLES,
> +	STM32F4_SMPR_144_CK_CYCLES,
> +	STM32F4_SMPR_480_CK_CYCLES,
> +};
> +
> +/* STM32F4_ADCX_SQR1 - bit fields */
> +#define STM32F4_L_SHIFT			20
> +#define STM32F4_L_MASK			GENMASK(23, 20)
> +#define STM32F4_SQ16_SHIFT		15
> +#define STM32F4_SQ16_MASK		GENMASK(19, 15)
> +#define STM32F4_SQ15_SHIFT		10
> +#define STM32F4_SQ15_MASK		GENMASK(14, 10)
> +#define STM32F4_SQ14_SHIFT		5
> +#define STM32F4_SQ14_MASK		GENMASK(9, 5)
> +#define STM32F4_SQ13_SHIFT		0
> +#define STM32F4_SQ13_MASK		GENMASK(4, 0)
> +
> +/* STM32F4_ADCX_SQR2 - bit fields */
> +#define STM32F4_SQ12_SHIFT		25
> +#define STM32F4_SQ12_MASK		GENMASK(29, 25)
> +#define STM32F4_SQ11_SHIFT		20
> +#define STM32F4_SQ11_MASK		GENMASK(24, 20)
> +#define STM32F4_SQ10_SHIFT		15
> +#define STM32F4_SQ10_MASK		GENMASK(19, 15)
> +#define STM32F4_SQ9_SHIFT		10
> +#define STM32F4_SQ9_MASK		GENMASK(14, 10)
> +#define STM32F4_SQ8_SHIFT		5
> +#define STM32F4_SQ8_MASK		GENMASK(9, 5)
> +#define STM32F4_SQ7_SHIFT		0
> +#define STM32F4_SQ7_MASK		GENMASK(4, 0)
> +
> +/* STM32F4_ADCX_SQR3 - bit fields */
> +#define STM32F4_SQ6_SHIFT		25
> +#define STM32F4_SQ6_MASK		GENMASK(29, 25)
> +#define STM32F4_SQ5_SHIFT		20
> +#define STM32F4_SQ5_MASK		GENMASK(24, 20)
> +#define STM32F4_SQ4_SHIFT		15
> +#define STM32F4_SQ4_MASK		GENMASK(19, 15)
> +#define STM32F4_SQ3_SHIFT		10
> +#define STM32F4_SQ3_MASK		GENMASK(14, 10)
> +#define STM32F4_SQ2_SHIFT		5
> +#define STM32F4_SQ2_MASK		GENMASK(9, 5)
> +#define STM32F4_SQ1_SHIFT		0
> +#define STM32F4_SQ1_MASK		GENMASK(4, 0)
> +
> +/* STM32F4_ADCX_JSQR - bit fields */
> +#define STM32F4_JL_SHIFT		20
> +#define STM32F4_JL_MASK			GENMASK(21, 20)
> +#define STM32F4_JSQ4_SHIFT		15
> +#define STM32F4_JSQ4_MASK		GENMASK(19, 15)
> +#define STM32F4_JSQ3_SHIFT		10
> +#define STM32F4_JSQ3_MASK		GENMASK(14, 10)
> +#define STM32F4_JSQ2_SHIFT		5
> +#define STM32F4_JSQ2_MASK		GENMASK(9, 5)
> +#define STM32F4_JSQ1_SHIFT		0
> +#define STM32F4_JSQ1_MASK		GENMASK(4, 0)
> +
> +/* STM32F4_ADC_CCR - bit fields */
> +#define STM32F4_ADC_ADCPRE_SHIFT	16
> +#define STM32F4_ADC_ADCPRE_MASK		GENMASK(17, 16)
> +
> +/*
> + * stm32 ADC1, ADC2 & ADC3 are tightly coupled and may be used in multi mode
> + * Define here all inputs for all ADC instances
> + */
> +static const struct stm32_adc_chan_spec stm32f4_adc1_channels[] = {
> +	/* master ADC1 */
> +	{ IIO_VOLTAGE, 0, "in0" },
> +	{ IIO_VOLTAGE, 1, "in1" },
> +	{ IIO_VOLTAGE, 2, "in2" },
> +	{ IIO_VOLTAGE, 3, "in3" },
> +	{ IIO_VOLTAGE, 4, "in4" },
> +	{ IIO_VOLTAGE, 5, "in5" },
> +	{ IIO_VOLTAGE, 6, "in6" },
> +	{ IIO_VOLTAGE, 7, "in7" },
> +	{ IIO_VOLTAGE, 8, "in8" },
> +	{ IIO_VOLTAGE, 9, "in9" },
> +	{ IIO_VOLTAGE, 10, "in10" },
> +	{ IIO_VOLTAGE, 11, "in11" },
> +	{ IIO_VOLTAGE, 12, "in12" },
> +	{ IIO_VOLTAGE, 13, "in13" },
> +	{ IIO_VOLTAGE, 14, "in14" },
> +	{ IIO_VOLTAGE, 15, "in15" },
> +	/* internal analog sources available on input 16 to 18 */
> +	{ IIO_VOLTAGE, 16, "in16" },
> +	{ IIO_VOLTAGE, 17, "in17" },
> +	{ IIO_VOLTAGE, 18, "in18" },
> +};
> +
> +static const struct stm32_adc_chan_spec stm32f4_adc23_channels[] = {
> +	/* slave ADC2 /	ADC3 */
> +	{ IIO_VOLTAGE, 0, "in0" },
> +	{ IIO_VOLTAGE, 1, "in1" },
> +	{ IIO_VOLTAGE, 2, "in2" },
> +	{ IIO_VOLTAGE, 3, "in3" },
> +	{ IIO_VOLTAGE, 4, "in4" },
> +	{ IIO_VOLTAGE, 5, "in5" },
> +	{ IIO_VOLTAGE, 6, "in6" },
> +	{ IIO_VOLTAGE, 7, "in7" },
> +	{ IIO_VOLTAGE, 8, "in8" },
> +	{ IIO_VOLTAGE, 9, "in9" },
> +	{ IIO_VOLTAGE, 10, "in10" },
> +	{ IIO_VOLTAGE, 11, "in11" },
> +	{ IIO_VOLTAGE, 12, "in12" },
> +	{ IIO_VOLTAGE, 13, "in13" },
> +	{ IIO_VOLTAGE, 14, "in14" },
> +	{ IIO_VOLTAGE, 15, "in15" },
> +};
> +
> +/* Triggers for regular channels */
> +static const struct stm32_adc_trig_info stm32f4_adc_ext_triggers[] = {
> +	{ STM32_EXT0, "TIM1_CH1" },
> +	{ STM32_EXT1, "TIM1_CH2" },
> +	{ STM32_EXT2, "TIM1_CH3" },
> +	{ STM32_EXT3, "TIM2_CH2" },
> +	{ STM32_EXT4, "TIM2_CH3" },
> +	{ STM32_EXT5, "TIM2_CH4" },
> +	{ STM32_EXT6, "TIM2_TRGO" },
> +	{ STM32_EXT7, "TIM3_CH1" },
> +	{ STM32_EXT8, "TIM3_TRGO" },
> +	{ STM32_EXT9, "TIM4_CH4" },
> +	{ STM32_EXT10, "TIM5_CH1" },
> +	{ STM32_EXT11, "TIM5_CH2" },
> +	{ STM32_EXT12, "TIM5_CH3" },
> +	{ STM32_EXT13, "TIM8_CH1" },
> +	{ STM32_EXT14, "TIM8_TRGO" },
> +	{ STM32_EXT15, "EXTI_11" },
> +	{},
> +};
> +
> +/* Triggers for injected channels */
> +static const struct stm32_adc_trig_info  stm32f4_adc_jext_triggers[] = {
> +	{ STM32_JEXT0, "TIM1_CH4" },
> +	{ STM32_JEXT1, "TIM1_TRGO" },
> +	{ STM32_JEXT2, "TIM2_CH1" },
> +	{ STM32_JEXT3, "TIM2_TRGO" },
> +	{ STM32_JEXT4, "TIM3_CH2" },
> +	{ STM32_JEXT5, "TIM3_CH4" },
> +	{ STM32_JEXT6, "TIM4_CH1" },
> +	{ STM32_JEXT7, "TIM4_CH2" },
> +	{ STM32_JEXT8, "TIM4_CH3" },
> +	{ STM32_JEXT9, "TIM4_TRGO" },
> +	{ STM32_JEXT10, "TIM5_CH4" },
> +	{ STM32_JEXT11, "TIM5_TRGO" },
> +	{ STM32_JEXT12, "TIM8_CH2" },
> +	{ STM32_JEXT13, "TIM8_CH3" },
> +	{ STM32_JEXT14, "TIM8_CH4" },
> +	{ STM32_JEXT15, "EXTI_15" },
> +	{},
> +};
> +
> +static const struct stm32_adc_info stm32f4_adc_info[] = {
> +	{
> +		.name = "adc1-master",
> +		.reg = 0x0,
> +		.channels = stm32f4_adc1_channels,
> +		.max_channels = ARRAY_SIZE(stm32f4_adc1_channels),
> +	},
> +	{
> +		.name = "adc2-slave",
> +		.reg = 0x100,
> +		.channels = stm32f4_adc23_channels,
> +		.max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
> +	},
> +	{
> +		.name = "adc3-slave",
> +		.reg = 0x200,
> +		.channels = stm32f4_adc23_channels,
> +		.max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
> +	},
> +	{},
> +};
> +
> +/**
> + * stm32f4_sqr_regs - describe regular sequence registers
> + * - L: sequence len (register & bit field)
> + * - SQ1..SQ16: sequence entries (register & bit field)
> + */
> +static const struct stm32_adc_regs stm32f4_sqr_regs[STM32_ADC_MAX_SQ + 1] = {
> +	/* L: len bit field description to be kept as first element */
> +	{ STM32F4_ADCX_SQR1, STM32F4_L_MASK, STM32F4_L_SHIFT },
> +	/* SQ1..SQ16 registers & bit fields */
> +	{ STM32F4_ADCX_SQR3, STM32F4_SQ1_MASK, STM32F4_SQ1_SHIFT },
> +	{ STM32F4_ADCX_SQR3, STM32F4_SQ2_MASK, STM32F4_SQ2_SHIFT },
> +	{ STM32F4_ADCX_SQR3, STM32F4_SQ3_MASK, STM32F4_SQ3_SHIFT },
> +	{ STM32F4_ADCX_SQR3, STM32F4_SQ4_MASK, STM32F4_SQ4_SHIFT },
> +	{ STM32F4_ADCX_SQR3, STM32F4_SQ5_MASK, STM32F4_SQ5_SHIFT },
> +	{ STM32F4_ADCX_SQR3, STM32F4_SQ6_MASK, STM32F4_SQ6_SHIFT },
> +	{ STM32F4_ADCX_SQR2, STM32F4_SQ7_MASK, STM32F4_SQ7_SHIFT },
> +	{ STM32F4_ADCX_SQR2, STM32F4_SQ8_MASK, STM32F4_SQ8_SHIFT },
> +	{ STM32F4_ADCX_SQR2, STM32F4_SQ9_MASK, STM32F4_SQ9_SHIFT },
> +	{ STM32F4_ADCX_SQR2, STM32F4_SQ10_MASK, STM32F4_SQ10_SHIFT },
> +	{ STM32F4_ADCX_SQR2, STM32F4_SQ11_MASK, STM32F4_SQ11_SHIFT },
> +	{ STM32F4_ADCX_SQR2, STM32F4_SQ12_MASK, STM32F4_SQ12_SHIFT },
> +	{ STM32F4_ADCX_SQR1, STM32F4_SQ13_MASK, STM32F4_SQ13_SHIFT },
> +	{ STM32F4_ADCX_SQR1, STM32F4_SQ14_MASK, STM32F4_SQ14_SHIFT },
> +	{ STM32F4_ADCX_SQR1, STM32F4_SQ15_MASK, STM32F4_SQ15_SHIFT },
> +	{ STM32F4_ADCX_SQR1, STM32F4_SQ16_MASK, STM32F4_SQ16_SHIFT },
> +};
> +
> +/**
> + * stm32f4_jsqr_reg - describe injected sequence register:
> + * - JL: injected sequence len
> + * - JSQ4..SQ1: sequence entries
> + * When JL == 3, ADC converts JSQ1, JSQ2, JSQ3, JSQ4
> + * When JL == 2, ADC converts JSQ2, JSQ3, JSQ4
> + * When JL == 1, ADC converts JSQ3, JSQ4
> + * When JL == 0, ADC converts JSQ4
> + */
> +static const struct stm32_adc_regs stm32f4_jsqr_reg[STM32_ADC_MAX_JSQ + 1] = {
> +	/* JL: len bit field description to be kept as first element */
> +	{STM32F4_ADCX_JSQR, STM32F4_JL_MASK, STM32F4_JL_SHIFT},
> +	/* JSQ4..JSQ1 registers & bit fields */
> +	{STM32F4_ADCX_JSQR, STM32F4_JSQ4_MASK, STM32F4_JSQ4_SHIFT},
> +	{STM32F4_ADCX_JSQR, STM32F4_JSQ3_MASK, STM32F4_JSQ3_SHIFT},
> +	{STM32F4_ADCX_JSQR, STM32F4_JSQ2_MASK, STM32F4_JSQ2_SHIFT},
> +	{STM32F4_ADCX_JSQR, STM32F4_JSQ1_MASK, STM32F4_JSQ1_SHIFT},
> +};
> +
> +static const struct stm32_adc_trig_reginfo stm32f4_adc_trig_reginfo = {
> +	.reg = STM32F4_ADCX_CR2,
> +	.exten_mask = STM32F4_EXTEN_MASK,
> +	.exten_shift = STM32F4_EXTEN_SHIFT,
> +	.extsel_mask = STM32F4_EXTSEL_MASK,
> +	.extsel_shift = STM32F4_EXTSEL_SHIFT,
> +};
> +
> +static const struct stm32_adc_trig_reginfo stm32f4_adc_jtrig_reginfo = {
> +	.reg = STM32F4_ADCX_CR2,
> +	.exten_mask = STM32F4_JEXTEN_MASK,
> +	.exten_shift = STM32F4_JEXTEN_SHIFT,
> +	.extsel_mask = STM32F4_JEXTSEL_MASK,
> +	.extsel_shift = STM32F4_JEXTSEL_SHIFT,
> +};
> +
> +static const struct stm32_adc_reginfo stm32f4_adc_reginfo = {
> +	.isr = STM32F4_ADCX_SR,
> +	.eoc = STM32F4_EOC,
> +	.jeoc = STM32F4_JEOC,
> +	.ier = STM32F4_ADCX_CR1,
> +	.eocie = STM32F4_EOCIE,
> +	.jeocie = STM32F4_JEOCIE,
> +	.dr = STM32F4_ADCX_DR,
> +	.jdr = {
> +		STM32F4_ADCX_JDR1,
> +		STM32F4_ADCX_JDR2,
> +		STM32F4_ADCX_JDR3,
> +		STM32F4_ADCX_JDR4,
> +	},
> +	.sqr_regs = stm32f4_sqr_regs,
> +	.jsqr_reg = stm32f4_jsqr_reg,
> +	.trig_reginfo = &stm32f4_adc_trig_reginfo,
> +	.jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
> +};
> +
> +static bool stm32f4_adc_is_started(struct stm32_adc *adc)
> +{
> +	u32 val = stm32_adc_readl(adc, STM32F4_ADCX_CR2) & STM32F4_ADON;
> +
> +	return !!val;
> +}
> +
> +static bool stm32f4_adc_regular_started(struct stm32_adc *adc)
> +{
> +	u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_STRT;
> +
> +	return !!val;
> +}
> +
> +static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
> +{
> +	u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_JSTRT;
> +
> +	return !!val;
> +}
> +
> +/**
> + * stm32f4_adc_start_conv() - Start regular or injected conversions
> + * @adc: stm32 adc instance
> + *
> + * Start single conversions for regular or injected channels.
> + */
> +static int stm32f4_adc_start_conv(struct stm32_adc *adc)
> +{
> +	u32 trig_msk, start_msk;
> +
> +	dev_dbg(adc->common->dev, "%s %s\n", __func__,
> +		adc->injected ? "injected" : "regular");
> +
> +	stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
> +
> +	if (!stm32f4_adc_is_started(adc)) {
> +		stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
> +				   STM32F4_EOCS | STM32F4_ADON);
> +
> +		/* Wait for Power-up time (tSTAB from datasheet) */
> +		usleep_range(2, 3);
> +	}
> +
> +	if (adc->injected) {
> +		trig_msk = STM32F4_JEXTEN_MASK;
> +		start_msk = STM32F4_JSWSTART;
> +	} else {
> +		trig_msk = STM32F4_EXTEN_MASK;
> +		start_msk = STM32F4_SWSTART;
> +	}
> +
> +	/* Software start ? (e.g. trigger detection disabled ?) */
> +	if (!(stm32_adc_readl(adc, STM32F4_ADCX_CR2) & trig_msk))
> +		stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, start_msk);
> +
> +	return 0;
> +}
> +
> +static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
> +{
> +	u32 val;
> +
> +	dev_dbg(adc->common->dev, "%s %s\n", __func__,
> +		adc->injected ? "injected" : "regular");
> +
> +	/* First disable trigger for either regular or injected channels */
> +	if (adc->injected) {
> +		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_JEXTEN_MASK);
> +		stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_JSTRT);
> +	} else {
> +		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
> +		stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_STRT);
> +	}
> +
> +	/* Disable adc when all triggered conversion have been disabled */
> +	val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
> +	val &= STM32F4_EXTEN_MASK | STM32F4_JEXTEN_MASK;
> +	if (!val) {
> +		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
> +		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
> +	}
> +
> +	return 0;
> +}
> +
> +/* ADC internal common clock prescaler division ratios */
> +static int stm32f4_pclk_div[] = {2, 4, 6, 8};
> +
> +/**
> + * stm32f4_adc_clk_sel() - Select ADC common clock prescaler
> + * @adc: stm32 adc instance
> + * Select clock prescaler used for analog conversions.
> + */
> +static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
> +{
> +	struct stm32_adc_common *common = adc->common;
> +	unsigned long rate;
> +	u32 val;
> +	int i;
> +
> +	/* Common prescaler is set only once, when 1st ADC instance starts */
> +	list_for_each_entry(adc, &common->adc_list, adc_list)
> +		if (stm32f4_adc_is_started(adc))
> +			return 0;
> +
> +	rate = clk_get_rate(common->aclk);
> +	for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
> +		if ((rate / stm32f4_pclk_div[i]) <=
> +		    common->data->max_clock_rate)
> +			break;
> +	}
> +	if (i >= ARRAY_SIZE(stm32f4_pclk_div))
> +		return -EINVAL;
> +
> +	val = stm32_adc_common_readl(common, STM32F4_ADC_CCR);
> +	val &= ~STM32F4_ADC_ADCPRE_MASK;
> +	val |= i << STM32F4_ADC_ADCPRE_SHIFT;
> +	stm32_adc_common_writel(common, STM32F4_ADC_CCR, val);
> +
> +	dev_dbg(common->dev, "Using analog clock source at %ld kHz\n",
> +		rate / (stm32f4_pclk_div[i] * 1000));
> +
> +	return 0;
> +}
> +
> +static const struct stm32_adc_ops stm32f4_adc_ops = {
> +	.adc_info = stm32f4_adc_info,
> +	.ext_triggers = stm32f4_adc_ext_triggers,
> +	.jext_triggers = stm32f4_adc_jext_triggers,
> +	.adc_reginfo = &stm32f4_adc_reginfo,
> +	.highres = 12,
> +	.max_clock_rate = 36000000,
> +	.clk_sel = stm32f4_adc_clk_sel,
> +	.start_conv = stm32f4_adc_start_conv,
> +	.stop_conv = stm32f4_adc_stop_conv,
> +	.is_started = stm32f4_adc_is_started,
> +	.regular_started = stm32f4_adc_regular_started,
> +	.injected_started = stm32f4_adc_injected_started,
> +};
> +
> +static const struct of_device_id stm32f4_adc_of_match[] = {
> +	{ .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_ops},
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, stm32f4_adc_of_match);
> +
> +static struct platform_driver stm32f4_adc_driver = {
> +	.probe = stm32_adc_probe,
> +	.remove = stm32_adc_remove,
> +	.driver = {
> +		.name = "stm32f4-adc",
> +		.of_match_table = stm32f4_adc_of_match,
> +	},
> +};
> +
> +module_platform_driver(stm32f4_adc_driver);
> +
> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics STM32F4 ADC driver");
> +MODULE_LICENSE("GPL v2");
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 03/10] iio: adc: stm32: add optional dma support
  2016-10-25 16:25 ` [PATCH 03/10] iio: adc: stm32: add optional dma support Fabrice Gasnier
@ 2016-10-30 15:34   ` Jonathan Cameron
  0 siblings, 0 replies; 20+ messages in thread
From: Jonathan Cameron @ 2016-10-30 15:34 UTC (permalink / raw)
  To: Fabrice Gasnier, linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: linux, robh+dt, mark.rutland, mcoquelin.stm32, alexandre.torgue,
	lars, knaack.h, pmeerw

On 25/10/16 17:25, Fabrice Gasnier wrote:
> Add optional DMA support to STM32 ADC.
> Use dma cyclic mode with at least two periods.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Few little bits inline, but looks superficially good (my dma knowledge isn't
really up to reviewing this.) Lars can you take a look (perhaps at the next
version)?

Also, you 'could' potentially use the dma buffer stuff to give a lower overhead
route for the data to userspace if the device goes quick enough to warrant
it.

Jonathan
> ---
>  drivers/iio/adc/stm32/Kconfig       |   2 +
>  drivers/iio/adc/stm32/stm32-adc.c   | 222 ++++++++++++++++++++++++++++++++----
>  drivers/iio/adc/stm32/stm32-adc.h   |  15 +++
>  drivers/iio/adc/stm32/stm32f4-adc.c |  20 +++-
>  4 files changed, 235 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
> index 245d037..5554ac8 100644
> --- a/drivers/iio/adc/stm32/Kconfig
> +++ b/drivers/iio/adc/stm32/Kconfig
> @@ -8,6 +8,7 @@ config STM32_ADC
>  	select REGULATOR_FIXED_VOLTAGE
>  	select IIO_BUFFER
>  	select IIO_TRIGGERED_BUFFER
> +	select IRQ_WORK
>  	help
>  	  Say yes here to build the driver for the STMicroelectronics
>  	  STM32 analog-to-digital converter (ADC).
> @@ -18,6 +19,7 @@ config STM32_ADC
>  config STM32F4_ADC
>  	tristate "STMicroelectronics STM32F4 adc"
>  	depends on ARCH_STM32 || COMPILE_TEST
> +	depends on HAS_DMA
>  	depends on OF
>  	select STM32_ADC
>  	help
> diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
> index 1e0850d..25d0307 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.c
> +++ b/drivers/iio/adc/stm32/stm32-adc.c
> @@ -21,6 +21,7 @@
>  
>  #include <linux/clk.h>
>  #include <linux/debugfs.h>
> +#include <linux/dma-mapping.h>
>  #include <linux/iio/iio.h>
>  #include <linux/iio/buffer.h>
>  #include <linux/iio/events.h>
> @@ -371,6 +372,34 @@ static int stm32_adc_read_raw(struct iio_dev *indio_dev,
>  	return ret;
>  }
>  
> +static int stm32_adc_residue(struct stm32_adc *adc)
> +{
> +	struct dma_tx_state state;
> +	enum dma_status status;
> +
> +	if (!adc->rx_buf)
> +		return 0;
> +
> +	status = dmaengine_tx_status(adc->dma_chan,
> +				     adc->dma_chan->cookie,
> +				     &state);
> +	if (status == DMA_IN_PROGRESS) {
> +		/* Residue is size in bytes from end of buffer */
> +		int i = adc->rx_buf_sz - state.residue;
> +		int size;
> +
> +		/* Return available bytes */
> +		if (i >= adc->bufi)
> +			size = i - adc->bufi;
> +		else
> +			size = adc->rx_buf_sz - adc->bufi + i;
> +
> +		return size;
> +	}
> +
> +	return 0;
> +}
> +
>  /**
>   * stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
>   */
> @@ -394,8 +423,8 @@ static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
>  	stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
>  	status &= mask;
>  
> -	/* Regular data */
> -	if (status & reginfo->eoc) {
> +	/* Regular data (when dma isn't used) */
> +	if ((status & reginfo->eoc) && (!adc->rx_buf)) {
>  		adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
>  		if (iio_buffer_enabled(indio_dev)) {
>  			adc->bufi++;
> @@ -553,29 +582,154 @@ static int stm32_adc_validate_device(struct iio_trigger *trig,
>  	return indio != indio_dev ? -EINVAL : 0;
>  }
>  
> -static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
> -				       bool state)
> +static void stm32_adc_dma_irq_work(struct irq_work *work)
>  {
> -	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
> +	struct stm32_adc *adc = container_of(work, struct stm32_adc, work);
> +	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +
> +	/**
> +	 * iio_trigger_poll calls generic_handle_irq(). So, it requires hard
> +	 * irq context, and cannot be called directly from dma callback,
> +	 * dma cb has to schedule this work instead.
> +	 */
> +	iio_trigger_poll(indio_dev->trig);
> +}
> +
> +static void stm32_adc_dma_buffer_done(void *data)
> +{
> +	struct iio_dev *indio_dev = data;
>  	struct stm32_adc *adc = iio_priv(indio_dev);
> -	int ret;
>  
> -	if (state) {
> -		/* Reset adc buffer index */
> -		adc->bufi = 0;
> +	/* invoques iio_trigger_poll() from hard irq context */
invokes.
> +	irq_work_queue(&adc->work);
> +}
> +
> +static int stm32_adc_buffer_alloc_dma_start(struct iio_dev *indio_dev)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	const struct stm32_adc_reginfo *reginfo =
> +		adc->common->data->adc_reginfo;
> +	struct dma_async_tx_descriptor *desc;
> +	struct dma_slave_config config;
> +	dma_cookie_t cookie;
> +	int ret, size, watermark;
>  
> +	/* Reset adc buffer index */
> +	adc->bufi = 0;
> +
> +	if (!adc->dma_chan) {
>  		/* Allocate adc buffer */
>  		adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
>  		if (!adc->buffer)
>  			return -ENOMEM;
>  
> +		return 0;
> +	}
> +
> +	/*
> +	 * Allocate at least twice the buffer size for dma cyclic transfers, so
> +	 * we can work with at least two dma periods. There should be :
> +	 * - always one buffer (period) dma is working on
> +	 * - one buffer (period) driver can push with iio_trigger_poll().
> +	 */
> +	size = indio_dev->buffer->bytes_per_datum * indio_dev->buffer->length;
> +	size = max(indio_dev->scan_bytes * 2, size);
> +
> +	adc->rx_buf = dma_alloc_coherent(adc->common->dev, PAGE_ALIGN(size),
> +					 &adc->rx_dma_buf,
> +					 GFP_KERNEL);
> +	if (!adc->rx_buf)
> +		return -ENOMEM;
> +	adc->rx_buf_sz = size;
> +	watermark = indio_dev->buffer->bytes_per_datum
> +		* indio_dev->buffer->watermark;
> +	watermark = max(indio_dev->scan_bytes, watermark);
> +	watermark = rounddown(watermark, indio_dev->scan_bytes);
> +
> +	dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__, size,
> +		watermark);
> +
> +	/* Configure DMA channel to read data register */
> +	memset(&config, 0, sizeof(config));
> +	config.src_addr = (dma_addr_t)adc->common->phys_base;
> +	config.src_addr += adc->offset + reginfo->dr;
> +	config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
> +
> +	ret = dmaengine_slave_config(adc->dma_chan, &config);
> +	if (ret)
> +		goto config_err;
> +
> +	/* Prepare a DMA cyclic transaction */
> +	desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
> +					 adc->rx_dma_buf,
> +					 size, watermark,
> +					 DMA_DEV_TO_MEM,
> +					 DMA_PREP_INTERRUPT);
> +	if (!desc) {
> +		ret = -ENODEV;
> +		goto config_err;
> +	}
> +
> +	desc->callback = stm32_adc_dma_buffer_done;
> +	desc->callback_param = indio_dev;
> +
> +	cookie = dmaengine_submit(desc);
> +	if (dma_submit_error(cookie)) {
> +		ret = dma_submit_error(cookie);
> +		goto config_err;
> +	}
> +
> +	/* Issue pending DMA requests */
> +	dma_async_issue_pending(adc->dma_chan);
> +
> +	return 0;
> +
> +config_err:
> +	dma_free_coherent(adc->common->dev, PAGE_ALIGN(size), adc->rx_buf,
> +			  adc->rx_dma_buf);
> +
> +	return ret;
> +}
> +
> +static void stm32_adc_dma_stop_buffer_free(struct iio_dev *indio_dev)
> +{
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +
> +	if (!adc->dma_chan) {
> +		kfree(adc->buffer);
> +	} else {
> +		dmaengine_terminate_all(adc->dma_chan);
> +		irq_work_sync(&adc->work);
> +		dma_free_coherent(adc->common->dev, PAGE_ALIGN(adc->rx_buf_sz),
> +				  adc->rx_buf, adc->rx_dma_buf);
> +		adc->rx_buf = NULL;
> +	}
> +
> +	adc->buffer = NULL;
> +}
> +
> +static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
> +				       bool state)
> +{
> +	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	int ret;
> +
> +	if (state) {
> +		ret = stm32_adc_buffer_alloc_dma_start(indio_dev);
> +		if (ret) {
> +			dev_err(&indio_dev->dev, "alloc failed\n");
> +			return ret;
> +		}
> +
>  		ret = stm32_adc_set_trig(indio_dev, trig);
>  		if (ret) {
>  			dev_err(&indio_dev->dev, "Can't set trigger\n");
>  			goto err_buffer_free;
>  		}
>  
> -		stm32_adc_conv_irq_enable(adc);
> +		if (!adc->dma_chan)
> +			stm32_adc_conv_irq_enable(adc);
>  
>  		ret = stm32_adc_start_conv(adc);
>  		if (ret) {
> @@ -589,25 +743,25 @@ static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
>  			return ret;
>  		}
>  
> -		stm32_adc_conv_irq_disable(adc);
> +		if (!adc->dma_chan)
> +			stm32_adc_conv_irq_disable(adc);
>  
>  		ret = stm32_adc_set_trig(indio_dev, NULL);
>  		if (ret)
>  			dev_warn(&indio_dev->dev, "Can't clear trigger\n");
>  
> -		kfree(adc->buffer);
> -		adc->buffer = NULL;
> +		stm32_adc_dma_stop_buffer_free(indio_dev);
>  	}
>  
>  	return 0;
>  
>  err_irq_trig_disable:
> -	stm32_adc_conv_irq_disable(adc);
> +	if (!adc->dma_chan)
> +		stm32_adc_conv_irq_disable(adc);
>  	stm32_adc_set_trig(indio_dev, NULL);
>  
>  err_buffer_free:
> -	kfree(adc->buffer);
> -	adc->buffer = NULL;
> +	stm32_adc_dma_stop_buffer_free(indio_dev);
>  
>  	return ret;
>  }
> @@ -624,17 +778,30 @@ static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
>  	struct iio_dev *indio_dev = pf->indio_dev;
>  	struct stm32_adc *adc = iio_priv(indio_dev);
>  
> -	dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
> -
> -	/* reset buffer index */
> -	adc->bufi = 0;
> -	iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
> -					   pf->timestamp);
> +	if (!adc->dma_chan) {
> +		adc->bufi = 0;
> +		iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
> +						   pf->timestamp);
> +	} else {
> +		int residue = stm32_adc_residue(adc);
> +
This bit wants some explanatory comments I think.
> +		while (residue >= indio_dev->scan_bytes) {
> +			adc->buffer = (u16 *)&adc->rx_buf[adc->bufi];
> +			iio_push_to_buffers_with_timestamp(indio_dev,
> +							   adc->buffer,
> +							   pf->timestamp);
> +			residue -= indio_dev->scan_bytes;
> +			adc->bufi += indio_dev->scan_bytes;
> +			if (adc->bufi >= adc->rx_buf_sz)
> +				adc->bufi = 0;
> +		}
> +	}
>  
>  	iio_trigger_notify_done(indio_dev->trig);
>  
>  	/* re-enable eoc irq */
> -	stm32_adc_conv_irq_enable(adc);
> +	if (!adc->dma_chan)
> +		stm32_adc_conv_irq_enable(adc);
>  
>  	return IRQ_HANDLED;
>  }
> @@ -827,6 +994,10 @@ static int stm32_adc_register(struct stm32_adc_common *common,
>  	if (ret)
>  		goto err_clk_disable;
>  
> +	adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
> +	if (adc->dma_chan)
> +		init_irq_work(&adc->work, stm32_adc_dma_irq_work);
> +
>  	ret = iio_triggered_buffer_setup(indio_dev,
>  					 &iio_pollfunc_store_time,
>  					 &stm32_adc_trigger_handler,
> @@ -850,6 +1021,8 @@ static int stm32_adc_register(struct stm32_adc_common *common,
>  	iio_triggered_buffer_cleanup(indio_dev);
>  
>  err_trig_unregister:
> +	if (adc->dma_chan)
> +		dma_release_channel(adc->dma_chan);
>  	stm32_adc_trig_unregister(indio_dev);
>  
>  err_clk_disable:
> @@ -870,6 +1043,8 @@ static void stm32_adc_unregister(struct stm32_adc *adc)
>  	iio_device_unregister(indio_dev);
>  	iio_triggered_buffer_cleanup(indio_dev);
>  	stm32_adc_trig_unregister(indio_dev);
> +	if (adc->dma_chan)
> +		dma_release_channel(adc->dma_chan);
>  	if (adc->clk) {
>  		clk_disable_unprepare(adc->clk);
>  		clk_put(adc->clk);
> @@ -900,6 +1075,7 @@ int stm32_adc_probe(struct platform_device *pdev)
>  	common->base = devm_ioremap_resource(&pdev->dev, res);
>  	if (IS_ERR(common->base))
>  		return PTR_ERR(common->base);
> +	common->phys_base = res->start;
>  
>  	common->data = match->data;
>  	common->dev = &pdev->dev;
> diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
> index 0be603c..01a0dda 100644
> --- a/drivers/iio/adc/stm32/stm32-adc.h
> +++ b/drivers/iio/adc/stm32/stm32-adc.h
> @@ -22,6 +22,9 @@
>  #ifndef __STM32_ADC_H
>  #define __STM32_ADC_H
>  
> +#include <linux/dmaengine.h>
> +#include <linux/irq_work.h>
> +
>  /*
>   * STM32 - ADC global register map
>   * ________________________________________________________
> @@ -276,6 +279,11 @@ struct stm32_adc_ops {
>   * @num_conv:		expected number of scan conversions
>   * @injected:		use injected channels on this adc
>   * @lock:		spinlock
> + * @work:		irq work used to call trigger poll routine
> + * @dma_chan:		dma channel
> + * @rx_buf:		dma rx buffer cpu address
> + * @rx_dma_buf:		dma rx buffer bus address
> + * @rx_buf_sz:		dma rx buffer size
>   * @clk:		optional adc clock, for this adc instance
>   * @calib:		optional calibration data
>   * @en:			emulates enabled state on some stm32 adc
> @@ -293,6 +301,11 @@ struct stm32_adc {
>  	int			num_conv;
>  	bool			injected;
>  	spinlock_t		lock;		/* interrupt lock */
> +	struct irq_work		work;
> +	struct dma_chan		*dma_chan;
> +	u8			*rx_buf;
> +	dma_addr_t		rx_dma_buf;
> +	int			rx_buf_sz;
>  	struct clk		*clk;
>  	void			*calib;
>  	bool			en;
> @@ -302,6 +315,7 @@ struct stm32_adc {
>   * struct stm32_adc_common - private data of ADC driver, common to all
>   * ADC instances (ADC block)
>   * @dev:		device for this controller
> + * @phys_base:		control registers base physical addr
>   * @base:		control registers base cpu addr
>   * @irq:		Common irq line for all adc instances
>   * @data:		STM32 dependent data from compatible
> @@ -313,6 +327,7 @@ struct stm32_adc {
>   */
>  struct stm32_adc_common {
>  	struct device			*dev;
> +	phys_addr_t			phys_base;
>  	void __iomem			*base;
>  	int				irq;
>  	const struct stm32_adc_ops	*data;
> diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
> index 147fe9c..4d7a2a8 100644
> --- a/drivers/iio/adc/stm32/stm32f4-adc.c
> +++ b/drivers/iio/adc/stm32/stm32f4-adc.c
> @@ -437,16 +437,30 @@ static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
>   * @adc: stm32 adc instance
>   *
>   * Start single conversions for regular or injected channels.
> + * Also take care of normal or DMA mode. DMA is used in circular mode for
> + * regular conversions, in IIO buffer modes. Rely on rx_buf as raw
> + * read doesn't use dma, but direct DR read.
>   */
>  static int stm32f4_adc_start_conv(struct stm32_adc *adc)
>  {
> -	u32 trig_msk, start_msk;
> +	u32 val, trig_msk, start_msk;
> +	unsigned long flags;
>  
>  	dev_dbg(adc->common->dev, "%s %s\n", __func__,
>  		adc->injected ? "injected" : "regular");
>  
>  	stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
>  
> +	if (!adc->injected) {
> +		spin_lock_irqsave(&adc->lock, flags);
> +		val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
> +		val &= ~(STM32F4_DMA | STM32F4_DDS);
> +		if (adc->rx_buf)
> +			val |= STM32F4_DMA | STM32F4_DDS;
> +		stm32_adc_writel(adc, STM32F4_ADCX_CR2, val);
> +		spin_unlock_irqrestore(&adc->lock, flags);
> +	}
> +
>  	if (!stm32f4_adc_is_started(adc)) {
>  		stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
>  				   STM32F4_EOCS | STM32F4_ADON);
> @@ -494,6 +508,10 @@ static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
>  		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
>  	}
>  
> +	if (!adc->injected)
> +		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2,
> +				   STM32F4_DMA | STM32F4_DDS);
> +
>  	return 0;
>  }
>  
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 01/10] Documentation: dt-bindings: Document STM32 ADC DT bindings
  2016-10-25 16:25 ` [PATCH 01/10] Documentation: dt-bindings: Document STM32 ADC DT bindings Fabrice Gasnier
@ 2016-10-31  3:02   ` Rob Herring
  2016-11-03 11:11     ` Fabrice Gasnier
  0 siblings, 1 reply; 20+ messages in thread
From: Rob Herring @ 2016-10-31  3:02 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: linux-iio, linux-arm-kernel, devicetree, linux-kernel, jic23,
	linux, mark.rutland, mcoquelin.stm32, alexandre.torgue, lars,
	knaack.h, pmeerw

On Tue, Oct 25, 2016 at 06:25:13PM +0200, Fabrice Gasnier wrote:
> This patch adds documentation of device tree bindings for the STM32 ADC.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> ---
>  .../devicetree/bindings/iio/adc/st,stm32-adc.txt   | 78 ++++++++++++++++++++++
>  1 file changed, 78 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
> new file mode 100644
> index 0000000..a9a8b3c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
> @@ -0,0 +1,78 @@
> +STMicroelectronics STM32 ADC device driver
> +
> +STM32 ADC is a successive approximation analog-to-digital converter.
> +It has several multiplexed input channels. Conversions can be performed
> +in single, continuous, scan or discontinuous mode. Result of the ADC is
> +stored in a left-aligned or right-aligned 32-bit data register.
> +Conversions can be launched in software or using hardware triggers.
> +
> +The analog watchdog feature allows the application to detect if the input
> +voltage goes beyond the user-defined, higher or lower thresholds.
> +
> +Each STM32 ADC block can have up to 3 ADC instances.
> +
> +Each instance supports two contexts to manage conversions, each one has its
> +own configurable sequence and trigger:
> +- regular conversion can be done in sequence, running in background
> +- injected conversions have higher priority, and so have the ability to
> +  interrupt regular conversion sequence (either triggered in SW or HW).
> +  Regular sequence is resumed, in case it has been interrupted.
> +
> +Required properties:
> +- compatible: Should be "st,stm32f4-adc".
> +- reg: Offset and length of the ADC block register set.
> +- interrupts: Must contain the interrupt for ADC.
> +- clocks: Clock for the analog circuitry (common to all ADCs).
> +- clock-names: Must be "adc".
> +- vref-supply: Phandle to the vref input analog reference voltage.
> +- #address-cells = <1>;
> +- #size-cells = <0>;
> +
> +Optional properties:
> +- A pinctrl state named "default" for each ADC channel may be defined to set
> +  inX ADC pins in mode of operation for analog input on external pin.
> +- gpios: Array of gpios that may be configured as EXTi trigger sources.
> +
> +Example:

This should be last.

> +	adc: adc@40012000 {
> +		compatible = "st,stm32f4-adc";
> +		reg = <0x40012000 0x400>;
> +		interrupts = <18>;
> +		clocks = <&rcc 0 168>;
> +		clock-names = "adc";
> +		vref-supply = <&reg_vref>;
> +		pinctrl-names = "default";
> +		pinctrl-0 = <&adc3_in8_pin>;
> +		gpios = <&gpioa 11 0>,
> +			<&gpioa 15 0>;
> +
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		adc1: adc1-master@0 {

adc@0 sufficient?

> +			#io-channel-cells = <1>;
> +			reg = <0x0>;
> +			clocks = <&rcc 0 168>;
> +			st,adc-channels = <8>;
> +		};
> +		...
> +		other adc child nodes follow...
> +	};
> +
> +Contents of a stm32 adc child node:
> +-----------------------------------
> +An ADC block node should contain at least one subnode, representing an
> +ADC instance available on the machine.
> +
> +Required properties:
> +- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
> +- st,adc-channels: List of single-ended channels muxed for this ADC.

How many? What are valid values?

> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
> +  Documentation/devicetree/bindings/iio/iio-bindings.txt
> +
> +Optional properties:
> +- clocks: Input clock private to this ADC instance.
> +- st,injected: Use injected conversion sequence on an ADC, rather than regular.

Not sure about this one. Seems like this would either be a user choice 
or depend on what's connected to the ADC.

> +- dmas: Phandle to dma channel for this ADC instance, only for regular
> +  conversions. See ../../dma/dma.txt for details.
> +- dma-names: Must be "rx" when dmas property is being used.
> -- 
> 1.9.1
> 

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 02/10] iio: adc: Add stm32 support
  2016-10-30 15:27   ` Jonathan Cameron
@ 2016-11-03  8:20     ` Fabrice Gasnier
  2016-11-05 15:44       ` Jonathan Cameron
  0 siblings, 1 reply; 20+ messages in thread
From: Fabrice Gasnier @ 2016-11-03  8:20 UTC (permalink / raw)
  To: Jonathan Cameron, linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: linux, robh+dt, mark.rutland, mcoquelin.stm32, alexandre.torgue,
	lars, knaack.h, pmeerw, Benjamin GAIGNARD

On 10/30/2016 04:27 PM, Jonathan Cameron wrote:
> On 25/10/16 17:25, Fabrice Gasnier wrote:
>> This patch adds support for STMicroelectronics STM32 MCU's analog to
>> digital converter.
>>
>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> Hi Fabrice,
>
> Sometimes I hate SoC ADCs.  For some reason the hardware designers seem to
> try and throw everything and the kitchen sink at them.  Discontinuous mode
> as an example in this device.  Not seen that particular piece of fun before
> and glad to see you haven't 'yet' tried to support it!
>
> Anyhow, the complexity of the hardware leads to an initially complex driver.
> My first thought it that this would be easier to follow / review if we
> built it up in smaller steps.   Perhaps ditch the injected channel support
> entirely in the first instance.  I also wonder if you don't need to support
> that whole thing (injected sampling) as another iio device entirely using the
> same channels.  That's kind of what it is from a data flow point of view
> (we've had arbitary sequencers before with priorities - don't think anyone
> ever decided the pain was worth supporting the complexity, but right answer
> has always been multiple IIO devices).
Hi Jonathan,

First, many thanks for your review. I agree with you, most reasonable 
approach is to remove some
complexity to ease the review. Regarding injected support, basically, 
bellow approach is to use
separate IIO devices for regular and injected. But, I'll remove this, at 
least for now, in next patch set.

> You also have at least one layer of abstraction in here that serves no
> current purpose.  Please clear that out for now. It'll make the code
> shorter and easier to follow.  If/when other parts are introduced then
> is the time to do that transistion to having the abstraction.

 From your suggestion, this may end-up in a single driver file in 
drivers/iio.
I think I'll try to keep simple routines like start, stop, conf_scan and 
so on, but
remove indirection routines from stm32-adc.h file (e.g. stm32_adc_ops).
Is it in line with your suggestions ?

>
> My first thought on the double / tripple adc handling is that you'd be better
> off handling them as 3 separate devices then doing some 'unusual' trigger
> handling to support the weird sequencing.  Guessing you thought about that?
> If so could you lay out your reasoning for the single driver instance approach.
> I'm not arguing against it btw, merely want to understand your reasoning!

I mainly came up with a single driver instance approach because there 
are basically
3 identical ADC instances 'mapped' in a single IP with few common resources.
I usually see mfd are more heterogeneous and declare cells for various 
subsystem drivers.
But I can try to move to mfd as you're suggesting.
I just hope this will not bring more complexity.

>
> It would be tricky given one set of channels are selectable over 3 devices
> and there are constraints to enforce (not sampling same channel on two ADCs
> at the same time) but not impossible...  Perhaps what you have here is
> indeed simpler!
>
> Whilst it's been a nasty job to review, I'm guessing writing it was
> much worse ;)  Pretty good starting point though might take a little while
> to pin down the remaining questions on how best to handle this particular
> monster.
My apologies... I hope you didn't had much of a headache :-) by reading me.
More questions bellow.

> Jonathan
>> ---
>>   drivers/iio/adc/Kconfig             |   2 +
>>   drivers/iio/adc/Makefile            |   1 +
>>   drivers/iio/adc/stm32/Kconfig       |  34 ++
>>   drivers/iio/adc/stm32/Makefile      |   4 +
>>   drivers/iio/adc/stm32/stm32-adc.c   | 999 ++++++++++++++++++++++++++++++++++++
>>   drivers/iio/adc/stm32/stm32-adc.h   | 442 ++++++++++++++++
>>   drivers/iio/adc/stm32/stm32f4-adc.c | 574 +++++++++++++++++++++
>>   7 files changed, 2056 insertions(+)
>>   create mode 100644 drivers/iio/adc/stm32/Kconfig
>>   create mode 100644 drivers/iio/adc/stm32/Makefile
>>   create mode 100644 drivers/iio/adc/stm32/stm32-adc.c
>>   create mode 100644 drivers/iio/adc/stm32/stm32-adc.h
>>   create mode 100644 drivers/iio/adc/stm32/stm32f4-adc.c
>>
>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>> index 7edcf32..5c96a55 100644
>> --- a/drivers/iio/adc/Kconfig
>> +++ b/drivers/iio/adc/Kconfig
>> @@ -583,4 +583,6 @@ config XILINX_XADC
>>   	  The driver can also be build as a module. If so, the module will be called
>>   	  xilinx-xadc.
>>   
>> +source "drivers/iio/adc/stm32/Kconfig"
>> +
>>   endmenu
>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>> index 7a40c04..a9dbf3a 100644
>> --- a/drivers/iio/adc/Makefile
>> +++ b/drivers/iio/adc/Makefile
>> @@ -13,6 +13,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
>>   obj-$(CONFIG_AD7793) += ad7793.o
>>   obj-$(CONFIG_AD7887) += ad7887.o
>>   obj-$(CONFIG_AD799X) += ad799x.o
>> +obj-$(CONFIG_ARCH_STM32) += stm32/
>>   obj-$(CONFIG_AT91_ADC) += at91_adc.o
>>   obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
>>   obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
>> diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
>> new file mode 100644
>> index 0000000..245d037
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32/Kconfig
>> @@ -0,0 +1,34 @@
>> +#
>> +# STM32 familly ADC drivers
>> +#
>> +
>> +config STM32_ADC
>> +	tristate
>> +	select REGULATOR
>> +	select REGULATOR_FIXED_VOLTAGE
>> +	select IIO_BUFFER
>> +	select IIO_TRIGGERED_BUFFER
>> +	help
>> +	  Say yes here to build the driver for the STMicroelectronics
>> +	  STM32 analog-to-digital converter (ADC).
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called stm32-adc.
>> +
>> +config STM32F4_ADC
>> +	tristate "STMicroelectronics STM32F4 adc"
>> +	depends on ARCH_STM32 || COMPILE_TEST
>> +	depends on OF
>> +	select STM32_ADC
>> +	help
>> +	  Say yes here to build support for STMicroelectronics stm32f4 Analog
>> +	  to Digital Converter (ADC).
>> +
>> +	  This driver can also be built as a module.  If so, the module
>> +	  will be called stm32f4-adc.
>> +
>> +config STM32_ADC_DEBUG
>> +	bool "Enable debug for stm32 ADC drivers"
>> +	depends on STM32_ADC
>> +	help
>> +	  Say "yes" to enable debug messages, on stm32 ADC drivers.
>> diff --git a/drivers/iio/adc/stm32/Makefile b/drivers/iio/adc/stm32/Makefile
>> new file mode 100644
>> index 0000000..83e8154
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32/Makefile
>> @@ -0,0 +1,4 @@
>> +# Core
>> +subdir-ccflags-$(CONFIG_STM32_ADC_DEBUG) := -DDEBUG
>> +obj-$(CONFIG_STM32_ADC) += stm32-adc.o
>> +obj-$(CONFIG_STM32F4_ADC) += stm32f4-adc.o
>> diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
>> new file mode 100644
>> index 0000000..1e0850d
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32/stm32-adc.c
>> @@ -0,0 +1,999 @@
[snip]

>> +
>> +static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
>> +			       const unsigned long *scan_mask)
>> +{
>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	ret = stm32_adc_clk_sel(adc);
>> +	if (ret) {
>> +		dev_err(&indio_dev->dev, "Clock sel failed\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = stm32_adc_enable(adc);
>> +	if (ret) {
>> +		dev_err(&indio_dev->dev, "Failed to enable adc\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
>> +	if (ret) {
>> +		dev_err(&indio_dev->dev, "Failed to configure sequence\n");
>> +		goto err_dis;
>> +	}
> It's horrible but to end up in the 'obvious' state I'd disable the adc
> again assuming that doesn't kill the stuff that is configured.
I'll check this and try to come up with something.

>> +
>> +	return 0;
>> +
>> +err_dis:
>> +	stm32_adc_disable(adc);
>> +
>> +	return ret;
>> +}
>> +
[snip]
>> +/**
>> + * stm32_adc_single_conv() - perform a single conversion
>> + * @indio_dev: IIO device
>> + * @chan: IIO channel
>> + * @result: conversion result
>> + *
>> + * The function performs a single conversion on a given channel, by
>> + * by:
>> + * - creating scan mask with only one channel
>> + * - using SW trigger
>> + * - then start single conv
>> + */
>> +static int stm32_adc_single_conv(struct iio_dev *indio_dev,
>> +				 const struct iio_chan_spec *chan,
>> +				 int *val)
>> +{
>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>> +	unsigned long *scan_mask;
>> +	long timeout;
>> +	u16 result;
>> +	int ret;
>> +
>> +	scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
>> +			    GFP_KERNEL);
> This is known maximum length... I'd just avoid the complexity of allocating
> it like this - a comment would do the job to say it is the right length.
Do you suggest to use a predefined variable (like unsigned long 
scan_mask) directly ?
And add a more basic test on 'masklength', to be sure ?

>> +	if (!scan_mask)
>> +		return -ENOMEM;
>> +
>> +	set_bit(chan->scan_index, scan_mask);
>> +
>> +	reinit_completion(&adc->completion);
>> +
>> +	adc->bufi = 0;
>> +	adc->num_conv = 1;
>> +	adc->buffer = &result;
>> +
>> +	ret = stm32_adc_conf_scan(indio_dev, scan_mask);
>> +	if (ret)
>> +		goto free;
>> +
>> +	/* No HW trigger: conversion can be launched in SW */
>> +	ret = stm32_adc_set_trig(indio_dev, NULL);
> Put it back again afterwards?  Otherwise some nasty race conditions look
> likely to me.. (userspace sets trigger and is about to enable the buffer
> when along comes this code and changes it underneath).
I'll fix this.

>> +	if (ret) {
>> +		dev_err(&indio_dev->dev, "Can't set SW trigger\n");
>> +		goto adc_disable;
>> +	}
>> +
>> +	stm32_adc_conv_irq_enable(adc);
>> +
>> +	ret = stm32_adc_start_conv(adc);
>> +	if (ret) {
>> +		dev_err(&indio_dev->dev, "Failed to start single conv\n");
>> +		goto irq_disable;
>> +	}
>> +
>> +	timeout = wait_for_completion_interruptible_timeout(
>> +					&adc->completion, STM32_ADC_TIMEOUT);
>> +	if (timeout == 0) {
>> +		dev_warn(&indio_dev->dev, "Conversion timed out!\n");
>> +		ret = -ETIMEDOUT;
>> +	} else if (timeout < 0) {
>> +		dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
>> +		ret = -EINTR;
>> +	} else {
>> +		*val = result & STM32_RESULT_MASK;
>> +		ret = IIO_VAL_INT;
>> +	}
>> +
>> +	if (stm32_adc_stop_conv(adc))
>> +		dev_err(&indio_dev->dev, "stop failed\n");
>> +
>> +irq_disable:
>> +	stm32_adc_conv_irq_disable(adc);
>> +
>> +adc_disable:
>> +	stm32_adc_disable(adc);
>> +
>> +free:
>> +	kfree(scan_mask);
>> +	adc->buffer = NULL;
>> +
>> +	return ret;
>> +}
>> +
>> +static int stm32_adc_read_raw(struct iio_dev *indio_dev,
>> +			      struct iio_chan_spec const *chan,
>> +			      int *val, int *val2, long mask)
>> +{
>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>> +	int ret = -EINVAL;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		ret = iio_device_claim_direct_mode(indio_dev);
>> +		if (ret)
>> +			return ret;
>> +		if (chan->type == IIO_VOLTAGE)
>> +			ret = stm32_adc_single_conv(indio_dev, chan, val);
>> +		iio_device_release_direct_mode(indio_dev);
>> +		break;
>> +	case IIO_CHAN_INFO_SCALE:
>> +		*val = adc->common->vref_mv;
>> +		*val2 = chan->scan_type.realbits;
>> +		ret = IIO_VAL_FRACTIONAL_LOG2;
>> +		break;
>> +	default:
>> +		break;
>> +	}
>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
> As this is kernel doc please document the parameter as well.  Otherwise
> we'll get a pile of warnings!
Sure.
>> + */
>> +static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
>> +{
>> +	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
>> +	const struct stm32_adc_reginfo *reginfo =
>> +		adc->common->data->adc_reginfo;
>> +	u32 mask, clr_mask, status = stm32_adc_readl(adc, reginfo->isr);
>> +
>> +	if (adc->injected) {
>> +		mask = reginfo->jeoc;
>> +		clr_mask = mask;
>> +	} else {
>> +		mask = reginfo->eoc;
>> +		/* don't clear 'eoc' as it is cleared when reading 'dr' */
>> +		clr_mask = 0;
>> +	}
>> +
>> +	/* clear irq */
>> +	stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
> Want to do this in the non injected case? it's a noop isn't it?
I'll rework this.

>
>> +	status &= mask;
>> +
>> +	/* Regular data */
>> +	if (status & reginfo->eoc) {
> Hmm.. this is a little bit of 'missuse' of the standard trigger architecture
> but as long as it's restricted to just this device I don't suppose we need
> to care.  Only reason we need it is to provide control of 'which' hardware
> trigger is being used.
>
> Guessing the DMA will almost always be turned on and will make this oddity
> effectively disappear.
I'm not sure to understand your remark. Above test checks end of 
conversion status flag.
Or do you talk about bellow lines ? Can you please clarify ?

>> +		adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
>> +		if (iio_buffer_enabled(indio_dev)) {
>> +			adc->bufi++;
>> +			if (adc->bufi >= adc->num_conv) {
>> +				stm32_adc_conv_irq_disable(adc);
>> +				iio_trigger_poll(indio_dev->trig);
>> +			}
>> +		} else {
>> +			complete(&adc->completion);
>> +		}
>> +	}
>> +
>> +	/* Injected data */
>> +	if (status & reginfo->jeoc) {
>> +		int i;
>> +
>> +		for (i = 0; i < adc->num_conv; i++) {
>> +			adc->buffer[i] = stm32_adc_readl(adc, reginfo->jdr[i]);
>> +			adc->bufi++;
>> +		}
>> +
>> +		if (iio_buffer_enabled(indio_dev)) {
>> +			stm32_adc_conv_irq_disable(adc);
>> +			iio_trigger_poll(indio_dev->trig);
>> +		} else {
>> +			complete(&adc->completion);
>> +		}
>> +	}
>> +
>> +	/*
>> +	 * In case end of conversion flags have been handled, this has been
>> +	 * handled for this ADC instance
>> +	 */
>> +	if (status)
>> +		return IRQ_HANDLED;
>> +
>> +	/* This adc instance didn't trigger this interrupt */
>> +	return IRQ_NONE;
>> +}
>> +
>> +/**
>> + * stm32_adc_common_isr() - Common isr for the whole ADC block
>> + *
>> + * There is one IRQ for all ADCs in ADC block, check all instances.
>> + */
>> +static irqreturn_t stm32_adc_common_isr(int irq, void *data)
>> +{
>> +	struct stm32_adc_common *common = data;
>> +	irqreturn_t ret = IRQ_NONE;
>> +	struct stm32_adc *adc;
>> +
>> +	list_for_each_entry(adc, &common->adc_list, adc_list)
>> +		ret |= stm32_adc_isr(adc);
> Hmm.. ret |= is rather fragile.  Preferable to make the handling of NONE
> vs IRQ_HANDLED explicit.
>
> If you were to split the driver up as I suggested might make sense above,
> then this would be done with an irq chip in a top level device (effectively
> a very simple mfd).
I'll look into it.

>> +
>> +	return ret;
>> +}
>> +
>> +/**
>> + * stm32_adc_validate_trigger() - validate trigger for stm32 adc
>> + * @indio_dev: IIO device
>> + * @trig: new trigger
>> + *
>> + * Returns: 0 if trig matches one of the triggers registered by stm32 adc
>> + * driver, -EINVAL otherwise.
>> + */
>> +static int stm32_adc_validate_trigger(struct iio_dev *indio_dev,
>> +				      struct iio_trigger *trig)
>> +{
>> +	return stm32_adc_get_trig_index(indio_dev, trig) < 0 ? -EINVAL : 0;
>> +}
>> +
>> +static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
>> +				      const unsigned long *scan_mask)
> I'm glad you kept this relatively simple compared to some of the
> 'fun' the hardware is capable of. Very wise!
>> +{
>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>> +	int ret;
>> +	u32 bit;
>> +
>> +	adc->num_conv = 0;
>> +	for_each_set_bit(bit, scan_mask, indio_dev->masklength)
>> +		adc->num_conv++;
>> +
>> +	ret = stm32_adc_conf_scan(indio_dev, scan_mask);
>> +	if (ret)
>> +		return ret;
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
>> +			      const struct of_phandle_args *iiospec)
>> +{
>> +	int i;
>> +
>> +	for (i = 0; i < indio_dev->num_channels; i++)
>> +		if (indio_dev->channels[i].channel == iiospec->args[0])
>> +			return i;
>> +
>> +	return -EINVAL;
>> +}
>> +
>> +/**
>> + * stm32_adc_debugfs_reg_access - read or write register value
>> + *
>> + * To read a value from an ADC register:
>> + *   echo [ADC reg offset] > direct_reg_access
>> + *   cat direct_reg_access
>> + *
>> + * To write a value in a ADC register:
>> + *   echo [ADC_reg_offset] [value] > direct_reg_access
>> + */
>> +static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
>> +					unsigned reg, unsigned writeval,
>> +					unsigned *readval)
>> +{
>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>> +
>> +	if (!readval)
>> +		stm32_adc_writel(adc, reg, writeval);
>> +	else
>> +		*readval = stm32_adc_readl(adc, reg);
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct iio_info stm32_adc_iio_info = {
>> +	.read_raw = stm32_adc_read_raw,
>> +	.validate_trigger = stm32_adc_validate_trigger,
>> +	.update_scan_mode = stm32_adc_update_scan_mode,
>> +	.debugfs_reg_access = stm32_adc_debugfs_reg_access,
>> +	.of_xlate = stm32_adc_of_xlate,
>> +	.driver_module = THIS_MODULE,
>> +};
>> +
>> +static int stm32_adc_buffer_postdisable(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>> +
>> +	stm32_adc_disable(adc);
> This is a surprise as postdisbale should balance preenable...
> Ah, you have update scan mode enabling the adc.  If you can balance it
> better by moving that to preenable please do as it is more 'obviously' correct.
I'll try to rework this.
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
>> +	.postenable = &iio_triggered_buffer_postenable,
>> +	.predisable = &iio_triggered_buffer_predisable,
>> +	.postdisable = &stm32_adc_buffer_postdisable,
>> +};
>> +
>> +static int stm32_adc_validate_device(struct iio_trigger *trig,
>> +				     struct iio_dev *indio_dev)
>> +{
>> +	struct iio_dev *indio = iio_trigger_get_drvdata(trig);
>> +
>> +	return indio != indio_dev ? -EINVAL : 0;
>> +}
>> +
>> +static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
>> +				       bool state)
>> +{
>> +	struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	if (state) {
>> +		/* Reset adc buffer index */
>> +		adc->bufi = 0;
>> +
>> +		/* Allocate adc buffer */
>> +		adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
> I'd be more cynical.  It's not that big a memory allocation at worst.
> Just put a big enough buffer in your adc structure and don't bother doing
> it dynamically.
>
> If you didn't want to do it, it should be in the preenable callback rather
> than the trigger state one (for semantic reasons rather than because it's a
> bug)
I'll fix this.
>> +		if (!adc->buffer)
>> +			return -ENOMEM;
>> +
>> +		ret = stm32_adc_set_trig(indio_dev, trig);
>> +		if (ret) {
>> +			dev_err(&indio_dev->dev, "Can't set trigger\n");
>> +			goto err_buffer_free;
>> +		}
>> +
>> +		stm32_adc_conv_irq_enable(adc);
>> +
>> +		ret = stm32_adc_start_conv(adc);
>> +		if (ret) {
>> +			dev_err(&indio_dev->dev, "Failed to start\n");
>> +			goto err_irq_trig_disable;
>> +		}
>> +	} else {
>> +		ret = stm32_adc_stop_conv(adc);
>> +		if (ret < 0) {
>> +			dev_err(&indio_dev->dev, "Failed to stop\n");
>> +			return ret;
>> +		}
>> +
>> +		stm32_adc_conv_irq_disable(adc);
>> +
>> +		ret = stm32_adc_set_trig(indio_dev, NULL);
>> +		if (ret)
>> +			dev_warn(&indio_dev->dev, "Can't clear trigger\n");
>> +
>> +		kfree(adc->buffer);
>> +		adc->buffer = NULL;
>> +	}
>> +
>> +	return 0;
>> +
>> +err_irq_trig_disable:
>> +	stm32_adc_conv_irq_disable(adc);
>> +	stm32_adc_set_trig(indio_dev, NULL);
>> +
>> +err_buffer_free:
>> +	kfree(adc->buffer);
>> +	adc->buffer = NULL;
>> +
>> +	return ret;
>> +}
>> +
>> +static const struct iio_trigger_ops stm32_adc_trigger_ops = {
>> +	.owner = THIS_MODULE,
>> +	.validate_device = stm32_adc_validate_device,
>> +	.set_trigger_state = stm32_adc_set_trigger_state,
>> +};
>> +
>> +static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
>> +{
>> +	struct iio_poll_func *pf = p;
>> +	struct iio_dev *indio_dev = pf->indio_dev;
>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>> +
>> +	dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
>> +
>> +	/* reset buffer index */
>> +	adc->bufi = 0;
>> +	iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
>> +					   pf->timestamp);
>> +
>> +	iio_trigger_notify_done(indio_dev->trig);
>> +
>> +	/* re-enable eoc irq */
>> +	stm32_adc_conv_irq_enable(adc);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static void stm32_adc_trig_unregister(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>> +	struct iio_trigger *trig, *_t;
>> +
>> +	list_for_each_entry_safe(trig, _t, &adc->extrig_list, alloc_list) {
>> +		iio_trigger_unregister(trig);
>> +		list_del(&trig->alloc_list);
>> +	}
>> +}
>> +
> I'd like a bit of documentation on this and a few of the other more
> complex functions.  Here it wasn't immediately obvious to me that it
> was registering a large set of triggers.  Also, silly question but
> do you have any means of controlling the various timer setups from userspace?
Sorry about this, I'll try to comment about trigger list to make it more 
obvious.
There is no mean to setup timers via userspace, yet...
I can remove them from the list for now, until this is supported.
BTW I have some questions on trigger...

>
> There have been numerous discussions over the years on having a generic
> timer subsystem, but if anything got written it passed me by. I have a couple
> of boards where it would be handy but never had the time to do more than
> talk about it ;)

This is interesting... I'd be glad to hear more about it. Can you point 
some discussions if you have it in mind?

In this driver, validate_trigger routine enforces that only triggers 
allocated for current indio_dev can be used.
What if all timer triggers are put in a separate driver ? (e.g. like 
hrtimer in drivers/iio/trigger/) ?
Purpose would be to tune 'sampling_frequency' and so on, on similar 
model, and have it configured basically when using it
(e.g. cat trigger/name>trigger/current_trigger.).

Is it a viable option, not to declare timer triggers in stm32-adc.c, but 
use pre-defined list of triggers, and separate trigger driver ?
I'm thinking then, of simple string based list... But maybe you already 
though about this king of things ?

Please kindly share you view on this.

Thanks again for your review.
Best Regards,
Fabrice

>> +static int stm32_adc_trig_register(struct iio_dev *indio_dev)
>> +{
>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>> +	struct stm32_adc_common *common = adc->common;
>> +	const struct stm32_adc_trig_info *ext = common->data->ext_triggers;
>> +	struct iio_trigger *trig;
>> +	int i, ret = 0;
>> +
>> +	if (adc->injected)
>> +		ext = common->data->jext_triggers;
>> +	else
>> +		ext = common->data->ext_triggers;
>> +
>> +	for (i = 0; ext && ext[i].name; i++) {
>> +		trig = devm_iio_trigger_alloc(common->dev, "%s_%s%d_%s",
>> +					      indio_dev->name,
>> +					      adc->injected ? "jext" : "ext",
>> +					      ext[i].extsel, ext[i].name);
>> +		if (!trig) {
>> +			dev_err(common->dev, "trig %s_%s%d_%s alloc failed\n",
>> +				indio_dev->name,
>> +				adc->injected ? "jext" : "ext",
>> +				ext[i].extsel, ext[i].name);
>> +			ret = -ENOMEM;
>> +			goto err;
>> +		}
>> +
>> +		trig->dev.parent = common->dev;
>> +		trig->ops = &stm32_adc_trigger_ops;
>> +		iio_trigger_set_drvdata(trig, indio_dev);
>> +
>> +		ret = iio_trigger_register(trig);
>> +		if (ret) {
>> +			dev_err(common->dev,
>> +				"trig %s_%s%d_%s register failed\n",
>> +				indio_dev->name,
>> +				adc->injected ? "jext" : "ext",
>> +				ext[i].extsel, ext[i].name);
>> +			goto err;
>> +		}
>> +
>> +		list_add_tail(&trig->alloc_list, &adc->extrig_list);
>> +	}
>> +
>> +	return 0;
>> +err:
>> +	stm32_adc_trig_unregister(indio_dev);
>> +
>> +	return ret;
>> +}
>> +
>> +static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
>> +				    struct iio_chan_spec *chan,
>> +				    const struct stm32_adc_chan_spec *channel,
>> +				    int scan_index)
>> +{
>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>> +
>> +	chan->type = channel->type;
>> +	chan->channel = channel->channel;
>> +	chan->datasheet_name = channel->name;
>> +	chan->extend_name = channel->name;
>> +	chan->scan_index = scan_index;
>> +	chan->indexed = 1;
>> +	chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
>> +	chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
>> +	chan->scan_type.sign = 'u';
>> +	chan->scan_type.realbits = adc->common->data->highres;
>> +	chan->scan_type.storagebits = STM32_STORAGEBITS;
> This is one of those cases where actually I'd argue just having the number
> here and not under a define would be clearer!  So just put 16 here.
>> +	chan->scan_type.shift = 0;
> Should be unneeded.  Shift of 0 is the obvious default so no info provided
> to readers of the code either really.
I'll fix this
>> +}
>> +
>> +static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
>> +				  const struct stm32_adc_info *adc_info)
>> +{
>> +	struct stm32_adc *adc = iio_priv(indio_dev);
>> +	struct device_node *node = indio_dev->dev.of_node;
>> +	struct property *prop;
>> +	const __be32 *cur;
>> +	struct iio_chan_spec *channels;
>> +	int scan_index = 0, num_channels = 0;
>> +	u32 val;
>> +
>> +	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
>> +		num_channels++;
>> +
>> +	channels = devm_kcalloc(&indio_dev->dev, num_channels,
>> +				sizeof(struct iio_chan_spec), GFP_KERNEL);
>> +	if (!channels)
>> +		return -ENOMEM;
>> +
>> +	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
>> +		stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
>> +					&adc_info->channels[val],
>> +					scan_index);
>> +		scan_index++;
>> +	}
>> +
>> +	adc->max_channels = adc_info->max_channels;
>> +	indio_dev->num_channels = scan_index;
>> +	indio_dev->channels = channels;
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32_adc_register(struct stm32_adc_common *common,
>> +			      struct device_node *child)
>> +{
>> +	struct iio_dev *indio_dev;
>> +	struct stm32_adc *adc;
>> +	int i, ret;
>> +	u32 reg;
>> +
>> +	ret = of_property_read_u32(child, "reg", &reg);
>> +	if (ret != 0) {
>> +		dev_err(common->dev, "missing reg property\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	for (i = 0; common->data->adc_info[i].channels; i++)
>> +		if (common->data->adc_info[i].reg == reg)
>> +			break;
>> +
>> +	if (i >= STM32_ADC_ID_MAX || !common->data->adc_info[i].channels) {
>> +		dev_err(common->dev, "bad adc reg offset\n");
>> +		return -ENOENT;
>> +	}
>> +
>> +	indio_dev = devm_iio_device_alloc(common->dev, sizeof(*adc));
>> +	if (!indio_dev) {
>> +		dev_err(common->dev, "iio device allocation failed\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	adc = iio_priv(indio_dev);
>> +	adc->id = i;
>> +	adc->offset = reg;
>> +	adc->common = common;
>> +	INIT_LIST_HEAD(&adc->extrig_list);
>> +	spin_lock_init(&adc->lock);
>> +	init_completion(&adc->completion);
>> +
>> +	if (child->name)
>> +		indio_dev->name = child->name;
>> +	else
>> +		indio_dev->name = common->data->adc_info[i].name;
>> +	indio_dev->dev.parent = common->dev;
>> +	indio_dev->dev.of_node = child;
>> +	indio_dev->info = &stm32_adc_iio_info;
>> +	indio_dev->modes = INDIO_DIRECT_MODE;
>> +
>> +	if (of_property_read_bool(child, "st,injected")) {
>> +		dev_dbg(common->dev, "%s Configured to use injected\n",
>> +			indio_dev->name);
>> +		adc->injected = true;
>> +	}
>> +
>> +	adc->clk = of_clk_get(child, 0);
>> +	if (IS_ERR(adc->clk)) {
>> +		adc->clk = NULL;
>> +		dev_dbg(common->dev, "No child clk found\n");
>> +	} else {
>> +		ret = clk_prepare_enable(adc->clk);
>> +		if (ret < 0)
>> +			goto err_clk_put;
>> +	}
>> +
>> +	ret = stm32_adc_chan_of_init(indio_dev, &common->data->adc_info[i]);
>> +	if (ret < 0) {
>> +		dev_err(common->dev, "iio channels init failed\n");
>> +		goto err_clk_disable;
>> +	}
>> +
>> +	ret = stm32_adc_trig_register(indio_dev);
>> +	if (ret)
>> +		goto err_clk_disable;
>> +
>> +	ret = iio_triggered_buffer_setup(indio_dev,
>> +					 &iio_pollfunc_store_time,
>> +					 &stm32_adc_trigger_handler,
>> +					 &iio_triggered_buffer_setup_ops);
>> +	if (ret) {
>> +		dev_err(common->dev, "buffer setup failed\n");
>> +		goto err_trig_unregister;
>> +	}
>> +
>> +	ret = iio_device_register(indio_dev);
>> +	if (ret) {
>> +		dev_err(common->dev, "iio dev register failed\n");
>> +		goto err_buffer_cleanup;
>> +	}
>> +
>> +	list_add_tail(&adc->adc_list, &common->adc_list);
>> +
>> +	return 0;
>> +
>> +err_buffer_cleanup:
>> +	iio_triggered_buffer_cleanup(indio_dev);
>> +
>> +err_trig_unregister:
>> +	stm32_adc_trig_unregister(indio_dev);
>> +
>> +err_clk_disable:
>> +	if (adc->clk)
>> +		clk_disable_unprepare(adc->clk);
>> +
>> +err_clk_put:
>> +	if (adc->clk)
>> +		clk_put(adc->clk);
>> +
>> +	return ret;
>> +}
>> +
>> +static void stm32_adc_unregister(struct stm32_adc *adc)
>> +{
>> +	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
>> +
>> +	iio_device_unregister(indio_dev);
>> +	iio_triggered_buffer_cleanup(indio_dev);
>> +	stm32_adc_trig_unregister(indio_dev);
>> +	if (adc->clk) {
>> +		clk_disable_unprepare(adc->clk);
>> +		clk_put(adc->clk);
>> +	}
>> +}
>> +
>> +int stm32_adc_probe(struct platform_device *pdev)
>> +{
>> +	struct device_node *np = pdev->dev.of_node, *child;
>> +	struct device *dev = &pdev->dev;
>> +	const struct of_device_id *match;
>> +	struct stm32_adc_common *common;
>> +	struct stm32_adc *adc;
>> +	struct resource *res;
>> +	int ret;
>> +
>> +	match = of_match_device(dev->driver->of_match_table, &pdev->dev);
>> +	if (!match || !match->data) {
>> +		dev_err(&pdev->dev, "compatible data not provided\n");
> How would we have instantiated this if there was not a suitable match?
> As such what does this check give us? (confused!)
I'll fix this.
>> +		return -EINVAL;
>> +	}
>> +
>> +	common = devm_kzalloc(&pdev->dev, sizeof(*common), GFP_KERNEL);
>> +	if (!common)
>> +		return -ENOMEM;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	common->base = devm_ioremap_resource(&pdev->dev, res);
>> +	if (IS_ERR(common->base))
>> +		return PTR_ERR(common->base);
>> +
>> +	common->data = match->data;
>> +	common->dev = &pdev->dev;
>> +	platform_set_drvdata(pdev, common);
>> +	mutex_init(&common->lock);
>> +	INIT_LIST_HEAD(&common->adc_list);
>> +
>> +	common->vref = devm_regulator_get(&pdev->dev, "vref");
>> +	if (IS_ERR(common->vref)) {
>> +		ret = PTR_ERR(common->vref);
>> +		dev_err(&pdev->dev, "vref get failed, %d\n", ret);
>> +		return ret;
>> +	}
>> +
>> +	ret = regulator_enable(common->vref);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "vref enable failed\n");
>> +		return ret;
>> +	}
>> +
>> +	ret = regulator_get_voltage(common->vref);
>> +	if (ret < 0) {
>> +		dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
>> +		goto err_regulator_disable;
>> +	}
>> +	common->vref_mv = ret / 1000;
>> +	dev_dbg(&pdev->dev, "vref+=%dmV\n", common->vref_mv);
>> +
>> +	common->aclk = devm_clk_get(&pdev->dev, "adc");
>> +	if (IS_ERR(common->aclk)) {
>> +		ret = PTR_ERR(common->aclk);
>> +		dev_err(&pdev->dev, "Can't get 'adc' clock\n");
>> +		goto err_regulator_disable;
>> +	}
>> +
>> +	ret = clk_prepare_enable(common->aclk);
>> +	if (ret < 0) {
>> +		dev_err(common->dev, "adc clk enable failed\n");
>> +		goto err_regulator_disable;
>> +	}
>> +
>> +	common->irq = platform_get_irq(pdev, 0);
>> +	if (common->irq < 0) {
>> +		dev_err(&pdev->dev, "failed to get irq\n");
>> +		ret = common->irq;
>> +		goto err_clk_disable;
>> +	}
>> +
>> +	ret = devm_request_irq(&pdev->dev, common->irq,	stm32_adc_common_isr,
>> +			       0, pdev->name, common);
>> +	if (ret) {
>> +		dev_err(&pdev->dev, "failed to request irq\n");
>> +		goto err_clk_disable;
>> +	}
>> +
>> +	/* Parse adc child nodes to retrieve master/slave instances data */
>> +	for_each_available_child_of_node(np, child) {
>> +		ret = stm32_adc_register(common, child);
>> +		if (ret)
>> +			goto err_unregister;
>> +	}
>> +
>> +	dev_info(&pdev->dev, "registered\n");
> No benefit in this info being provided (it's obvious, device just turned up
> in sysfs :) So drop it.
I'll fix this.
>> +
>> +	return 0;
>> +
>> +err_unregister:
>> +	list_for_each_entry(adc, &common->adc_list, adc_list)
>> +		stm32_adc_unregister(adc);
>> +
>> +err_clk_disable:
>> +	clk_disable_unprepare(common->aclk);
>> +
>> +err_regulator_disable:
>> +	regulator_disable(common->vref);
>> +
>> +	return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(stm32_adc_probe);
>> +
>> +int stm32_adc_remove(struct platform_device *pdev)
>> +{
>> +	struct stm32_adc_common *common = platform_get_drvdata(pdev);
>> +	struct stm32_adc *adc;
>> +
>> +	list_for_each_entry(adc, &common->adc_list, adc_list)
>> +		stm32_adc_unregister(adc);
>> +	clk_disable_unprepare(common->aclk);
>> +	regulator_disable(common->vref);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(stm32_adc_remove);
>> +
>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
>> new file mode 100644
>> index 0000000..0be603c
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32/stm32-adc.h
>> @@ -0,0 +1,442 @@
>> +/*
>> + * This file is part of STM32 ADC driver
>> + *
>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>> + * Author: Fabrice Gasnier <fabrice.gasnier@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/>.
>> + */
>> +
>> +#ifndef __STM32_ADC_H
>> +#define __STM32_ADC_H
>> +
>> +/*
>> + * STM32 - ADC global register map
>> + * ________________________________________________________
>> + * | Offset |                 Register                    |
>> + * --------------------------------------------------------
>> + * | 0x000  |                Master ADC1                  |
>> + * --------------------------------------------------------
>> + * | 0x100  |                Slave ADC2                   |
>> + * --------------------------------------------------------
>> + * | 0x200  |                Slave ADC3                   |
>> + * --------------------------------------------------------
>> + * | 0x300  |         Master & Slave common regs          |
>> + * --------------------------------------------------------
>> + */
>> +#define STM32_ADCX_COMN_OFFSET		0x300
>> +#define STM32_ADC_ID_MAX		3
>> +#define STM32_ADC_MAX_SQ		16	/* SQ1..SQ16 */
>> +#define STM32_ADC_MAX_JSQ		4	/* JSQ1..JSQ4 */
>> +
>> +/* STM32 value masks */
>> +#define STM32_RESULT_MASK	GENMASK(15, 0)
>> +#define STM32_STORAGEBITS	16
>> +
>> +/* External trigger enable for regular or injected channels (exten/jexten) */
>> +enum stm32_adc_exten {
>> +	STM32_EXTEN_SWTRIG,
>> +	STM32_EXTEN_HWTRIG_RISING_EDGE,
>> +	STM32_EXTEN_HWTRIG_FALLING_EDGE,
>> +	STM32_EXTEN_HWTRIG_BOTH_EDGES,
>> +};
>> +
>> +enum stm32_adc_extsel {
>> +	STM32_EXT0,
>> +	STM32_EXT1,
>> +	STM32_EXT2,
>> +	STM32_EXT3,
>> +	STM32_EXT4,
>> +	STM32_EXT5,
>> +	STM32_EXT6,
>> +	STM32_EXT7,
>> +	STM32_EXT8,
>> +	STM32_EXT9,
>> +	STM32_EXT10,
>> +	STM32_EXT11,
>> +	STM32_EXT12,
>> +	STM32_EXT13,
>> +	STM32_EXT14,
>> +	STM32_EXT15,
>> +	STM32_EXT16,
>> +	STM32_EXT17,
>> +	STM32_EXT18,
>> +	STM32_EXT19,
>> +	STM32_EXT20,
>> +	STM32_EXT21,
>> +	STM32_EXT22,
>> +	STM32_EXT23,
>> +	STM32_EXT24,
>> +	STM32_EXT25,
>> +	STM32_EXT26,
>> +	STM32_EXT27,
>> +	STM32_EXT28,
>> +	STM32_EXT29,
>> +	STM32_EXT30,
>> +	STM32_EXT31,
>> +};
>> +
>> +enum stm32_adc_jextsel {
>> +	STM32_JEXT0,
>> +	STM32_JEXT1,
>> +	STM32_JEXT2,
>> +	STM32_JEXT3,
>> +	STM32_JEXT4,
>> +	STM32_JEXT5,
>> +	STM32_JEXT6,
>> +	STM32_JEXT7,
>> +	STM32_JEXT8,
>> +	STM32_JEXT9,
>> +	STM32_JEXT10,
>> +	STM32_JEXT11,
>> +	STM32_JEXT12,
>> +	STM32_JEXT13,
>> +	STM32_JEXT14,
>> +	STM32_JEXT15,
>> +	STM32_JEXT16,
>> +	STM32_JEXT17,
>> +	STM32_JEXT18,
>> +	STM32_JEXT19,
>> +	STM32_JEXT20,
>> +	STM32_JEXT21,
>> +	STM32_JEXT22,
>> +	STM32_JEXT23,
>> +	STM32_JEXT24,
>> +	STM32_JEXT25,
>> +	STM32_JEXT26,
>> +	STM32_JEXT27,
>> +	STM32_JEXT28,
>> +	STM32_JEXT29,
>> +	STM32_JEXT30,
>> +	STM32_JEXT31,
>> +};
>> +
>> +#define	STM32_ADC_TIMEOUT_US	100000
>> +#define	STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
>> +
>> +/**
>> + * struct stm32_adc_chan_spec - specification of stm32 adc channel
>> + * @type:	IIO channel type
>> + * @channel:	channel number (single ended)
>> + * @name:	channel name (single ended)
>> + */
>> +struct stm32_adc_chan_spec {
>> +	enum iio_chan_type	type;
>> +	int			channel;
>> +	const char		*name;
>> +};
>> +
>> +/**
>> + * struct stm32_adc_trig_info - ADC trigger info
>> + * @extsel:		trigger selection for regular or injected
>> + * @name:		name of the trigger, corresponding to its source
>> + */
>> +struct stm32_adc_trig_info {
>> +	u32 extsel;
>> +	const char *name;
>> +};
>> +
>> +/**
>> + * struct stm32_adc_info - stm32 ADC, per instance config data
>> + * @name:		default name for this instance (like "adc1")
>> + * @reg:		reg offset for this instance (e.g. 0x0 for adc1...)
>> + * @channels:		Reference to stm32 channels spec
>> + * @max_channels:	Number of single ended channels
>> + */
>> +struct stm32_adc_info {
>> +	const char *name;
>> +	u32 reg;
>> +	const struct stm32_adc_chan_spec *channels;
>> +	int max_channels;
>> +};
>> +
>> +/**
>> + * stm32_adc_regs - stm32 ADC misc registers & bitfield desc
>> + * @reg:		register offset
>> + * @mask:		bitfield mask
>> + * @shift:		left shift
>> + */
>> +struct stm32_adc_regs {
>> +	int reg;
>> +	int mask;
>> +	int shift;
>> +};
>> +
>> +/**
>> + * stm32_adc_trig_reginfo - stm32 ADC trigger control registers description
>> + * @reg:		trigger control register offset (exten/jexten)
>> + * @exten_mask:		external trigger en/polarity mask in @reg
>> + * @exten_shift:	external trigger en/polarity shift in @reg
>> + * @extsel_mask:	external trigger source mask in @reg
>> + * @extsel_shift:	external trigger source shift in @reg
>> + */
>> +struct stm32_adc_trig_reginfo {
>> +	u32 reg;
>> +	u32 exten_mask;
>> +	u32 exten_shift;
>> +	u32 extsel_mask;
>> +	u32 extsel_shift;
>> +};
>> +
>> +/**
>> + * struct stm32_adc_reginfo - stm32 ADC registers description
>> + * @isr:		interrupt status register offset
>> + * @eoc:		end of conversion mask in @isr
>> + * @jeoc:		end of injected conversion sequence mask in @isr
>> + * @ier:		interrupt enable register offset
>> + * @eocie:		end of conversion interrupt enable mask in @ier
>> + * @jeocie:		end of injected conversion sequence interrupt en mask
>> + * @dr:			data register offset
>> + * @jdr:		injected data registers offsets
>> + * @sqr_regs:		Regular sequence registers description
>> + * @jsqr_reg:		Injected sequence register description
>> + * @trig_reginfo:	regular trigger control registers description
>> + * @jtrig_reginfo:	injected trigger control registers description
>> + */
>> +struct stm32_adc_reginfo {
>> +	u32 isr;
>> +	u32 eoc;
>> +	u32 jeoc;
>> +	u32 ier;
>> +	u32 eocie;
>> +	u32 jeocie;
>> +	u32 dr;
>> +	u32 jdr[4];
>> +	const struct stm32_adc_regs *sqr_regs;
>> +	const struct stm32_adc_regs *jsqr_reg;
>> +	const struct stm32_adc_trig_reginfo *trig_reginfo;
>> +	const struct stm32_adc_trig_reginfo *jtrig_reginfo;
>> +};
>> +
>> +struct stm32_adc;
>> +
>> +/**
>> + * struct stm32_adc_ops - stm32 ADC, compatible dependent data
>> + * - stm32 ADC may work as single ADC, or as tightly coupled master/slave ADCs.
>> + *
>> + * @adc_info:		Array spec for stm32 adc master/slaves instances
>> + * @ext_triggers:	Reference to trigger info for regular channels
>> + * @jext_triggers:	Reference to trigger info for injected channels
>> + * @adc_reginfo:	stm32 ADC registers description
>> + * @highres:		Max resolution
>> + * @max_clock_rate:	Max input clock rate
>> + * @clk_sel:		routine to select common clock and prescaler
>> + * @start_conv:		routine to start conversions
>> + * @stop_conv:		routine to stop conversions
>> + * @is_started:		routine to get adc 'started' state
>> + * @regular_started	routine to check regular conversions status
>> + * @injected_started	routine to check injected conversions status
>> + * @enable:		optional routine to enable stm32 adc
>> + * @disable:		optional routine to disable stm32 adc
>> + * @is_enabled		reports enabled state
>> + */
> This is a big chunk of abstraction that seems excessive at the moment.
> I'd rather see it introduced only just before it's actually used..
> (I'm guessing it's intended for support of similar parts?)
>
> Right now it just makes the driver harder to review.
>> +struct stm32_adc_ops {
>> +	const struct stm32_adc_info *adc_info;
>> +	const struct stm32_adc_trig_info *ext_triggers;
>> +	const struct stm32_adc_trig_info *jext_triggers;
>> +	const struct stm32_adc_reginfo *adc_reginfo;
>> +	int highres;
>> +	unsigned long max_clock_rate;
>> +	int (*clk_sel)(struct stm32_adc *adc);
>> +	int (*start_conv)(struct stm32_adc *adc);
>> +	int (*stop_conv)(struct stm32_adc *adc);
>> +	bool (*is_started)(struct stm32_adc *adc);
>> +	bool (*regular_started)(struct stm32_adc *adc);
>> +	bool (*injected_started)(struct stm32_adc *adc);
>> +	int (*enable)(struct stm32_adc *adc);
>> +	void (*disable)(struct stm32_adc *adc);
>> +	bool (*is_enabled)(struct stm32_adc *adc);
>> +};
>> +
>> +struct stm32_adc_common;
>> +
>> +/**
>> + * struct stm32_adc - private data of each ADC IIO instance
>> + * @common:		reference to ADC block common data
>> + * @adc_list:		current ADC entry in common ADC list
>> + * @id:			ADC instance number (e.g. adc 1, 2 or 3)
>> + * @offset:		ADC instance register offset in ADC block
>> + * @max_channels:	Max channels number for this ADC.
>> + * @extrig_list:	External trigger list (for regular channel)
>> + * @completion:		end of single conversion completion
>> + * @buffer:		data buffer
>> + * @bufi:		data buffer index
>> + * @num_conv:		expected number of scan conversions
>> + * @injected:		use injected channels on this adc
>> + * @lock:		spinlock
>> + * @clk:		optional adc clock, for this adc instance
>> + * @calib:		optional calibration data
>> + * @en:			emulates enabled state on some stm32 adc
>> + */
>> +struct stm32_adc {
>> +	struct stm32_adc_common	*common;
>> +	struct list_head	adc_list;
>> +	int			id;
>> +	int			offset;
>> +	int			max_channels;
>> +	struct list_head	extrig_list;
>> +	struct completion	completion;
>> +	u16			*buffer;
>> +	int			bufi;
>> +	int			num_conv;
>> +	bool			injected;
>> +	spinlock_t		lock;		/* interrupt lock */
>> +	struct clk		*clk;
>> +	void			*calib;
>> +	bool			en;
>> +};
>> +
>> +/**
>> + * struct stm32_adc_common - private data of ADC driver, common to all
>> + * ADC instances (ADC block)
>> + * @dev:		device for this controller
>> + * @base:		control registers base cpu addr
>> + * @irq:		Common irq line for all adc instances
>> + * @data:		STM32 dependent data from compatible
>> + * @adc_list:		list of all stm32 ADC in this ADC block
>> + * @aclk:		common clock for the analog circuitry
>> + * @vref:		regulator reference
>> + * @vref_mv:		vref voltage (mv)
>> + * @lock:		mutex
>> + */
>> +struct stm32_adc_common {
>> +	struct device			*dev;
>> +	void __iomem			*base;
>> +	int				irq;
>> +	const struct stm32_adc_ops	*data;
>> +	struct list_head		adc_list;
>> +	struct clk			*aclk;
>> +	struct regulator		*vref;
>> +	int				vref_mv;
>> +	struct mutex			lock;	/* read_raw lock */
>> +};
>> +
>> +/* Helper routines */
>> +static inline int stm32_adc_start_conv(struct stm32_adc *adc)
>> +{
>> +	return adc->common->data->start_conv(adc);
>> +}
>> +
>> +static inline int stm32_adc_stop_conv(struct stm32_adc *adc)
>> +{
>> +	return adc->common->data->stop_conv(adc);
>> +}
>> +
>> +static inline bool stm32_adc_is_started(struct stm32_adc *adc)
>> +{
>> +	return adc->common->data->is_started(adc);
>> +}
>> +
>> +static inline bool stm32_adc_regular_started(struct stm32_adc *adc)
>> +{
>> +	return adc->common->data->regular_started(adc);
>> +}
>> +
>> +static inline bool stm32_adc_injected_started(struct stm32_adc *adc)
>> +{
>> +	return adc->common->data->injected_started(adc);
>> +}
>> +
>> +static inline bool stm32_adc_clk_sel(struct stm32_adc *adc)
>> +{
>> +	return adc->common->data->clk_sel(adc);
>> +}
>> +
>> +static inline int stm32_adc_enable(struct stm32_adc *adc)
>> +{
>> +	if (adc->common->data->enable)
>> +		return adc->common->data->enable(adc);
>> +
>> +	adc->en = true;
>> +
>> +	return 0;
>> +}
>> +
>> +static inline bool stm32_adc_is_enabled(struct stm32_adc *adc)
>> +{
>> +	if (adc->common->data->is_enabled)
>> +		return adc->common->data->is_enabled(adc);
>> +	else
>> +		return adc->en;
>> +}
>> +
>> +static inline void stm32_adc_disable(struct stm32_adc *adc)
>> +{
>> +	/* Check there is no regular or injected on-going conversions */
>> +	if (stm32_adc_is_started(adc))
>> +		return;
>> +
>> +	if (adc->common->data->disable)
>> +		adc->common->data->disable(adc);
>> +	else
>> +		adc->en = false;
>> +}
>> +
>> +/* STM32 ADC registers access routines */
>> +static inline u32 stm32_adc_common_readl(struct stm32_adc_common *com, u32 reg)
>> +{
>> +	u32 val = readl_relaxed(com->base + reg);
>> +
>> +	return val;
>> +}
>> +
>> +static inline void stm32_adc_common_writel(struct stm32_adc_common *com,
>> +					   u32 reg, u32 val)
>> +{
>> +	writel_relaxed(val, com->base + reg);
>> +}
>> +
>> +static inline u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
>> +{
>> +	u32 val = readl_relaxed(adc->common->base + adc->offset + reg);
>> +
>> +	return val;
>> +}
>> +
>> +#define stm32_adc_readl_addr(addr)	stm32_adc_readl(adc, addr)
>> +
>> +#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \
>> +	readx_poll_timeout(stm32_adc_readl_addr, reg, val, \
>> +			   cond, sleep_us, timeout_us)
>> +
>> +static inline void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
>> +{
>> +	writel_relaxed(val, adc->common->base + adc->offset + reg);
>> +}
>> +
>> +static inline void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
>> +{
>> +	unsigned long flags;
>> +
>> +	spin_lock_irqsave(&adc->lock, flags);
>> +	stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
>> +	spin_unlock_irqrestore(&adc->lock, flags);
>> +}
>> +
>> +static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
>> +{
>> +	unsigned long flags;
>> +
>> +	spin_lock_irqsave(&adc->lock, flags);
>> +	stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
>> +	spin_unlock_irqrestore(&adc->lock, flags);
>> +}
>> +
>> +/* STM32 common extended attributes */
>> +extern const struct iio_enum stm32_adc_trig_pol;
>> +int stm32_adc_probe(struct platform_device *pdev);
>> +int stm32_adc_remove(struct platform_device *pdev);
>> +
>> +#endif
>> diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
>> new file mode 100644
>> index 0000000..147fe9c
>> --- /dev/null
>> +++ b/drivers/iio/adc/stm32/stm32f4-adc.c
>> @@ -0,0 +1,574 @@
>> +/*
>> + * This file is part of STM32F4 ADC driver
>> + *
>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>> + * Author: Fabrice Gasnier <fabrice.gasnier@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/clk.h>
>> +#include <linux/delay.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/trigger.h>
>> +#include <linux/platform_device.h>
>> +#include "stm32-adc.h"
>> +
>> +/*
>> + * STM32F4 - ADC global register map
>> + * ________________________________________________________
>> + * | Offset |                 Register                    |
>> + * --------------------------------------------------------
>> + * | 0x000  |                Master ADC1                  |
>> + * --------------------------------------------------------
>> + * | 0x100  |                Slave ADC2                   |
>> + * --------------------------------------------------------
>> + * | 0x200  |                Slave ADC3                   |
>> + * --------------------------------------------------------
>> + * | 0x300  |         Master & Slave common regs          |
>> + * --------------------------------------------------------
>> + */
>> +
>> +/* STM32F4 - Registers for each ADC instance */
>> +#define STM32F4_ADCX_SR			0x00
>> +#define STM32F4_ADCX_CR1		0x04
>> +#define STM32F4_ADCX_CR2		0x08
>> +#define STM32F4_ADCX_SMPR1		0x0C
>> +#define STM32F4_ADCX_SMPR2		0x10
>> +#define STM32F4_ADCX_HTR		0x24
>> +#define STM32F4_ADCX_LTR		0x28
>> +#define STM32F4_ADCX_SQR1		0x2C
>> +#define STM32F4_ADCX_SQR2		0x30
>> +#define STM32F4_ADCX_SQR3		0x34
>> +#define STM32F4_ADCX_JSQR		0x38
>> +#define STM32F4_ADCX_JDR1		0x3C
>> +#define STM32F4_ADCX_JDR2		0x40
>> +#define STM32F4_ADCX_JDR3		0x44
>> +#define STM32F4_ADCX_JDR4		0x48
>> +#define STM32F4_ADCX_DR			0x4C
>> +
>> +/* STM32 - Master & slave registers (common for all instances: 1, 2 & 3) */
>> +#define STM32F4_ADC_CSR			(STM32_ADCX_COMN_OFFSET + 0x00)
>> +#define STM32F4_ADC_CCR			(STM32_ADCX_COMN_OFFSET + 0x04)
>> +#define STM32F4_ADC_CDR			(STM32_ADCX_COMN_OFFSET + 0x08)
>> +
>> +/* STM32F4_ADCX_SR - bit fields */
>> +#define STM32F4_OVR			BIT(5)
>> +#define STM32F4_STRT			BIT(4)
>> +#define STM32F4_JSTRT			BIT(3)
>> +#define STM32F4_JEOC			BIT(2)
>> +#define STM32F4_EOC			BIT(1)
>> +#define STM32F4_AWD			BIT(0)
>> +
>> +/* STM32F4_ADCX_CR1 - bit fields */
>> +#define STM32F4_OVRIE			BIT(26)
>> +#define STM32F4_RES_SHIFT		24
>> +#define STM32F4_RES_MASK		GENMASK(25, 24)
>> +#define STM32F4_AWDEN			BIT(23)
>> +#define STM32F4_JAWDEN			BIT(22)
>> +#define STM32F4_DISCNUM_SHIFT		13
>> +#define STM32F4_DISCNUM_MASK		GENMASK(15, 13)
>> +#define STM32F4_JDISCEN			BIT(12)
>> +#define STM32F4_DISCEN			BIT(11)
>> +#define STM32F4_JAUTO			BIT(10)
>> +#define STM32F4_AWDSGL			BIT(9)
>> +#define STM32F4_SCAN			BIT(8)
>> +#define STM32F4_JEOCIE			BIT(7)
>> +#define STM32F4_AWDIE			BIT(6)
>> +#define STM32F4_EOCIE			BIT(5)
>> +#define STM32F4_AWDCH_SHIFT		0
>> +#define STM32F4_AWDCH_MASK		GENMASK(4, 0)
>> +
>> +/* STM32F4_ADCX_CR2 - bit fields */
>> +#define STM32F4_SWSTART			BIT(30)
>> +#define STM32F4_EXTEN_SHIFT		28
>> +#define STM32F4_EXTEN_MASK		GENMASK(29, 28)
>> +#define STM32F4_EXTSEL_SHIFT		24
>> +#define STM32F4_EXTSEL_MASK		GENMASK(27, 24)
>> +#define STM32F4_JSWSTART		BIT(22)
>> +#define STM32F4_JEXTEN_SHIFT		20
>> +#define STM32F4_JEXTEN_MASK		GENMASK(21, 20)
>> +#define STM32F4_JEXTSEL_SHIFT		16
>> +#define STM32F4_JEXTSEL_MASK		GENMASK(19, 16)
>> +#define STM32F4_ALIGN			BIT(11)
>> +#define STM32F4_EOCS			BIT(10)
>> +#define STM32F4_DDS			BIT(9)
>> +#define STM32F4_DMA			BIT(8)
>> +#define STM32F4_CONT			BIT(1)
>> +#define STM32F4_ADON			BIT(0)
>> +
>> +/* STM32F4_ADCX_SMPR1 - bit fields */
>> +#define STM32F4_SMP18_SHIFT		24
>> +#define STM32F4_SMP18_MASK		GENMASK(26, 24)
>> +#define STM32F4_SMP17_SHIFT		21
>> +#define STM32F4_SMP17_MASK		GENMASK(23, 21)
>> +#define STM32F4_SMP16_SHIFT		18
>> +#define STM32F4_SMP16_MASK		GENMASK(20, 18)
>> +#define STM32F4_SMP15_SHIFT		15
>> +#define STM32F4_SMP15_MASK		GENMASK(17, 15)
>> +#define STM32F4_SMP14_SHIFT		12
>> +#define STM32F4_SMP14_MASK		GENMASK(14, 12)
>> +#define STM32F4_SMP13_SHIFT		9
>> +#define STM32F4_SMP13_MASK		GENMASK(11, 9)
>> +#define STM32F4_SMP12_SHIFT		6
>> +#define STM32F4_SMP12_MASK		GENMASK(8, 6)
>> +#define STM32F4_SMP11_SHIFT		3
>> +#define STM32F4_SMP11_MASK		GENMASK(5, 3)
>> +#define STM32F4_SMP10_SHIFT		0
>> +#define STM32F4_SMP10_MASK		GENMASK(2, 0)
>> +
>> +/* STM32F4_ADCX_SMPR2 - bit fields */
>> +#define STM32F4_SMP9_SHIFT		27
>> +#define STM32F4_SMP9_MASK		GENMASK(29, 27)
>> +#define STM32F4_SMP8_SHIFT		24
>> +#define STM32F4_SMP8_MASK		GENMASK(26, 24)
>> +#define STM32F4_SMP7_SHIFT		21
>> +#define STM32F4_SMP7_MASK		GENMASK(23, 21)
>> +#define STM32F4_SMP6_SHIFT		18
>> +#define STM32F4_SMP6_MASK		GENMASK(20, 18)
>> +#define STM32F4_SMP5_SHIFT		15
>> +#define STM32F4_SMP5_MASK		GENMASK(17, 15)
>> +#define STM32F4_SMP4_SHIFT		12
>> +#define STM32F4_SMP4_MASK		GENMASK(14, 12)
>> +#define STM32F4_SMP3_SHIFT		9
>> +#define STM32F4_SMP3_MASK		GENMASK(11, 9)
>> +#define STM32F4_SMP2_SHIFT		6
>> +#define STM32F4_SMP2_MASK		GENMASK(8, 6)
>> +#define STM32F4_SMP1_SHIFT		3
>> +#define STM32F4_SMP1_MASK		GENMASK(5, 3)
>> +#define STM32F4_SMP0_SHIFT		0
>> +#define STM32F4_SMP0_MASK		GENMASK(2, 0)
>> +enum stm32f4_adc_smpr {
>> +	STM32F4_SMPR_3_CK_CYCLES,
>> +	STM32F4_SMPR_15_CK_CYCLES,
>> +	STM32F4_SMPR_28_CK_CYCLES,
>> +	STM32F4_SMPR_56_CK_CYCLES,
>> +	STM32F4_SMPR_84_CK_CYCLES,
>> +	STM32F4_SMPR_112_CK_CYCLES,
>> +	STM32F4_SMPR_144_CK_CYCLES,
>> +	STM32F4_SMPR_480_CK_CYCLES,
>> +};
>> +
>> +/* STM32F4_ADCX_SQR1 - bit fields */
>> +#define STM32F4_L_SHIFT			20
>> +#define STM32F4_L_MASK			GENMASK(23, 20)
>> +#define STM32F4_SQ16_SHIFT		15
>> +#define STM32F4_SQ16_MASK		GENMASK(19, 15)
>> +#define STM32F4_SQ15_SHIFT		10
>> +#define STM32F4_SQ15_MASK		GENMASK(14, 10)
>> +#define STM32F4_SQ14_SHIFT		5
>> +#define STM32F4_SQ14_MASK		GENMASK(9, 5)
>> +#define STM32F4_SQ13_SHIFT		0
>> +#define STM32F4_SQ13_MASK		GENMASK(4, 0)
>> +
>> +/* STM32F4_ADCX_SQR2 - bit fields */
>> +#define STM32F4_SQ12_SHIFT		25
>> +#define STM32F4_SQ12_MASK		GENMASK(29, 25)
>> +#define STM32F4_SQ11_SHIFT		20
>> +#define STM32F4_SQ11_MASK		GENMASK(24, 20)
>> +#define STM32F4_SQ10_SHIFT		15
>> +#define STM32F4_SQ10_MASK		GENMASK(19, 15)
>> +#define STM32F4_SQ9_SHIFT		10
>> +#define STM32F4_SQ9_MASK		GENMASK(14, 10)
>> +#define STM32F4_SQ8_SHIFT		5
>> +#define STM32F4_SQ8_MASK		GENMASK(9, 5)
>> +#define STM32F4_SQ7_SHIFT		0
>> +#define STM32F4_SQ7_MASK		GENMASK(4, 0)
>> +
>> +/* STM32F4_ADCX_SQR3 - bit fields */
>> +#define STM32F4_SQ6_SHIFT		25
>> +#define STM32F4_SQ6_MASK		GENMASK(29, 25)
>> +#define STM32F4_SQ5_SHIFT		20
>> +#define STM32F4_SQ5_MASK		GENMASK(24, 20)
>> +#define STM32F4_SQ4_SHIFT		15
>> +#define STM32F4_SQ4_MASK		GENMASK(19, 15)
>> +#define STM32F4_SQ3_SHIFT		10
>> +#define STM32F4_SQ3_MASK		GENMASK(14, 10)
>> +#define STM32F4_SQ2_SHIFT		5
>> +#define STM32F4_SQ2_MASK		GENMASK(9, 5)
>> +#define STM32F4_SQ1_SHIFT		0
>> +#define STM32F4_SQ1_MASK		GENMASK(4, 0)
>> +
>> +/* STM32F4_ADCX_JSQR - bit fields */
>> +#define STM32F4_JL_SHIFT		20
>> +#define STM32F4_JL_MASK			GENMASK(21, 20)
>> +#define STM32F4_JSQ4_SHIFT		15
>> +#define STM32F4_JSQ4_MASK		GENMASK(19, 15)
>> +#define STM32F4_JSQ3_SHIFT		10
>> +#define STM32F4_JSQ3_MASK		GENMASK(14, 10)
>> +#define STM32F4_JSQ2_SHIFT		5
>> +#define STM32F4_JSQ2_MASK		GENMASK(9, 5)
>> +#define STM32F4_JSQ1_SHIFT		0
>> +#define STM32F4_JSQ1_MASK		GENMASK(4, 0)
>> +
>> +/* STM32F4_ADC_CCR - bit fields */
>> +#define STM32F4_ADC_ADCPRE_SHIFT	16
>> +#define STM32F4_ADC_ADCPRE_MASK		GENMASK(17, 16)
>> +
>> +/*
>> + * stm32 ADC1, ADC2 & ADC3 are tightly coupled and may be used in multi mode
>> + * Define here all inputs for all ADC instances
>> + */
>> +static const struct stm32_adc_chan_spec stm32f4_adc1_channels[] = {
>> +	/* master ADC1 */
>> +	{ IIO_VOLTAGE, 0, "in0" },
>> +	{ IIO_VOLTAGE, 1, "in1" },
>> +	{ IIO_VOLTAGE, 2, "in2" },
>> +	{ IIO_VOLTAGE, 3, "in3" },
>> +	{ IIO_VOLTAGE, 4, "in4" },
>> +	{ IIO_VOLTAGE, 5, "in5" },
>> +	{ IIO_VOLTAGE, 6, "in6" },
>> +	{ IIO_VOLTAGE, 7, "in7" },
>> +	{ IIO_VOLTAGE, 8, "in8" },
>> +	{ IIO_VOLTAGE, 9, "in9" },
>> +	{ IIO_VOLTAGE, 10, "in10" },
>> +	{ IIO_VOLTAGE, 11, "in11" },
>> +	{ IIO_VOLTAGE, 12, "in12" },
>> +	{ IIO_VOLTAGE, 13, "in13" },
>> +	{ IIO_VOLTAGE, 14, "in14" },
>> +	{ IIO_VOLTAGE, 15, "in15" },
>> +	/* internal analog sources available on input 16 to 18 */
>> +	{ IIO_VOLTAGE, 16, "in16" },
>> +	{ IIO_VOLTAGE, 17, "in17" },
>> +	{ IIO_VOLTAGE, 18, "in18" },
>> +};
>> +
>> +static const struct stm32_adc_chan_spec stm32f4_adc23_channels[] = {
>> +	/* slave ADC2 /	ADC3 */
>> +	{ IIO_VOLTAGE, 0, "in0" },
>> +	{ IIO_VOLTAGE, 1, "in1" },
>> +	{ IIO_VOLTAGE, 2, "in2" },
>> +	{ IIO_VOLTAGE, 3, "in3" },
>> +	{ IIO_VOLTAGE, 4, "in4" },
>> +	{ IIO_VOLTAGE, 5, "in5" },
>> +	{ IIO_VOLTAGE, 6, "in6" },
>> +	{ IIO_VOLTAGE, 7, "in7" },
>> +	{ IIO_VOLTAGE, 8, "in8" },
>> +	{ IIO_VOLTAGE, 9, "in9" },
>> +	{ IIO_VOLTAGE, 10, "in10" },
>> +	{ IIO_VOLTAGE, 11, "in11" },
>> +	{ IIO_VOLTAGE, 12, "in12" },
>> +	{ IIO_VOLTAGE, 13, "in13" },
>> +	{ IIO_VOLTAGE, 14, "in14" },
>> +	{ IIO_VOLTAGE, 15, "in15" },
>> +};
>> +
>> +/* Triggers for regular channels */
>> +static const struct stm32_adc_trig_info stm32f4_adc_ext_triggers[] = {
>> +	{ STM32_EXT0, "TIM1_CH1" },
>> +	{ STM32_EXT1, "TIM1_CH2" },
>> +	{ STM32_EXT2, "TIM1_CH3" },
>> +	{ STM32_EXT3, "TIM2_CH2" },
>> +	{ STM32_EXT4, "TIM2_CH3" },
>> +	{ STM32_EXT5, "TIM2_CH4" },
>> +	{ STM32_EXT6, "TIM2_TRGO" },
>> +	{ STM32_EXT7, "TIM3_CH1" },
>> +	{ STM32_EXT8, "TIM3_TRGO" },
>> +	{ STM32_EXT9, "TIM4_CH4" },
>> +	{ STM32_EXT10, "TIM5_CH1" },
>> +	{ STM32_EXT11, "TIM5_CH2" },
>> +	{ STM32_EXT12, "TIM5_CH3" },
>> +	{ STM32_EXT13, "TIM8_CH1" },
>> +	{ STM32_EXT14, "TIM8_TRGO" },
>> +	{ STM32_EXT15, "EXTI_11" },
>> +	{},
>> +};
>> +
>> +/* Triggers for injected channels */
>> +static const struct stm32_adc_trig_info  stm32f4_adc_jext_triggers[] = {
>> +	{ STM32_JEXT0, "TIM1_CH4" },
>> +	{ STM32_JEXT1, "TIM1_TRGO" },
>> +	{ STM32_JEXT2, "TIM2_CH1" },
>> +	{ STM32_JEXT3, "TIM2_TRGO" },
>> +	{ STM32_JEXT4, "TIM3_CH2" },
>> +	{ STM32_JEXT5, "TIM3_CH4" },
>> +	{ STM32_JEXT6, "TIM4_CH1" },
>> +	{ STM32_JEXT7, "TIM4_CH2" },
>> +	{ STM32_JEXT8, "TIM4_CH3" },
>> +	{ STM32_JEXT9, "TIM4_TRGO" },
>> +	{ STM32_JEXT10, "TIM5_CH4" },
>> +	{ STM32_JEXT11, "TIM5_TRGO" },
>> +	{ STM32_JEXT12, "TIM8_CH2" },
>> +	{ STM32_JEXT13, "TIM8_CH3" },
>> +	{ STM32_JEXT14, "TIM8_CH4" },
>> +	{ STM32_JEXT15, "EXTI_15" },
>> +	{},
>> +};
>> +
>> +static const struct stm32_adc_info stm32f4_adc_info[] = {
>> +	{
>> +		.name = "adc1-master",
>> +		.reg = 0x0,
>> +		.channels = stm32f4_adc1_channels,
>> +		.max_channels = ARRAY_SIZE(stm32f4_adc1_channels),
>> +	},
>> +	{
>> +		.name = "adc2-slave",
>> +		.reg = 0x100,
>> +		.channels = stm32f4_adc23_channels,
>> +		.max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
>> +	},
>> +	{
>> +		.name = "adc3-slave",
>> +		.reg = 0x200,
>> +		.channels = stm32f4_adc23_channels,
>> +		.max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
>> +	},
>> +	{},
>> +};
>> +
>> +/**
>> + * stm32f4_sqr_regs - describe regular sequence registers
>> + * - L: sequence len (register & bit field)
>> + * - SQ1..SQ16: sequence entries (register & bit field)
>> + */
>> +static const struct stm32_adc_regs stm32f4_sqr_regs[STM32_ADC_MAX_SQ + 1] = {
>> +	/* L: len bit field description to be kept as first element */
>> +	{ STM32F4_ADCX_SQR1, STM32F4_L_MASK, STM32F4_L_SHIFT },
>> +	/* SQ1..SQ16 registers & bit fields */
>> +	{ STM32F4_ADCX_SQR3, STM32F4_SQ1_MASK, STM32F4_SQ1_SHIFT },
>> +	{ STM32F4_ADCX_SQR3, STM32F4_SQ2_MASK, STM32F4_SQ2_SHIFT },
>> +	{ STM32F4_ADCX_SQR3, STM32F4_SQ3_MASK, STM32F4_SQ3_SHIFT },
>> +	{ STM32F4_ADCX_SQR3, STM32F4_SQ4_MASK, STM32F4_SQ4_SHIFT },
>> +	{ STM32F4_ADCX_SQR3, STM32F4_SQ5_MASK, STM32F4_SQ5_SHIFT },
>> +	{ STM32F4_ADCX_SQR3, STM32F4_SQ6_MASK, STM32F4_SQ6_SHIFT },
>> +	{ STM32F4_ADCX_SQR2, STM32F4_SQ7_MASK, STM32F4_SQ7_SHIFT },
>> +	{ STM32F4_ADCX_SQR2, STM32F4_SQ8_MASK, STM32F4_SQ8_SHIFT },
>> +	{ STM32F4_ADCX_SQR2, STM32F4_SQ9_MASK, STM32F4_SQ9_SHIFT },
>> +	{ STM32F4_ADCX_SQR2, STM32F4_SQ10_MASK, STM32F4_SQ10_SHIFT },
>> +	{ STM32F4_ADCX_SQR2, STM32F4_SQ11_MASK, STM32F4_SQ11_SHIFT },
>> +	{ STM32F4_ADCX_SQR2, STM32F4_SQ12_MASK, STM32F4_SQ12_SHIFT },
>> +	{ STM32F4_ADCX_SQR1, STM32F4_SQ13_MASK, STM32F4_SQ13_SHIFT },
>> +	{ STM32F4_ADCX_SQR1, STM32F4_SQ14_MASK, STM32F4_SQ14_SHIFT },
>> +	{ STM32F4_ADCX_SQR1, STM32F4_SQ15_MASK, STM32F4_SQ15_SHIFT },
>> +	{ STM32F4_ADCX_SQR1, STM32F4_SQ16_MASK, STM32F4_SQ16_SHIFT },
>> +};
>> +
>> +/**
>> + * stm32f4_jsqr_reg - describe injected sequence register:
>> + * - JL: injected sequence len
>> + * - JSQ4..SQ1: sequence entries
>> + * When JL == 3, ADC converts JSQ1, JSQ2, JSQ3, JSQ4
>> + * When JL == 2, ADC converts JSQ2, JSQ3, JSQ4
>> + * When JL == 1, ADC converts JSQ3, JSQ4
>> + * When JL == 0, ADC converts JSQ4
>> + */
>> +static const struct stm32_adc_regs stm32f4_jsqr_reg[STM32_ADC_MAX_JSQ + 1] = {
>> +	/* JL: len bit field description to be kept as first element */
>> +	{STM32F4_ADCX_JSQR, STM32F4_JL_MASK, STM32F4_JL_SHIFT},
>> +	/* JSQ4..JSQ1 registers & bit fields */
>> +	{STM32F4_ADCX_JSQR, STM32F4_JSQ4_MASK, STM32F4_JSQ4_SHIFT},
>> +	{STM32F4_ADCX_JSQR, STM32F4_JSQ3_MASK, STM32F4_JSQ3_SHIFT},
>> +	{STM32F4_ADCX_JSQR, STM32F4_JSQ2_MASK, STM32F4_JSQ2_SHIFT},
>> +	{STM32F4_ADCX_JSQR, STM32F4_JSQ1_MASK, STM32F4_JSQ1_SHIFT},
>> +};
>> +
>> +static const struct stm32_adc_trig_reginfo stm32f4_adc_trig_reginfo = {
>> +	.reg = STM32F4_ADCX_CR2,
>> +	.exten_mask = STM32F4_EXTEN_MASK,
>> +	.exten_shift = STM32F4_EXTEN_SHIFT,
>> +	.extsel_mask = STM32F4_EXTSEL_MASK,
>> +	.extsel_shift = STM32F4_EXTSEL_SHIFT,
>> +};
>> +
>> +static const struct stm32_adc_trig_reginfo stm32f4_adc_jtrig_reginfo = {
>> +	.reg = STM32F4_ADCX_CR2,
>> +	.exten_mask = STM32F4_JEXTEN_MASK,
>> +	.exten_shift = STM32F4_JEXTEN_SHIFT,
>> +	.extsel_mask = STM32F4_JEXTSEL_MASK,
>> +	.extsel_shift = STM32F4_JEXTSEL_SHIFT,
>> +};
>> +
>> +static const struct stm32_adc_reginfo stm32f4_adc_reginfo = {
>> +	.isr = STM32F4_ADCX_SR,
>> +	.eoc = STM32F4_EOC,
>> +	.jeoc = STM32F4_JEOC,
>> +	.ier = STM32F4_ADCX_CR1,
>> +	.eocie = STM32F4_EOCIE,
>> +	.jeocie = STM32F4_JEOCIE,
>> +	.dr = STM32F4_ADCX_DR,
>> +	.jdr = {
>> +		STM32F4_ADCX_JDR1,
>> +		STM32F4_ADCX_JDR2,
>> +		STM32F4_ADCX_JDR3,
>> +		STM32F4_ADCX_JDR4,
>> +	},
>> +	.sqr_regs = stm32f4_sqr_regs,
>> +	.jsqr_reg = stm32f4_jsqr_reg,
>> +	.trig_reginfo = &stm32f4_adc_trig_reginfo,
>> +	.jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
>> +};
>> +
>> +static bool stm32f4_adc_is_started(struct stm32_adc *adc)
>> +{
>> +	u32 val = stm32_adc_readl(adc, STM32F4_ADCX_CR2) & STM32F4_ADON;
>> +
>> +	return !!val;
>> +}
>> +
>> +static bool stm32f4_adc_regular_started(struct stm32_adc *adc)
>> +{
>> +	u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_STRT;
>> +
>> +	return !!val;
>> +}
>> +
>> +static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
>> +{
>> +	u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_JSTRT;
>> +
>> +	return !!val;
>> +}
>> +
>> +/**
>> + * stm32f4_adc_start_conv() - Start regular or injected conversions
>> + * @adc: stm32 adc instance
>> + *
>> + * Start single conversions for regular or injected channels.
>> + */
>> +static int stm32f4_adc_start_conv(struct stm32_adc *adc)
>> +{
>> +	u32 trig_msk, start_msk;
>> +
>> +	dev_dbg(adc->common->dev, "%s %s\n", __func__,
>> +		adc->injected ? "injected" : "regular");
>> +
>> +	stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
>> +
>> +	if (!stm32f4_adc_is_started(adc)) {
>> +		stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
>> +				   STM32F4_EOCS | STM32F4_ADON);
>> +
>> +		/* Wait for Power-up time (tSTAB from datasheet) */
>> +		usleep_range(2, 3);
>> +	}
>> +
>> +	if (adc->injected) {
>> +		trig_msk = STM32F4_JEXTEN_MASK;
>> +		start_msk = STM32F4_JSWSTART;
>> +	} else {
>> +		trig_msk = STM32F4_EXTEN_MASK;
>> +		start_msk = STM32F4_SWSTART;
>> +	}
>> +
>> +	/* Software start ? (e.g. trigger detection disabled ?) */
>> +	if (!(stm32_adc_readl(adc, STM32F4_ADCX_CR2) & trig_msk))
>> +		stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, start_msk);
>> +
>> +	return 0;
>> +}
>> +
>> +static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
>> +{
>> +	u32 val;
>> +
>> +	dev_dbg(adc->common->dev, "%s %s\n", __func__,
>> +		adc->injected ? "injected" : "regular");
>> +
>> +	/* First disable trigger for either regular or injected channels */
>> +	if (adc->injected) {
>> +		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_JEXTEN_MASK);
>> +		stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_JSTRT);
>> +	} else {
>> +		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
>> +		stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_STRT);
>> +	}
>> +
>> +	/* Disable adc when all triggered conversion have been disabled */
>> +	val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
>> +	val &= STM32F4_EXTEN_MASK | STM32F4_JEXTEN_MASK;
>> +	if (!val) {
>> +		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
>> +		stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +/* ADC internal common clock prescaler division ratios */
>> +static int stm32f4_pclk_div[] = {2, 4, 6, 8};
>> +
>> +/**
>> + * stm32f4_adc_clk_sel() - Select ADC common clock prescaler
>> + * @adc: stm32 adc instance
>> + * Select clock prescaler used for analog conversions.
>> + */
>> +static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
>> +{
>> +	struct stm32_adc_common *common = adc->common;
>> +	unsigned long rate;
>> +	u32 val;
>> +	int i;
>> +
>> +	/* Common prescaler is set only once, when 1st ADC instance starts */
>> +	list_for_each_entry(adc, &common->adc_list, adc_list)
>> +		if (stm32f4_adc_is_started(adc))
>> +			return 0;
>> +
>> +	rate = clk_get_rate(common->aclk);
>> +	for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
>> +		if ((rate / stm32f4_pclk_div[i]) <=
>> +		    common->data->max_clock_rate)
>> +			break;
>> +	}
>> +	if (i >= ARRAY_SIZE(stm32f4_pclk_div))
>> +		return -EINVAL;
>> +
>> +	val = stm32_adc_common_readl(common, STM32F4_ADC_CCR);
>> +	val &= ~STM32F4_ADC_ADCPRE_MASK;
>> +	val |= i << STM32F4_ADC_ADCPRE_SHIFT;
>> +	stm32_adc_common_writel(common, STM32F4_ADC_CCR, val);
>> +
>> +	dev_dbg(common->dev, "Using analog clock source at %ld kHz\n",
>> +		rate / (stm32f4_pclk_div[i] * 1000));
>> +
>> +	return 0;
>> +}
>> +
>> +static const struct stm32_adc_ops stm32f4_adc_ops = {
>> +	.adc_info = stm32f4_adc_info,
>> +	.ext_triggers = stm32f4_adc_ext_triggers,
>> +	.jext_triggers = stm32f4_adc_jext_triggers,
>> +	.adc_reginfo = &stm32f4_adc_reginfo,
>> +	.highres = 12,
>> +	.max_clock_rate = 36000000,
>> +	.clk_sel = stm32f4_adc_clk_sel,
>> +	.start_conv = stm32f4_adc_start_conv,
>> +	.stop_conv = stm32f4_adc_stop_conv,
>> +	.is_started = stm32f4_adc_is_started,
>> +	.regular_started = stm32f4_adc_regular_started,
>> +	.injected_started = stm32f4_adc_injected_started,
>> +};
>> +
>> +static const struct of_device_id stm32f4_adc_of_match[] = {
>> +	{ .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_ops},
>> +	{},
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32f4_adc_of_match);
>> +
>> +static struct platform_driver stm32f4_adc_driver = {
>> +	.probe = stm32_adc_probe,
>> +	.remove = stm32_adc_remove,
>> +	.driver = {
>> +		.name = "stm32f4-adc",
>> +		.of_match_table = stm32f4_adc_of_match,
>> +	},
>> +};
>> +
>> +module_platform_driver(stm32f4_adc_driver);
>> +
>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32F4 ADC driver");
>> +MODULE_LICENSE("GPL v2");
>>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 01/10] Documentation: dt-bindings: Document STM32 ADC DT bindings
  2016-10-31  3:02   ` Rob Herring
@ 2016-11-03 11:11     ` Fabrice Gasnier
  0 siblings, 0 replies; 20+ messages in thread
From: Fabrice Gasnier @ 2016-11-03 11:11 UTC (permalink / raw)
  To: Rob Herring
  Cc: linux-iio, linux-arm-kernel, devicetree, linux-kernel, jic23,
	linux, mark.rutland, mcoquelin.stm32, alexandre.torgue, lars,
	knaack.h, pmeerw

On 10/31/2016 04:02 AM, Rob Herring wrote:
> On Tue, Oct 25, 2016 at 06:25:13PM +0200, Fabrice Gasnier wrote:
>> This patch adds documentation of device tree bindings for the STM32 ADC.
>>
>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
>> ---
>>   .../devicetree/bindings/iio/adc/st,stm32-adc.txt   | 78 ++++++++++++++++++++++
>>   1 file changed, 78 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
>>
>> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
>> new file mode 100644
>> index 0000000..a9a8b3c
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
>> @@ -0,0 +1,78 @@
>> +STMicroelectronics STM32 ADC device driver
>> +
>> +STM32 ADC is a successive approximation analog-to-digital converter.
>> +It has several multiplexed input channels. Conversions can be performed
>> +in single, continuous, scan or discontinuous mode. Result of the ADC is
>> +stored in a left-aligned or right-aligned 32-bit data register.
>> +Conversions can be launched in software or using hardware triggers.
>> +
>> +The analog watchdog feature allows the application to detect if the input
>> +voltage goes beyond the user-defined, higher or lower thresholds.
>> +
>> +Each STM32 ADC block can have up to 3 ADC instances.
>> +
>> +Each instance supports two contexts to manage conversions, each one has its
>> +own configurable sequence and trigger:
>> +- regular conversion can be done in sequence, running in background
>> +- injected conversions have higher priority, and so have the ability to
>> +  interrupt regular conversion sequence (either triggered in SW or HW).
>> +  Regular sequence is resumed, in case it has been interrupted.
>> +
>> +Required properties:
>> +- compatible: Should be "st,stm32f4-adc".
>> +- reg: Offset and length of the ADC block register set.
>> +- interrupts: Must contain the interrupt for ADC.
>> +- clocks: Clock for the analog circuitry (common to all ADCs).
>> +- clock-names: Must be "adc".
>> +- vref-supply: Phandle to the vref input analog reference voltage.
>> +- #address-cells = <1>;
>> +- #size-cells = <0>;
>> +
>> +Optional properties:
>> +- A pinctrl state named "default" for each ADC channel may be defined to set
>> +  inX ADC pins in mode of operation for analog input on external pin.
>> +- gpios: Array of gpios that may be configured as EXTi trigger sources.
>> +
>> +Example:
> This should be last.
Hi Rob,

I'll fix this.
>
>> +	adc: adc@40012000 {
>> +		compatible = "st,stm32f4-adc";
>> +		reg = <0x40012000 0x400>;
>> +		interrupts = <18>;
>> +		clocks = <&rcc 0 168>;
>> +		clock-names = "adc";
>> +		vref-supply = <&reg_vref>;
>> +		pinctrl-names = "default";
>> +		pinctrl-0 = <&adc3_in8_pin>;
>> +		gpios = <&gpioa 11 0>,
>> +			<&gpioa 15 0>;
>> +
>> +		#address-cells = <1>;
>> +		#size-cells = <0>;
>> +
>> +		adc1: adc1-master@0 {
> adc@0 sufficient?
Yes, if you agree, I'd go for adc1@0, adc2@100, adc3@200, to reflect reg 
property for child node.
Is it ok from your point of view ?

>
>> +			#io-channel-cells = <1>;
>> +			reg = <0x0>;
>> +			clocks = <&rcc 0 168>;
>> +			st,adc-channels = <8>;
>> +		};
>> +		...
>> +		other adc child nodes follow...
>> +	};
>> +
>> +Contents of a stm32 adc child node:
>> +-----------------------------------
>> +An ADC block node should contain at least one subnode, representing an
>> +ADC instance available on the machine.
>> +
>> +Required properties:
>> +- reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
>> +- st,adc-channels: List of single-ended channels muxed for this ADC.
> How many? What are valid values?
stm32f4 can have up to 19 channels, numbered from 0 to 18 to match with 
reference manual.
I'll add this.
>
>> +- #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
>> +  Documentation/devicetree/bindings/iio/iio-bindings.txt
>> +
>> +Optional properties:
>> +- clocks: Input clock private to this ADC instance.
>> +- st,injected: Use injected conversion sequence on an ADC, rather than regular.
> Not sure about this one. Seems like this would either be a user choice
> or depend on what's connected to the ADC.
It's related to ADC sequencer, and the way it's being configured/used 
(see above paragraph on regular/injected).
This is not related to what's connected to adc inputs.
As suggested by Jonathan, I think I'll drop injected support for now, to 
simplify the driver and review.

Thanks,
Best Regards,
Fabrice
>
>> +- dmas: Phandle to dma channel for this ADC instance, only for regular
>> +  conversions. See ../../dma/dma.txt for details.
>> +- dma-names: Must be "rx" when dmas property is being used.
>> -- 
>> 1.9.1
>>

^ permalink raw reply	[flat|nested] 20+ messages in thread

* Re: [PATCH 02/10] iio: adc: Add stm32 support
  2016-11-03  8:20     ` Fabrice Gasnier
@ 2016-11-05 15:44       ` Jonathan Cameron
  0 siblings, 0 replies; 20+ messages in thread
From: Jonathan Cameron @ 2016-11-05 15:44 UTC (permalink / raw)
  To: Fabrice Gasnier, linux-iio, linux-arm-kernel, devicetree, linux-kernel
  Cc: linux, robh+dt, mark.rutland, mcoquelin.stm32, alexandre.torgue,
	lars, knaack.h, pmeerw, Benjamin GAIGNARD

On 03/11/16 08:20, Fabrice Gasnier wrote:
> On 10/30/2016 04:27 PM, Jonathan Cameron wrote:
>> On 25/10/16 17:25, Fabrice Gasnier wrote:
>>> This patch adds support for STMicroelectronics STM32 MCU's analog to
>>> digital converter.
>>>
>>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
>> Hi Fabrice,
>>
>> Sometimes I hate SoC ADCs.  For some reason the hardware designers seem to
>> try and throw everything and the kitchen sink at them.  Discontinuous mode
>> as an example in this device.  Not seen that particular piece of fun before
>> and glad to see you haven't 'yet' tried to support it!
>>
>> Anyhow, the complexity of the hardware leads to an initially complex driver.
>> My first thought it that this would be easier to follow / review if we
>> built it up in smaller steps.   Perhaps ditch the injected channel support
>> entirely in the first instance.  I also wonder if you don't need to support
>> that whole thing (injected sampling) as another iio device entirely using the
>> same channels.  That's kind of what it is from a data flow point of view
>> (we've had arbitary sequencers before with priorities - don't think anyone
>> ever decided the pain was worth supporting the complexity, but right answer
>> has always been multiple IIO devices).
> Hi Jonathan,
> 
> First, many thanks for your review. I agree with you, most reasonable approach is to remove some
> complexity to ease the review. Regarding injected support, basically, bellow approach is to use
> separate IIO devices for regular and injected. But, I'll remove this, at least for now, in next patch set.
> 
>> You also have at least one layer of abstraction in here that serves no
>> current purpose.  Please clear that out for now. It'll make the code
>> shorter and easier to follow.  If/when other parts are introduced then
>> is the time to do that transistion to having the abstraction.
> 
> From your suggestion, this may end-up in a single driver file in drivers/iio.
> I think I'll try to keep simple routines like start, stop, conf_scan and so on, but
> remove indirection routines from stm32-adc.h file (e.g. stm32_adc_ops).
> Is it in line with your suggestions ?
Sure, some of those will want to be in their own functions so it sounds
about right.
> 
>>
>> My first thought on the double / tripple adc handling is that you'd be better
>> off handling them as 3 separate devices then doing some 'unusual' trigger
>> handling to support the weird sequencing.  Guessing you thought about that?
>> If so could you lay out your reasoning for the single driver instance approach.
>> I'm not arguing against it btw, merely want to understand your reasoning!
> 
> I mainly came up with a single driver instance approach because there are basically
> 3 identical ADC instances 'mapped' in a single IP with few common resources.
> I usually see mfd are more heterogeneous and declare cells for various subsystem drivers.
> But I can try to move to mfd as you're suggesting.
> I just hope this will not bring more complexity.
If anything I suspect it'll end up simpler to read (be it a tiny bit longer
in terms of lines of code).
> 
>>
>> It would be tricky given one set of channels are selectable over 3 devices
>> and there are constraints to enforce (not sampling same channel on two ADCs
>> at the same time) but not impossible...  Perhaps what you have here is
>> indeed simpler!
>>
>> Whilst it's been a nasty job to review, I'm guessing writing it was
>> much worse ;)  Pretty good starting point though might take a little while
>> to pin down the remaining questions on how best to handle this particular
>> monster.
> My apologies... I hope you didn't had much of a headache :-) by reading me.
> More questions bellow.
> 
>> Jonathan
>>> ---
>>>   drivers/iio/adc/Kconfig             |   2 +
>>>   drivers/iio/adc/Makefile            |   1 +
>>>   drivers/iio/adc/stm32/Kconfig       |  34 ++
>>>   drivers/iio/adc/stm32/Makefile      |   4 +
>>>   drivers/iio/adc/stm32/stm32-adc.c   | 999 ++++++++++++++++++++++++++++++++++++
>>>   drivers/iio/adc/stm32/stm32-adc.h   | 442 ++++++++++++++++
>>>   drivers/iio/adc/stm32/stm32f4-adc.c | 574 +++++++++++++++++++++
>>>   7 files changed, 2056 insertions(+)
>>>   create mode 100644 drivers/iio/adc/stm32/Kconfig
>>>   create mode 100644 drivers/iio/adc/stm32/Makefile
>>>   create mode 100644 drivers/iio/adc/stm32/stm32-adc.c
>>>   create mode 100644 drivers/iio/adc/stm32/stm32-adc.h
>>>   create mode 100644 drivers/iio/adc/stm32/stm32f4-adc.c
>>>
>>> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
>>> index 7edcf32..5c96a55 100644
>>> --- a/drivers/iio/adc/Kconfig
>>> +++ b/drivers/iio/adc/Kconfig
>>> @@ -583,4 +583,6 @@ config XILINX_XADC
>>>         The driver can also be build as a module. If so, the module will be called
>>>         xilinx-xadc.
>>>   +source "drivers/iio/adc/stm32/Kconfig"
>>> +
>>>   endmenu
>>> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
>>> index 7a40c04..a9dbf3a 100644
>>> --- a/drivers/iio/adc/Makefile
>>> +++ b/drivers/iio/adc/Makefile
>>> @@ -13,6 +13,7 @@ obj-$(CONFIG_AD7791) += ad7791.o
>>>   obj-$(CONFIG_AD7793) += ad7793.o
>>>   obj-$(CONFIG_AD7887) += ad7887.o
>>>   obj-$(CONFIG_AD799X) += ad799x.o
>>> +obj-$(CONFIG_ARCH_STM32) += stm32/
>>>   obj-$(CONFIG_AT91_ADC) += at91_adc.o
>>>   obj-$(CONFIG_AT91_SAMA5D2_ADC) += at91-sama5d2_adc.o
>>>   obj-$(CONFIG_AXP288_ADC) += axp288_adc.o
>>> diff --git a/drivers/iio/adc/stm32/Kconfig b/drivers/iio/adc/stm32/Kconfig
>>> new file mode 100644
>>> index 0000000..245d037
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32/Kconfig
>>> @@ -0,0 +1,34 @@
>>> +#
>>> +# STM32 familly ADC drivers
>>> +#
>>> +
>>> +config STM32_ADC
>>> +    tristate
>>> +    select REGULATOR
>>> +    select REGULATOR_FIXED_VOLTAGE
>>> +    select IIO_BUFFER
>>> +    select IIO_TRIGGERED_BUFFER
>>> +    help
>>> +      Say yes here to build the driver for the STMicroelectronics
>>> +      STM32 analog-to-digital converter (ADC).
>>> +
>>> +      This driver can also be built as a module.  If so, the module
>>> +      will be called stm32-adc.
>>> +
>>> +config STM32F4_ADC
>>> +    tristate "STMicroelectronics STM32F4 adc"
>>> +    depends on ARCH_STM32 || COMPILE_TEST
>>> +    depends on OF
>>> +    select STM32_ADC
>>> +    help
>>> +      Say yes here to build support for STMicroelectronics stm32f4 Analog
>>> +      to Digital Converter (ADC).
>>> +
>>> +      This driver can also be built as a module.  If so, the module
>>> +      will be called stm32f4-adc.
>>> +
>>> +config STM32_ADC_DEBUG
>>> +    bool "Enable debug for stm32 ADC drivers"
>>> +    depends on STM32_ADC
>>> +    help
>>> +      Say "yes" to enable debug messages, on stm32 ADC drivers.
>>> diff --git a/drivers/iio/adc/stm32/Makefile b/drivers/iio/adc/stm32/Makefile
>>> new file mode 100644
>>> index 0000000..83e8154
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32/Makefile
>>> @@ -0,0 +1,4 @@
>>> +# Core
>>> +subdir-ccflags-$(CONFIG_STM32_ADC_DEBUG) := -DDEBUG
>>> +obj-$(CONFIG_STM32_ADC) += stm32-adc.o
>>> +obj-$(CONFIG_STM32F4_ADC) += stm32f4-adc.o
>>> diff --git a/drivers/iio/adc/stm32/stm32-adc.c b/drivers/iio/adc/stm32/stm32-adc.c
>>> new file mode 100644
>>> index 0000000..1e0850d
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32/stm32-adc.c
>>> @@ -0,0 +1,999 @@
> [snip]
> 
>>> +
>>> +static int stm32_adc_conf_scan(struct iio_dev *indio_dev,
>>> +                   const unsigned long *scan_mask)
>>> +{
>>> +    struct stm32_adc *adc = iio_priv(indio_dev);
>>> +    int ret;
>>> +
>>> +    ret = stm32_adc_clk_sel(adc);
>>> +    if (ret) {
>>> +        dev_err(&indio_dev->dev, "Clock sel failed\n");
>>> +        return ret;
>>> +    }
>>> +
>>> +    ret = stm32_adc_enable(adc);
>>> +    if (ret) {
>>> +        dev_err(&indio_dev->dev, "Failed to enable adc\n");
>>> +        return ret;
>>> +    }
>>> +
>>> +    ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
>>> +    if (ret) {
>>> +        dev_err(&indio_dev->dev, "Failed to configure sequence\n");
>>> +        goto err_dis;
>>> +    }
>> It's horrible but to end up in the 'obvious' state I'd disable the adc
>> again assuming that doesn't kill the stuff that is configured.
> I'll check this and try to come up with something.
> 
>>> +
>>> +    return 0;
>>> +
>>> +err_dis:
>>> +    stm32_adc_disable(adc);
>>> +
>>> +    return ret;
>>> +}
>>> +
> [snip]
>>> +/**
>>> + * stm32_adc_single_conv() - perform a single conversion
>>> + * @indio_dev: IIO device
>>> + * @chan: IIO channel
>>> + * @result: conversion result
>>> + *
>>> + * The function performs a single conversion on a given channel, by
>>> + * by:
>>> + * - creating scan mask with only one channel
>>> + * - using SW trigger
>>> + * - then start single conv
>>> + */
>>> +static int stm32_adc_single_conv(struct iio_dev *indio_dev,
>>> +                 const struct iio_chan_spec *chan,
>>> +                 int *val)
>>> +{
>>> +    struct stm32_adc *adc = iio_priv(indio_dev);
>>> +    unsigned long *scan_mask;
>>> +    long timeout;
>>> +    u16 result;
>>> +    int ret;
>>> +
>>> +    scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
>>> +                GFP_KERNEL);
>> This is known maximum length... I'd just avoid the complexity of allocating
>> it like this - a comment would do the job to say it is the right length.
> Do you suggest to use a predefined variable (like unsigned long scan_mask) directly ?
> And add a more basic test on 'masklength', to be sure ?
A test would make sense as would prevent any problems if this driver
is extended to support much larger devices in future.
> 
>>> +    if (!scan_mask)
>>> +        return -ENOMEM;
>>> +
>>> +    set_bit(chan->scan_index, scan_mask);
>>> +
>>> +    reinit_completion(&adc->completion);
>>> +
>>> +    adc->bufi = 0;
>>> +    adc->num_conv = 1;
>>> +    adc->buffer = &result;
>>> +
>>> +    ret = stm32_adc_conf_scan(indio_dev, scan_mask);
>>> +    if (ret)
>>> +        goto free;
>>> +
>>> +    /* No HW trigger: conversion can be launched in SW */
>>> +    ret = stm32_adc_set_trig(indio_dev, NULL);
>> Put it back again afterwards?  Otherwise some nasty race conditions look
>> likely to me.. (userspace sets trigger and is about to enable the buffer
>> when along comes this code and changes it underneath).
> I'll fix this.
> 
>>> +    if (ret) {
>>> +        dev_err(&indio_dev->dev, "Can't set SW trigger\n");
>>> +        goto adc_disable;
>>> +    }
>>> +
>>> +    stm32_adc_conv_irq_enable(adc);
>>> +
>>> +    ret = stm32_adc_start_conv(adc);
>>> +    if (ret) {
>>> +        dev_err(&indio_dev->dev, "Failed to start single conv\n");
>>> +        goto irq_disable;
>>> +    }
>>> +
>>> +    timeout = wait_for_completion_interruptible_timeout(
>>> +                    &adc->completion, STM32_ADC_TIMEOUT);
>>> +    if (timeout == 0) {
>>> +        dev_warn(&indio_dev->dev, "Conversion timed out!\n");
>>> +        ret = -ETIMEDOUT;
>>> +    } else if (timeout < 0) {
>>> +        dev_warn(&indio_dev->dev, "Interrupted conversion!\n");
>>> +        ret = -EINTR;
>>> +    } else {
>>> +        *val = result & STM32_RESULT_MASK;
>>> +        ret = IIO_VAL_INT;
>>> +    }
>>> +
>>> +    if (stm32_adc_stop_conv(adc))
>>> +        dev_err(&indio_dev->dev, "stop failed\n");
>>> +
>>> +irq_disable:
>>> +    stm32_adc_conv_irq_disable(adc);
>>> +
>>> +adc_disable:
>>> +    stm32_adc_disable(adc);
>>> +
>>> +free:
>>> +    kfree(scan_mask);
>>> +    adc->buffer = NULL;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static int stm32_adc_read_raw(struct iio_dev *indio_dev,
>>> +                  struct iio_chan_spec const *chan,
>>> +                  int *val, int *val2, long mask)
>>> +{
>>> +    struct stm32_adc *adc = iio_priv(indio_dev);
>>> +    int ret = -EINVAL;
>>> +
>>> +    switch (mask) {
>>> +    case IIO_CHAN_INFO_RAW:
>>> +        ret = iio_device_claim_direct_mode(indio_dev);
>>> +        if (ret)
>>> +            return ret;
>>> +        if (chan->type == IIO_VOLTAGE)
>>> +            ret = stm32_adc_single_conv(indio_dev, chan, val);
>>> +        iio_device_release_direct_mode(indio_dev);
>>> +        break;
>>> +    case IIO_CHAN_INFO_SCALE:
>>> +        *val = adc->common->vref_mv;
>>> +        *val2 = chan->scan_type.realbits;
>>> +        ret = IIO_VAL_FRACTIONAL_LOG2;
>>> +        break;
>>> +    default:
>>> +        break;
>>> +    }
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +/**
>>> + * stm32_adc_isr() - Treat interrupt for one ADC instance within ADC block
>> As this is kernel doc please document the parameter as well.  Otherwise
>> we'll get a pile of warnings!
> Sure.
>>> + */
>>> +static irqreturn_t stm32_adc_isr(struct stm32_adc *adc)
>>> +{
>>> +    struct iio_dev *indio_dev = iio_priv_to_dev(adc);
>>> +    const struct stm32_adc_reginfo *reginfo =
>>> +        adc->common->data->adc_reginfo;
>>> +    u32 mask, clr_mask, status = stm32_adc_readl(adc, reginfo->isr);
>>> +
>>> +    if (adc->injected) {
>>> +        mask = reginfo->jeoc;
>>> +        clr_mask = mask;
>>> +    } else {
>>> +        mask = reginfo->eoc;
>>> +        /* don't clear 'eoc' as it is cleared when reading 'dr' */
>>> +        clr_mask = 0;
>>> +    }
>>> +
>>> +    /* clear irq */
>>> +    stm32_adc_writel(adc, reginfo->isr, status & ~clr_mask);
>> Want to do this in the non injected case? it's a noop isn't it?
> I'll rework this.
> 
>>
>>> +    status &= mask;
>>> +
>>> +    /* Regular data */
>>> +    if (status & reginfo->eoc) {
>> Hmm.. this is a little bit of 'missuse' of the standard trigger architecture
>> but as long as it's restricted to just this device I don't suppose we need
>> to care.  Only reason we need it is to provide control of 'which' hardware
>> trigger is being used.
>>
>> Guessing the DMA will almost always be turned on and will make this oddity
>> effectively disappear.
> I'm not sure to understand your remark. Above test checks end of conversion status flag.
> Or do you talk about bellow lines ? Can you please clarify ?
Took me  a while to figure this out. (i.e. I wasn't sure what I meant either!)

This setup corresponds (more or less) to having an external trigger fire
off a software based sequencer.  So we'd expect the 'loop' element of this
to run in the buffer handler rather than the trigger handler.  In theory
that would allow other triggers to be used as well as the ones supported
in hardware.

Here that is somewhat of a pain however.  If there weren't multiple triggers
to select between I'd just suggest dropping the trigger interface entirely
(it's optional) but then we'd have to do something custom to select
which of the device supplied triggers to use.

Hence probably best plan is leave it as it is.  Sometimes hardware
just doesn't fit the conceptual model we have for it!
> 
>>> +        adc->buffer[adc->bufi] = stm32_adc_readl(adc, reginfo->dr);
>>> +        if (iio_buffer_enabled(indio_dev)) {
>>> +            adc->bufi++;
>>> +            if (adc->bufi >= adc->num_conv) {
>>> +                stm32_adc_conv_irq_disable(adc);
>>> +                iio_trigger_poll(indio_dev->trig);
>>> +            }
>>> +        } else {
>>> +            complete(&adc->completion);
>>> +        }
>>> +    }
>>> +
>>> +    /* Injected data */
>>> +    if (status & reginfo->jeoc) {
>>> +        int i;
>>> +
>>> +        for (i = 0; i < adc->num_conv; i++) {
>>> +            adc->buffer[i] = stm32_adc_readl(adc, reginfo->jdr[i]);
>>> +            adc->bufi++;
>>> +        }
>>> +
>>> +        if (iio_buffer_enabled(indio_dev)) {
>>> +            stm32_adc_conv_irq_disable(adc);
>>> +            iio_trigger_poll(indio_dev->trig);
>>> +        } else {
>>> +            complete(&adc->completion);
>>> +        }
>>> +    }
>>> +
>>> +    /*
>>> +     * In case end of conversion flags have been handled, this has been
>>> +     * handled for this ADC instance
>>> +     */
>>> +    if (status)
>>> +        return IRQ_HANDLED;
>>> +
>>> +    /* This adc instance didn't trigger this interrupt */
>>> +    return IRQ_NONE;
>>> +}
>>> +
>>> +/**
>>> + * stm32_adc_common_isr() - Common isr for the whole ADC block
>>> + *
>>> + * There is one IRQ for all ADCs in ADC block, check all instances.
>>> + */
>>> +static irqreturn_t stm32_adc_common_isr(int irq, void *data)
>>> +{
>>> +    struct stm32_adc_common *common = data;
>>> +    irqreturn_t ret = IRQ_NONE;
>>> +    struct stm32_adc *adc;
>>> +
>>> +    list_for_each_entry(adc, &common->adc_list, adc_list)
>>> +        ret |= stm32_adc_isr(adc);
>> Hmm.. ret |= is rather fragile.  Preferable to make the handling of NONE
>> vs IRQ_HANDLED explicit.
>>
>> If you were to split the driver up as I suggested might make sense above,
>> then this would be done with an irq chip in a top level device (effectively
>> a very simple mfd).
> I'll look into it.
> 
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +/**
>>> + * stm32_adc_validate_trigger() - validate trigger for stm32 adc
>>> + * @indio_dev: IIO device
>>> + * @trig: new trigger
>>> + *
>>> + * Returns: 0 if trig matches one of the triggers registered by stm32 adc
>>> + * driver, -EINVAL otherwise.
>>> + */
>>> +static int stm32_adc_validate_trigger(struct iio_dev *indio_dev,
>>> +                      struct iio_trigger *trig)
>>> +{
>>> +    return stm32_adc_get_trig_index(indio_dev, trig) < 0 ? -EINVAL : 0;
>>> +}
>>> +
>>> +static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
>>> +                      const unsigned long *scan_mask)
>> I'm glad you kept this relatively simple compared to some of the
>> 'fun' the hardware is capable of. Very wise!
>>> +{
>>> +    struct stm32_adc *adc = iio_priv(indio_dev);
>>> +    int ret;
>>> +    u32 bit;
>>> +
>>> +    adc->num_conv = 0;
>>> +    for_each_set_bit(bit, scan_mask, indio_dev->masklength)
>>> +        adc->num_conv++;
>>> +
>>> +    ret = stm32_adc_conf_scan(indio_dev, scan_mask);
>>> +    if (ret)
>>> +        return ret;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
>>> +                  const struct of_phandle_args *iiospec)
>>> +{
>>> +    int i;
>>> +
>>> +    for (i = 0; i < indio_dev->num_channels; i++)
>>> +        if (indio_dev->channels[i].channel == iiospec->args[0])
>>> +            return i;
>>> +
>>> +    return -EINVAL;
>>> +}
>>> +
>>> +/**
>>> + * stm32_adc_debugfs_reg_access - read or write register value
>>> + *
>>> + * To read a value from an ADC register:
>>> + *   echo [ADC reg offset] > direct_reg_access
>>> + *   cat direct_reg_access
>>> + *
>>> + * To write a value in a ADC register:
>>> + *   echo [ADC_reg_offset] [value] > direct_reg_access
>>> + */
>>> +static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
>>> +                    unsigned reg, unsigned writeval,
>>> +                    unsigned *readval)
>>> +{
>>> +    struct stm32_adc *adc = iio_priv(indio_dev);
>>> +
>>> +    if (!readval)
>>> +        stm32_adc_writel(adc, reg, writeval);
>>> +    else
>>> +        *readval = stm32_adc_readl(adc, reg);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static const struct iio_info stm32_adc_iio_info = {
>>> +    .read_raw = stm32_adc_read_raw,
>>> +    .validate_trigger = stm32_adc_validate_trigger,
>>> +    .update_scan_mode = stm32_adc_update_scan_mode,
>>> +    .debugfs_reg_access = stm32_adc_debugfs_reg_access,
>>> +    .of_xlate = stm32_adc_of_xlate,
>>> +    .driver_module = THIS_MODULE,
>>> +};
>>> +
>>> +static int stm32_adc_buffer_postdisable(struct iio_dev *indio_dev)
>>> +{
>>> +    struct stm32_adc *adc = iio_priv(indio_dev);
>>> +
>>> +    stm32_adc_disable(adc);
>> This is a surprise as postdisbale should balance preenable...
>> Ah, you have update scan mode enabling the adc.  If you can balance it
>> better by moving that to preenable please do as it is more 'obviously' correct.
> I'll try to rework this.
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
>>> +    .postenable = &iio_triggered_buffer_postenable,
>>> +    .predisable = &iio_triggered_buffer_predisable,
>>> +    .postdisable = &stm32_adc_buffer_postdisable,
>>> +};
>>> +
>>> +static int stm32_adc_validate_device(struct iio_trigger *trig,
>>> +                     struct iio_dev *indio_dev)
>>> +{
>>> +    struct iio_dev *indio = iio_trigger_get_drvdata(trig);
>>> +
>>> +    return indio != indio_dev ? -EINVAL : 0;
>>> +}
>>> +
>>> +static int stm32_adc_set_trigger_state(struct iio_trigger *trig,
>>> +                       bool state)
>>> +{
>>> +    struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
>>> +    struct stm32_adc *adc = iio_priv(indio_dev);
>>> +    int ret;
>>> +
>>> +    if (state) {
>>> +        /* Reset adc buffer index */
>>> +        adc->bufi = 0;
>>> +
>>> +        /* Allocate adc buffer */
>>> +        adc->buffer = kzalloc(indio_dev->scan_bytes, GFP_KERNEL);
>> I'd be more cynical.  It's not that big a memory allocation at worst.
>> Just put a big enough buffer in your adc structure and don't bother doing
>> it dynamically.
>>
>> If you didn't want to do it, it should be in the preenable callback rather
>> than the trigger state one (for semantic reasons rather than because it's a
>> bug)
> I'll fix this.
>>> +        if (!adc->buffer)
>>> +            return -ENOMEM;
>>> +
>>> +        ret = stm32_adc_set_trig(indio_dev, trig);
>>> +        if (ret) {
>>> +            dev_err(&indio_dev->dev, "Can't set trigger\n");
>>> +            goto err_buffer_free;
>>> +        }
>>> +
>>> +        stm32_adc_conv_irq_enable(adc);
>>> +
>>> +        ret = stm32_adc_start_conv(adc);
>>> +        if (ret) {
>>> +            dev_err(&indio_dev->dev, "Failed to start\n");
>>> +            goto err_irq_trig_disable;
>>> +        }
>>> +    } else {
>>> +        ret = stm32_adc_stop_conv(adc);
>>> +        if (ret < 0) {
>>> +            dev_err(&indio_dev->dev, "Failed to stop\n");
>>> +            return ret;
>>> +        }
>>> +
>>> +        stm32_adc_conv_irq_disable(adc);
>>> +
>>> +        ret = stm32_adc_set_trig(indio_dev, NULL);
>>> +        if (ret)
>>> +            dev_warn(&indio_dev->dev, "Can't clear trigger\n");
>>> +
>>> +        kfree(adc->buffer);
>>> +        adc->buffer = NULL;
>>> +    }
>>> +
>>> +    return 0;
>>> +
>>> +err_irq_trig_disable:
>>> +    stm32_adc_conv_irq_disable(adc);
>>> +    stm32_adc_set_trig(indio_dev, NULL);
>>> +
>>> +err_buffer_free:
>>> +    kfree(adc->buffer);
>>> +    adc->buffer = NULL;
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static const struct iio_trigger_ops stm32_adc_trigger_ops = {
>>> +    .owner = THIS_MODULE,
>>> +    .validate_device = stm32_adc_validate_device,
>>> +    .set_trigger_state = stm32_adc_set_trigger_state,
>>> +};
>>> +
>>> +static irqreturn_t stm32_adc_trigger_handler(int irq, void *p)
>>> +{
>>> +    struct iio_poll_func *pf = p;
>>> +    struct iio_dev *indio_dev = pf->indio_dev;
>>> +    struct stm32_adc *adc = iio_priv(indio_dev);
>>> +
>>> +    dev_dbg(&indio_dev->dev, "%s bufi=%d\n", __func__, adc->bufi);
>>> +
>>> +    /* reset buffer index */
>>> +    adc->bufi = 0;
>>> +    iio_push_to_buffers_with_timestamp(indio_dev, adc->buffer,
>>> +                       pf->timestamp);
>>> +
>>> +    iio_trigger_notify_done(indio_dev->trig);
>>> +
>>> +    /* re-enable eoc irq */
>>> +    stm32_adc_conv_irq_enable(adc);
>>> +
>>> +    return IRQ_HANDLED;
>>> +}
>>> +
>>> +static void stm32_adc_trig_unregister(struct iio_dev *indio_dev)
>>> +{
>>> +    struct stm32_adc *adc = iio_priv(indio_dev);
>>> +    struct iio_trigger *trig, *_t;
>>> +
>>> +    list_for_each_entry_safe(trig, _t, &adc->extrig_list, alloc_list) {
>>> +        iio_trigger_unregister(trig);
>>> +        list_del(&trig->alloc_list);
>>> +    }
>>> +}
>>> +
>> I'd like a bit of documentation on this and a few of the other more
>> complex functions.  Here it wasn't immediately obvious to me that it
>> was registering a large set of triggers.  Also, silly question but
>> do you have any means of controlling the various timer setups from userspace?
> Sorry about this, I'll try to comment about trigger list to make it more obvious.
> There is no mean to setup timers via userspace, yet...
> I can remove them from the list for now, until this is supported.
> BTW I have some questions on trigger...
> 
>>
>> There have been numerous discussions over the years on having a generic
>> timer subsystem, but if anything got written it passed me by. I have a couple
>> of boards where it would be handy but never had the time to do more than
>> talk about it ;)
> 
> This is interesting... I'd be glad to hear more about it. Can you
> point some discussions if you have it in mind?
Err. Not sure I'll actually find an email thread on this.  I can find
a reference to earlier discussions on it in the original IIO submission
over 8 years ago...  So it was before we had an IIO list which means
it must have been on lkml.

As far as I know, no one ever took this futher though...
> 
> In this driver, validate_trigger routine enforces that only triggers
> allocated for current indio_dev can be used. What if all timer
> triggers are put in a separate driver ? (e.g. like hrtimer in
> drivers/iio/trigger/) ? Purpose would be to tune 'sampling_frequency'
> and so on, on similar model, and have it configured basically when
> using it (e.g. cat trigger/name>trigger/current_trigger.).
I think we'd be closer to having a timer subsystem offer some 
'services' to the drivers and then have your driver make use of those.
As the timer is hard wired to the actual adc timing here (rather than
a signal passing through kernel space like the high resolution timer
triggers are) I think the ADC driver will need to know about it
directly in some fashion.
> 
> Is it a viable option, not to declare timer triggers in stm32-adc.c,
> but use pre-defined list of triggers, and separate trigger driver ? 
> I'm thinking then, of simple string based list... But maybe you
> already though about this king of things ?
A simple string based identification might be prone to problems as
any driver could define it's own naming.

So I think the triggers need to be supplied by stm32-adc.c as the
driver needs to know about the 'hard wired' nature of which timers
can be used.  The underlying handling of timer configuring etc
might be provided by a separate 'provider' module - similar to we
do for clock sources or regulators for example.  Both of these
have the same characteristic of being separate 'hardware blocks'
that can be connected to lots of things, but are in this case
directly feeding the devices using them.

(we do this with IIO consumers too but that is a different game
and not relevant here + harder to follow than simple regs and
clocks).
> 
> Please kindly share you view on this.

It's a non trivial job, but if you ultimately want to be able
to use those periodic timers for multiple possible purposes then
you'll need to do a fair bit of the work towards a generic
subsystem for timers anyway (callbacks etc).

Whether it is worth supporting the more 'soft' connected
equivalents (blackfin timer which is in staging as an IIO trigger
or the pxa271 periodic timer driver I wrote years ago - which
cheated and created a pile of RTCs to expose the interface)
is unclear.  A lot of those usecases are well handled by
the High Resolution Timer trigger.

Jonathan

> 
> Thanks again for your review.
> Best Regards,
> Fabrice
> 
>>> +static int stm32_adc_trig_register(struct iio_dev *indio_dev)
>>> +{
>>> +    struct stm32_adc *adc = iio_priv(indio_dev);
>>> +    struct stm32_adc_common *common = adc->common;
>>> +    const struct stm32_adc_trig_info *ext = common->data->ext_triggers;
>>> +    struct iio_trigger *trig;
>>> +    int i, ret = 0;
>>> +
>>> +    if (adc->injected)
>>> +        ext = common->data->jext_triggers;
>>> +    else
>>> +        ext = common->data->ext_triggers;
>>> +
>>> +    for (i = 0; ext && ext[i].name; i++) {
>>> +        trig = devm_iio_trigger_alloc(common->dev, "%s_%s%d_%s",
>>> +                          indio_dev->name,
>>> +                          adc->injected ? "jext" : "ext",
>>> +                          ext[i].extsel, ext[i].name);
>>> +        if (!trig) {
>>> +            dev_err(common->dev, "trig %s_%s%d_%s alloc failed\n",
>>> +                indio_dev->name,
>>> +                adc->injected ? "jext" : "ext",
>>> +                ext[i].extsel, ext[i].name);
>>> +            ret = -ENOMEM;
>>> +            goto err;
>>> +        }
>>> +
>>> +        trig->dev.parent = common->dev;
>>> +        trig->ops = &stm32_adc_trigger_ops;
>>> +        iio_trigger_set_drvdata(trig, indio_dev);
>>> +
>>> +        ret = iio_trigger_register(trig);
>>> +        if (ret) {
>>> +            dev_err(common->dev,
>>> +                "trig %s_%s%d_%s register failed\n",
>>> +                indio_dev->name,
>>> +                adc->injected ? "jext" : "ext",
>>> +                ext[i].extsel, ext[i].name);
>>> +            goto err;
>>> +        }
>>> +
>>> +        list_add_tail(&trig->alloc_list, &adc->extrig_list);
>>> +    }
>>> +
>>> +    return 0;
>>> +err:
>>> +    stm32_adc_trig_unregister(indio_dev);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
>>> +                    struct iio_chan_spec *chan,
>>> +                    const struct stm32_adc_chan_spec *channel,
>>> +                    int scan_index)
>>> +{
>>> +    struct stm32_adc *adc = iio_priv(indio_dev);
>>> +
>>> +    chan->type = channel->type;
>>> +    chan->channel = channel->channel;
>>> +    chan->datasheet_name = channel->name;
>>> +    chan->extend_name = channel->name;
>>> +    chan->scan_index = scan_index;
>>> +    chan->indexed = 1;
>>> +    chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
>>> +    chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
>>> +    chan->scan_type.sign = 'u';
>>> +    chan->scan_type.realbits = adc->common->data->highres;
>>> +    chan->scan_type.storagebits = STM32_STORAGEBITS;
>> This is one of those cases where actually I'd argue just having the number
>> here and not under a define would be clearer!  So just put 16 here.
>>> +    chan->scan_type.shift = 0;
>> Should be unneeded.  Shift of 0 is the obvious default so no info provided
>> to readers of the code either really.
> I'll fix this
>>> +}
>>> +
>>> +static int stm32_adc_chan_of_init(struct iio_dev *indio_dev,
>>> +                  const struct stm32_adc_info *adc_info)
>>> +{
>>> +    struct stm32_adc *adc = iio_priv(indio_dev);
>>> +    struct device_node *node = indio_dev->dev.of_node;
>>> +    struct property *prop;
>>> +    const __be32 *cur;
>>> +    struct iio_chan_spec *channels;
>>> +    int scan_index = 0, num_channels = 0;
>>> +    u32 val;
>>> +
>>> +    of_property_for_each_u32(node, "st,adc-channels", prop, cur, val)
>>> +        num_channels++;
>>> +
>>> +    channels = devm_kcalloc(&indio_dev->dev, num_channels,
>>> +                sizeof(struct iio_chan_spec), GFP_KERNEL);
>>> +    if (!channels)
>>> +        return -ENOMEM;
>>> +
>>> +    of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
>>> +        stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
>>> +                    &adc_info->channels[val],
>>> +                    scan_index);
>>> +        scan_index++;
>>> +    }
>>> +
>>> +    adc->max_channels = adc_info->max_channels;
>>> +    indio_dev->num_channels = scan_index;
>>> +    indio_dev->channels = channels;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int stm32_adc_register(struct stm32_adc_common *common,
>>> +                  struct device_node *child)
>>> +{
>>> +    struct iio_dev *indio_dev;
>>> +    struct stm32_adc *adc;
>>> +    int i, ret;
>>> +    u32 reg;
>>> +
>>> +    ret = of_property_read_u32(child, "reg", &reg);
>>> +    if (ret != 0) {
>>> +        dev_err(common->dev, "missing reg property\n");
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    for (i = 0; common->data->adc_info[i].channels; i++)
>>> +        if (common->data->adc_info[i].reg == reg)
>>> +            break;
>>> +
>>> +    if (i >= STM32_ADC_ID_MAX || !common->data->adc_info[i].channels) {
>>> +        dev_err(common->dev, "bad adc reg offset\n");
>>> +        return -ENOENT;
>>> +    }
>>> +
>>> +    indio_dev = devm_iio_device_alloc(common->dev, sizeof(*adc));
>>> +    if (!indio_dev) {
>>> +        dev_err(common->dev, "iio device allocation failed\n");
>>> +        return -ENOMEM;
>>> +    }
>>> +
>>> +    adc = iio_priv(indio_dev);
>>> +    adc->id = i;
>>> +    adc->offset = reg;
>>> +    adc->common = common;
>>> +    INIT_LIST_HEAD(&adc->extrig_list);
>>> +    spin_lock_init(&adc->lock);
>>> +    init_completion(&adc->completion);
>>> +
>>> +    if (child->name)
>>> +        indio_dev->name = child->name;
>>> +    else
>>> +        indio_dev->name = common->data->adc_info[i].name;
>>> +    indio_dev->dev.parent = common->dev;
>>> +    indio_dev->dev.of_node = child;
>>> +    indio_dev->info = &stm32_adc_iio_info;
>>> +    indio_dev->modes = INDIO_DIRECT_MODE;
>>> +
>>> +    if (of_property_read_bool(child, "st,injected")) {
>>> +        dev_dbg(common->dev, "%s Configured to use injected\n",
>>> +            indio_dev->name);
>>> +        adc->injected = true;
>>> +    }
>>> +
>>> +    adc->clk = of_clk_get(child, 0);
>>> +    if (IS_ERR(adc->clk)) {
>>> +        adc->clk = NULL;
>>> +        dev_dbg(common->dev, "No child clk found\n");
>>> +    } else {
>>> +        ret = clk_prepare_enable(adc->clk);
>>> +        if (ret < 0)
>>> +            goto err_clk_put;
>>> +    }
>>> +
>>> +    ret = stm32_adc_chan_of_init(indio_dev, &common->data->adc_info[i]);
>>> +    if (ret < 0) {
>>> +        dev_err(common->dev, "iio channels init failed\n");
>>> +        goto err_clk_disable;
>>> +    }
>>> +
>>> +    ret = stm32_adc_trig_register(indio_dev);
>>> +    if (ret)
>>> +        goto err_clk_disable;
>>> +
>>> +    ret = iio_triggered_buffer_setup(indio_dev,
>>> +                     &iio_pollfunc_store_time,
>>> +                     &stm32_adc_trigger_handler,
>>> +                     &iio_triggered_buffer_setup_ops);
>>> +    if (ret) {
>>> +        dev_err(common->dev, "buffer setup failed\n");
>>> +        goto err_trig_unregister;
>>> +    }
>>> +
>>> +    ret = iio_device_register(indio_dev);
>>> +    if (ret) {
>>> +        dev_err(common->dev, "iio dev register failed\n");
>>> +        goto err_buffer_cleanup;
>>> +    }
>>> +
>>> +    list_add_tail(&adc->adc_list, &common->adc_list);
>>> +
>>> +    return 0;
>>> +
>>> +err_buffer_cleanup:
>>> +    iio_triggered_buffer_cleanup(indio_dev);
>>> +
>>> +err_trig_unregister:
>>> +    stm32_adc_trig_unregister(indio_dev);
>>> +
>>> +err_clk_disable:
>>> +    if (adc->clk)
>>> +        clk_disable_unprepare(adc->clk);
>>> +
>>> +err_clk_put:
>>> +    if (adc->clk)
>>> +        clk_put(adc->clk);
>>> +
>>> +    return ret;
>>> +}
>>> +
>>> +static void stm32_adc_unregister(struct stm32_adc *adc)
>>> +{
>>> +    struct iio_dev *indio_dev = iio_priv_to_dev(adc);
>>> +
>>> +    iio_device_unregister(indio_dev);
>>> +    iio_triggered_buffer_cleanup(indio_dev);
>>> +    stm32_adc_trig_unregister(indio_dev);
>>> +    if (adc->clk) {
>>> +        clk_disable_unprepare(adc->clk);
>>> +        clk_put(adc->clk);
>>> +    }
>>> +}
>>> +
>>> +int stm32_adc_probe(struct platform_device *pdev)
>>> +{
>>> +    struct device_node *np = pdev->dev.of_node, *child;
>>> +    struct device *dev = &pdev->dev;
>>> +    const struct of_device_id *match;
>>> +    struct stm32_adc_common *common;
>>> +    struct stm32_adc *adc;
>>> +    struct resource *res;
>>> +    int ret;
>>> +
>>> +    match = of_match_device(dev->driver->of_match_table, &pdev->dev);
>>> +    if (!match || !match->data) {
>>> +        dev_err(&pdev->dev, "compatible data not provided\n");
>> How would we have instantiated this if there was not a suitable match?
>> As such what does this check give us? (confused!)
> I'll fix this.
>>> +        return -EINVAL;
>>> +    }
>>> +
>>> +    common = devm_kzalloc(&pdev->dev, sizeof(*common), GFP_KERNEL);
>>> +    if (!common)
>>> +        return -ENOMEM;
>>> +
>>> +    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>>> +    common->base = devm_ioremap_resource(&pdev->dev, res);
>>> +    if (IS_ERR(common->base))
>>> +        return PTR_ERR(common->base);
>>> +
>>> +    common->data = match->data;
>>> +    common->dev = &pdev->dev;
>>> +    platform_set_drvdata(pdev, common);
>>> +    mutex_init(&common->lock);
>>> +    INIT_LIST_HEAD(&common->adc_list);
>>> +
>>> +    common->vref = devm_regulator_get(&pdev->dev, "vref");
>>> +    if (IS_ERR(common->vref)) {
>>> +        ret = PTR_ERR(common->vref);
>>> +        dev_err(&pdev->dev, "vref get failed, %d\n", ret);
>>> +        return ret;
>>> +    }
>>> +
>>> +    ret = regulator_enable(common->vref);
>>> +    if (ret < 0) {
>>> +        dev_err(&pdev->dev, "vref enable failed\n");
>>> +        return ret;
>>> +    }
>>> +
>>> +    ret = regulator_get_voltage(common->vref);
>>> +    if (ret < 0) {
>>> +        dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
>>> +        goto err_regulator_disable;
>>> +    }
>>> +    common->vref_mv = ret / 1000;
>>> +    dev_dbg(&pdev->dev, "vref+=%dmV\n", common->vref_mv);
>>> +
>>> +    common->aclk = devm_clk_get(&pdev->dev, "adc");
>>> +    if (IS_ERR(common->aclk)) {
>>> +        ret = PTR_ERR(common->aclk);
>>> +        dev_err(&pdev->dev, "Can't get 'adc' clock\n");
>>> +        goto err_regulator_disable;
>>> +    }
>>> +
>>> +    ret = clk_prepare_enable(common->aclk);
>>> +    if (ret < 0) {
>>> +        dev_err(common->dev, "adc clk enable failed\n");
>>> +        goto err_regulator_disable;
>>> +    }
>>> +
>>> +    common->irq = platform_get_irq(pdev, 0);
>>> +    if (common->irq < 0) {
>>> +        dev_err(&pdev->dev, "failed to get irq\n");
>>> +        ret = common->irq;
>>> +        goto err_clk_disable;
>>> +    }
>>> +
>>> +    ret = devm_request_irq(&pdev->dev, common->irq,    stm32_adc_common_isr,
>>> +                   0, pdev->name, common);
>>> +    if (ret) {
>>> +        dev_err(&pdev->dev, "failed to request irq\n");
>>> +        goto err_clk_disable;
>>> +    }
>>> +
>>> +    /* Parse adc child nodes to retrieve master/slave instances data */
>>> +    for_each_available_child_of_node(np, child) {
>>> +        ret = stm32_adc_register(common, child);
>>> +        if (ret)
>>> +            goto err_unregister;
>>> +    }
>>> +
>>> +    dev_info(&pdev->dev, "registered\n");
>> No benefit in this info being provided (it's obvious, device just turned up
>> in sysfs :) So drop it.
> I'll fix this.
>>> +
>>> +    return 0;
>>> +
>>> +err_unregister:
>>> +    list_for_each_entry(adc, &common->adc_list, adc_list)
>>> +        stm32_adc_unregister(adc);
>>> +
>>> +err_clk_disable:
>>> +    clk_disable_unprepare(common->aclk);
>>> +
>>> +err_regulator_disable:
>>> +    regulator_disable(common->vref);
>>> +
>>> +    return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(stm32_adc_probe);
>>> +
>>> +int stm32_adc_remove(struct platform_device *pdev)
>>> +{
>>> +    struct stm32_adc_common *common = platform_get_drvdata(pdev);
>>> +    struct stm32_adc *adc;
>>> +
>>> +    list_for_each_entry(adc, &common->adc_list, adc_list)
>>> +        stm32_adc_unregister(adc);
>>> +    clk_disable_unprepare(common->aclk);
>>> +    regulator_disable(common->vref);
>>> +
>>> +    return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(stm32_adc_remove);
>>> +
>>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>>> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC driver");
>>> +MODULE_LICENSE("GPL v2");
>>> diff --git a/drivers/iio/adc/stm32/stm32-adc.h b/drivers/iio/adc/stm32/stm32-adc.h
>>> new file mode 100644
>>> index 0000000..0be603c
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32/stm32-adc.h
>>> @@ -0,0 +1,442 @@
>>> +/*
>>> + * This file is part of STM32 ADC driver
>>> + *
>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>> + * Author: Fabrice Gasnier <fabrice.gasnier@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/>.
>>> + */
>>> +
>>> +#ifndef __STM32_ADC_H
>>> +#define __STM32_ADC_H
>>> +
>>> +/*
>>> + * STM32 - ADC global register map
>>> + * ________________________________________________________
>>> + * | Offset |                 Register                    |
>>> + * --------------------------------------------------------
>>> + * | 0x000  |                Master ADC1                  |
>>> + * --------------------------------------------------------
>>> + * | 0x100  |                Slave ADC2                   |
>>> + * --------------------------------------------------------
>>> + * | 0x200  |                Slave ADC3                   |
>>> + * --------------------------------------------------------
>>> + * | 0x300  |         Master & Slave common regs          |
>>> + * --------------------------------------------------------
>>> + */
>>> +#define STM32_ADCX_COMN_OFFSET        0x300
>>> +#define STM32_ADC_ID_MAX        3
>>> +#define STM32_ADC_MAX_SQ        16    /* SQ1..SQ16 */
>>> +#define STM32_ADC_MAX_JSQ        4    /* JSQ1..JSQ4 */
>>> +
>>> +/* STM32 value masks */
>>> +#define STM32_RESULT_MASK    GENMASK(15, 0)
>>> +#define STM32_STORAGEBITS    16
>>> +
>>> +/* External trigger enable for regular or injected channels (exten/jexten) */
>>> +enum stm32_adc_exten {
>>> +    STM32_EXTEN_SWTRIG,
>>> +    STM32_EXTEN_HWTRIG_RISING_EDGE,
>>> +    STM32_EXTEN_HWTRIG_FALLING_EDGE,
>>> +    STM32_EXTEN_HWTRIG_BOTH_EDGES,
>>> +};
>>> +
>>> +enum stm32_adc_extsel {
>>> +    STM32_EXT0,
>>> +    STM32_EXT1,
>>> +    STM32_EXT2,
>>> +    STM32_EXT3,
>>> +    STM32_EXT4,
>>> +    STM32_EXT5,
>>> +    STM32_EXT6,
>>> +    STM32_EXT7,
>>> +    STM32_EXT8,
>>> +    STM32_EXT9,
>>> +    STM32_EXT10,
>>> +    STM32_EXT11,
>>> +    STM32_EXT12,
>>> +    STM32_EXT13,
>>> +    STM32_EXT14,
>>> +    STM32_EXT15,
>>> +    STM32_EXT16,
>>> +    STM32_EXT17,
>>> +    STM32_EXT18,
>>> +    STM32_EXT19,
>>> +    STM32_EXT20,
>>> +    STM32_EXT21,
>>> +    STM32_EXT22,
>>> +    STM32_EXT23,
>>> +    STM32_EXT24,
>>> +    STM32_EXT25,
>>> +    STM32_EXT26,
>>> +    STM32_EXT27,
>>> +    STM32_EXT28,
>>> +    STM32_EXT29,
>>> +    STM32_EXT30,
>>> +    STM32_EXT31,
>>> +};
>>> +
>>> +enum stm32_adc_jextsel {
>>> +    STM32_JEXT0,
>>> +    STM32_JEXT1,
>>> +    STM32_JEXT2,
>>> +    STM32_JEXT3,
>>> +    STM32_JEXT4,
>>> +    STM32_JEXT5,
>>> +    STM32_JEXT6,
>>> +    STM32_JEXT7,
>>> +    STM32_JEXT8,
>>> +    STM32_JEXT9,
>>> +    STM32_JEXT10,
>>> +    STM32_JEXT11,
>>> +    STM32_JEXT12,
>>> +    STM32_JEXT13,
>>> +    STM32_JEXT14,
>>> +    STM32_JEXT15,
>>> +    STM32_JEXT16,
>>> +    STM32_JEXT17,
>>> +    STM32_JEXT18,
>>> +    STM32_JEXT19,
>>> +    STM32_JEXT20,
>>> +    STM32_JEXT21,
>>> +    STM32_JEXT22,
>>> +    STM32_JEXT23,
>>> +    STM32_JEXT24,
>>> +    STM32_JEXT25,
>>> +    STM32_JEXT26,
>>> +    STM32_JEXT27,
>>> +    STM32_JEXT28,
>>> +    STM32_JEXT29,
>>> +    STM32_JEXT30,
>>> +    STM32_JEXT31,
>>> +};
>>> +
>>> +#define    STM32_ADC_TIMEOUT_US    100000
>>> +#define    STM32_ADC_TIMEOUT    (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
>>> +
>>> +/**
>>> + * struct stm32_adc_chan_spec - specification of stm32 adc channel
>>> + * @type:    IIO channel type
>>> + * @channel:    channel number (single ended)
>>> + * @name:    channel name (single ended)
>>> + */
>>> +struct stm32_adc_chan_spec {
>>> +    enum iio_chan_type    type;
>>> +    int            channel;
>>> +    const char        *name;
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_adc_trig_info - ADC trigger info
>>> + * @extsel:        trigger selection for regular or injected
>>> + * @name:        name of the trigger, corresponding to its source
>>> + */
>>> +struct stm32_adc_trig_info {
>>> +    u32 extsel;
>>> +    const char *name;
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_adc_info - stm32 ADC, per instance config data
>>> + * @name:        default name for this instance (like "adc1")
>>> + * @reg:        reg offset for this instance (e.g. 0x0 for adc1...)
>>> + * @channels:        Reference to stm32 channels spec
>>> + * @max_channels:    Number of single ended channels
>>> + */
>>> +struct stm32_adc_info {
>>> +    const char *name;
>>> +    u32 reg;
>>> +    const struct stm32_adc_chan_spec *channels;
>>> +    int max_channels;
>>> +};
>>> +
>>> +/**
>>> + * stm32_adc_regs - stm32 ADC misc registers & bitfield desc
>>> + * @reg:        register offset
>>> + * @mask:        bitfield mask
>>> + * @shift:        left shift
>>> + */
>>> +struct stm32_adc_regs {
>>> +    int reg;
>>> +    int mask;
>>> +    int shift;
>>> +};
>>> +
>>> +/**
>>> + * stm32_adc_trig_reginfo - stm32 ADC trigger control registers description
>>> + * @reg:        trigger control register offset (exten/jexten)
>>> + * @exten_mask:        external trigger en/polarity mask in @reg
>>> + * @exten_shift:    external trigger en/polarity shift in @reg
>>> + * @extsel_mask:    external trigger source mask in @reg
>>> + * @extsel_shift:    external trigger source shift in @reg
>>> + */
>>> +struct stm32_adc_trig_reginfo {
>>> +    u32 reg;
>>> +    u32 exten_mask;
>>> +    u32 exten_shift;
>>> +    u32 extsel_mask;
>>> +    u32 extsel_shift;
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_adc_reginfo - stm32 ADC registers description
>>> + * @isr:        interrupt status register offset
>>> + * @eoc:        end of conversion mask in @isr
>>> + * @jeoc:        end of injected conversion sequence mask in @isr
>>> + * @ier:        interrupt enable register offset
>>> + * @eocie:        end of conversion interrupt enable mask in @ier
>>> + * @jeocie:        end of injected conversion sequence interrupt en mask
>>> + * @dr:            data register offset
>>> + * @jdr:        injected data registers offsets
>>> + * @sqr_regs:        Regular sequence registers description
>>> + * @jsqr_reg:        Injected sequence register description
>>> + * @trig_reginfo:    regular trigger control registers description
>>> + * @jtrig_reginfo:    injected trigger control registers description
>>> + */
>>> +struct stm32_adc_reginfo {
>>> +    u32 isr;
>>> +    u32 eoc;
>>> +    u32 jeoc;
>>> +    u32 ier;
>>> +    u32 eocie;
>>> +    u32 jeocie;
>>> +    u32 dr;
>>> +    u32 jdr[4];
>>> +    const struct stm32_adc_regs *sqr_regs;
>>> +    const struct stm32_adc_regs *jsqr_reg;
>>> +    const struct stm32_adc_trig_reginfo *trig_reginfo;
>>> +    const struct stm32_adc_trig_reginfo *jtrig_reginfo;
>>> +};
>>> +
>>> +struct stm32_adc;
>>> +
>>> +/**
>>> + * struct stm32_adc_ops - stm32 ADC, compatible dependent data
>>> + * - stm32 ADC may work as single ADC, or as tightly coupled master/slave ADCs.
>>> + *
>>> + * @adc_info:        Array spec for stm32 adc master/slaves instances
>>> + * @ext_triggers:    Reference to trigger info for regular channels
>>> + * @jext_triggers:    Reference to trigger info for injected channels
>>> + * @adc_reginfo:    stm32 ADC registers description
>>> + * @highres:        Max resolution
>>> + * @max_clock_rate:    Max input clock rate
>>> + * @clk_sel:        routine to select common clock and prescaler
>>> + * @start_conv:        routine to start conversions
>>> + * @stop_conv:        routine to stop conversions
>>> + * @is_started:        routine to get adc 'started' state
>>> + * @regular_started    routine to check regular conversions status
>>> + * @injected_started    routine to check injected conversions status
>>> + * @enable:        optional routine to enable stm32 adc
>>> + * @disable:        optional routine to disable stm32 adc
>>> + * @is_enabled        reports enabled state
>>> + */
>> This is a big chunk of abstraction that seems excessive at the moment.
>> I'd rather see it introduced only just before it's actually used..
>> (I'm guessing it's intended for support of similar parts?)
>>
>> Right now it just makes the driver harder to review.
>>> +struct stm32_adc_ops {
>>> +    const struct stm32_adc_info *adc_info;
>>> +    const struct stm32_adc_trig_info *ext_triggers;
>>> +    const struct stm32_adc_trig_info *jext_triggers;
>>> +    const struct stm32_adc_reginfo *adc_reginfo;
>>> +    int highres;
>>> +    unsigned long max_clock_rate;
>>> +    int (*clk_sel)(struct stm32_adc *adc);
>>> +    int (*start_conv)(struct stm32_adc *adc);
>>> +    int (*stop_conv)(struct stm32_adc *adc);
>>> +    bool (*is_started)(struct stm32_adc *adc);
>>> +    bool (*regular_started)(struct stm32_adc *adc);
>>> +    bool (*injected_started)(struct stm32_adc *adc);
>>> +    int (*enable)(struct stm32_adc *adc);
>>> +    void (*disable)(struct stm32_adc *adc);
>>> +    bool (*is_enabled)(struct stm32_adc *adc);
>>> +};
>>> +
>>> +struct stm32_adc_common;
>>> +
>>> +/**
>>> + * struct stm32_adc - private data of each ADC IIO instance
>>> + * @common:        reference to ADC block common data
>>> + * @adc_list:        current ADC entry in common ADC list
>>> + * @id:            ADC instance number (e.g. adc 1, 2 or 3)
>>> + * @offset:        ADC instance register offset in ADC block
>>> + * @max_channels:    Max channels number for this ADC.
>>> + * @extrig_list:    External trigger list (for regular channel)
>>> + * @completion:        end of single conversion completion
>>> + * @buffer:        data buffer
>>> + * @bufi:        data buffer index
>>> + * @num_conv:        expected number of scan conversions
>>> + * @injected:        use injected channels on this adc
>>> + * @lock:        spinlock
>>> + * @clk:        optional adc clock, for this adc instance
>>> + * @calib:        optional calibration data
>>> + * @en:            emulates enabled state on some stm32 adc
>>> + */
>>> +struct stm32_adc {
>>> +    struct stm32_adc_common    *common;
>>> +    struct list_head    adc_list;
>>> +    int            id;
>>> +    int            offset;
>>> +    int            max_channels;
>>> +    struct list_head    extrig_list;
>>> +    struct completion    completion;
>>> +    u16            *buffer;
>>> +    int            bufi;
>>> +    int            num_conv;
>>> +    bool            injected;
>>> +    spinlock_t        lock;        /* interrupt lock */
>>> +    struct clk        *clk;
>>> +    void            *calib;
>>> +    bool            en;
>>> +};
>>> +
>>> +/**
>>> + * struct stm32_adc_common - private data of ADC driver, common to all
>>> + * ADC instances (ADC block)
>>> + * @dev:        device for this controller
>>> + * @base:        control registers base cpu addr
>>> + * @irq:        Common irq line for all adc instances
>>> + * @data:        STM32 dependent data from compatible
>>> + * @adc_list:        list of all stm32 ADC in this ADC block
>>> + * @aclk:        common clock for the analog circuitry
>>> + * @vref:        regulator reference
>>> + * @vref_mv:        vref voltage (mv)
>>> + * @lock:        mutex
>>> + */
>>> +struct stm32_adc_common {
>>> +    struct device            *dev;
>>> +    void __iomem            *base;
>>> +    int                irq;
>>> +    const struct stm32_adc_ops    *data;
>>> +    struct list_head        adc_list;
>>> +    struct clk            *aclk;
>>> +    struct regulator        *vref;
>>> +    int                vref_mv;
>>> +    struct mutex            lock;    /* read_raw lock */
>>> +};
>>> +
>>> +/* Helper routines */
>>> +static inline int stm32_adc_start_conv(struct stm32_adc *adc)
>>> +{
>>> +    return adc->common->data->start_conv(adc);
>>> +}
>>> +
>>> +static inline int stm32_adc_stop_conv(struct stm32_adc *adc)
>>> +{
>>> +    return adc->common->data->stop_conv(adc);
>>> +}
>>> +
>>> +static inline bool stm32_adc_is_started(struct stm32_adc *adc)
>>> +{
>>> +    return adc->common->data->is_started(adc);
>>> +}
>>> +
>>> +static inline bool stm32_adc_regular_started(struct stm32_adc *adc)
>>> +{
>>> +    return adc->common->data->regular_started(adc);
>>> +}
>>> +
>>> +static inline bool stm32_adc_injected_started(struct stm32_adc *adc)
>>> +{
>>> +    return adc->common->data->injected_started(adc);
>>> +}
>>> +
>>> +static inline bool stm32_adc_clk_sel(struct stm32_adc *adc)
>>> +{
>>> +    return adc->common->data->clk_sel(adc);
>>> +}
>>> +
>>> +static inline int stm32_adc_enable(struct stm32_adc *adc)
>>> +{
>>> +    if (adc->common->data->enable)
>>> +        return adc->common->data->enable(adc);
>>> +
>>> +    adc->en = true;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static inline bool stm32_adc_is_enabled(struct stm32_adc *adc)
>>> +{
>>> +    if (adc->common->data->is_enabled)
>>> +        return adc->common->data->is_enabled(adc);
>>> +    else
>>> +        return adc->en;
>>> +}
>>> +
>>> +static inline void stm32_adc_disable(struct stm32_adc *adc)
>>> +{
>>> +    /* Check there is no regular or injected on-going conversions */
>>> +    if (stm32_adc_is_started(adc))
>>> +        return;
>>> +
>>> +    if (adc->common->data->disable)
>>> +        adc->common->data->disable(adc);
>>> +    else
>>> +        adc->en = false;
>>> +}
>>> +
>>> +/* STM32 ADC registers access routines */
>>> +static inline u32 stm32_adc_common_readl(struct stm32_adc_common *com, u32 reg)
>>> +{
>>> +    u32 val = readl_relaxed(com->base + reg);
>>> +
>>> +    return val;
>>> +}
>>> +
>>> +static inline void stm32_adc_common_writel(struct stm32_adc_common *com,
>>> +                       u32 reg, u32 val)
>>> +{
>>> +    writel_relaxed(val, com->base + reg);
>>> +}
>>> +
>>> +static inline u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
>>> +{
>>> +    u32 val = readl_relaxed(adc->common->base + adc->offset + reg);
>>> +
>>> +    return val;
>>> +}
>>> +
>>> +#define stm32_adc_readl_addr(addr)    stm32_adc_readl(adc, addr)
>>> +
>>> +#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \
>>> +    readx_poll_timeout(stm32_adc_readl_addr, reg, val, \
>>> +               cond, sleep_us, timeout_us)
>>> +
>>> +static inline void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
>>> +{
>>> +    writel_relaxed(val, adc->common->base + adc->offset + reg);
>>> +}
>>> +
>>> +static inline void stm32_adc_set_bits(struct stm32_adc *adc, u32 reg, u32 bits)
>>> +{
>>> +    unsigned long flags;
>>> +
>>> +    spin_lock_irqsave(&adc->lock, flags);
>>> +    stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) | bits);
>>> +    spin_unlock_irqrestore(&adc->lock, flags);
>>> +}
>>> +
>>> +static inline void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
>>> +{
>>> +    unsigned long flags;
>>> +
>>> +    spin_lock_irqsave(&adc->lock, flags);
>>> +    stm32_adc_writel(adc, reg, stm32_adc_readl(adc, reg) & ~bits);
>>> +    spin_unlock_irqrestore(&adc->lock, flags);
>>> +}
>>> +
>>> +/* STM32 common extended attributes */
>>> +extern const struct iio_enum stm32_adc_trig_pol;
>>> +int stm32_adc_probe(struct platform_device *pdev);
>>> +int stm32_adc_remove(struct platform_device *pdev);
>>> +
>>> +#endif
>>> diff --git a/drivers/iio/adc/stm32/stm32f4-adc.c b/drivers/iio/adc/stm32/stm32f4-adc.c
>>> new file mode 100644
>>> index 0000000..147fe9c
>>> --- /dev/null
>>> +++ b/drivers/iio/adc/stm32/stm32f4-adc.c
>>> @@ -0,0 +1,574 @@
>>> +/*
>>> + * This file is part of STM32F4 ADC driver
>>> + *
>>> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
>>> + * Author: Fabrice Gasnier <fabrice.gasnier@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/clk.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/iio/iio.h>
>>> +#include <linux/iio/trigger.h>
>>> +#include <linux/platform_device.h>
>>> +#include "stm32-adc.h"
>>> +
>>> +/*
>>> + * STM32F4 - ADC global register map
>>> + * ________________________________________________________
>>> + * | Offset |                 Register                    |
>>> + * --------------------------------------------------------
>>> + * | 0x000  |                Master ADC1                  |
>>> + * --------------------------------------------------------
>>> + * | 0x100  |                Slave ADC2                   |
>>> + * --------------------------------------------------------
>>> + * | 0x200  |                Slave ADC3                   |
>>> + * --------------------------------------------------------
>>> + * | 0x300  |         Master & Slave common regs          |
>>> + * --------------------------------------------------------
>>> + */
>>> +
>>> +/* STM32F4 - Registers for each ADC instance */
>>> +#define STM32F4_ADCX_SR            0x00
>>> +#define STM32F4_ADCX_CR1        0x04
>>> +#define STM32F4_ADCX_CR2        0x08
>>> +#define STM32F4_ADCX_SMPR1        0x0C
>>> +#define STM32F4_ADCX_SMPR2        0x10
>>> +#define STM32F4_ADCX_HTR        0x24
>>> +#define STM32F4_ADCX_LTR        0x28
>>> +#define STM32F4_ADCX_SQR1        0x2C
>>> +#define STM32F4_ADCX_SQR2        0x30
>>> +#define STM32F4_ADCX_SQR3        0x34
>>> +#define STM32F4_ADCX_JSQR        0x38
>>> +#define STM32F4_ADCX_JDR1        0x3C
>>> +#define STM32F4_ADCX_JDR2        0x40
>>> +#define STM32F4_ADCX_JDR3        0x44
>>> +#define STM32F4_ADCX_JDR4        0x48
>>> +#define STM32F4_ADCX_DR            0x4C
>>> +
>>> +/* STM32 - Master & slave registers (common for all instances: 1, 2 & 3) */
>>> +#define STM32F4_ADC_CSR            (STM32_ADCX_COMN_OFFSET + 0x00)
>>> +#define STM32F4_ADC_CCR            (STM32_ADCX_COMN_OFFSET + 0x04)
>>> +#define STM32F4_ADC_CDR            (STM32_ADCX_COMN_OFFSET + 0x08)
>>> +
>>> +/* STM32F4_ADCX_SR - bit fields */
>>> +#define STM32F4_OVR            BIT(5)
>>> +#define STM32F4_STRT            BIT(4)
>>> +#define STM32F4_JSTRT            BIT(3)
>>> +#define STM32F4_JEOC            BIT(2)
>>> +#define STM32F4_EOC            BIT(1)
>>> +#define STM32F4_AWD            BIT(0)
>>> +
>>> +/* STM32F4_ADCX_CR1 - bit fields */
>>> +#define STM32F4_OVRIE            BIT(26)
>>> +#define STM32F4_RES_SHIFT        24
>>> +#define STM32F4_RES_MASK        GENMASK(25, 24)
>>> +#define STM32F4_AWDEN            BIT(23)
>>> +#define STM32F4_JAWDEN            BIT(22)
>>> +#define STM32F4_DISCNUM_SHIFT        13
>>> +#define STM32F4_DISCNUM_MASK        GENMASK(15, 13)
>>> +#define STM32F4_JDISCEN            BIT(12)
>>> +#define STM32F4_DISCEN            BIT(11)
>>> +#define STM32F4_JAUTO            BIT(10)
>>> +#define STM32F4_AWDSGL            BIT(9)
>>> +#define STM32F4_SCAN            BIT(8)
>>> +#define STM32F4_JEOCIE            BIT(7)
>>> +#define STM32F4_AWDIE            BIT(6)
>>> +#define STM32F4_EOCIE            BIT(5)
>>> +#define STM32F4_AWDCH_SHIFT        0
>>> +#define STM32F4_AWDCH_MASK        GENMASK(4, 0)
>>> +
>>> +/* STM32F4_ADCX_CR2 - bit fields */
>>> +#define STM32F4_SWSTART            BIT(30)
>>> +#define STM32F4_EXTEN_SHIFT        28
>>> +#define STM32F4_EXTEN_MASK        GENMASK(29, 28)
>>> +#define STM32F4_EXTSEL_SHIFT        24
>>> +#define STM32F4_EXTSEL_MASK        GENMASK(27, 24)
>>> +#define STM32F4_JSWSTART        BIT(22)
>>> +#define STM32F4_JEXTEN_SHIFT        20
>>> +#define STM32F4_JEXTEN_MASK        GENMASK(21, 20)
>>> +#define STM32F4_JEXTSEL_SHIFT        16
>>> +#define STM32F4_JEXTSEL_MASK        GENMASK(19, 16)
>>> +#define STM32F4_ALIGN            BIT(11)
>>> +#define STM32F4_EOCS            BIT(10)
>>> +#define STM32F4_DDS            BIT(9)
>>> +#define STM32F4_DMA            BIT(8)
>>> +#define STM32F4_CONT            BIT(1)
>>> +#define STM32F4_ADON            BIT(0)
>>> +
>>> +/* STM32F4_ADCX_SMPR1 - bit fields */
>>> +#define STM32F4_SMP18_SHIFT        24
>>> +#define STM32F4_SMP18_MASK        GENMASK(26, 24)
>>> +#define STM32F4_SMP17_SHIFT        21
>>> +#define STM32F4_SMP17_MASK        GENMASK(23, 21)
>>> +#define STM32F4_SMP16_SHIFT        18
>>> +#define STM32F4_SMP16_MASK        GENMASK(20, 18)
>>> +#define STM32F4_SMP15_SHIFT        15
>>> +#define STM32F4_SMP15_MASK        GENMASK(17, 15)
>>> +#define STM32F4_SMP14_SHIFT        12
>>> +#define STM32F4_SMP14_MASK        GENMASK(14, 12)
>>> +#define STM32F4_SMP13_SHIFT        9
>>> +#define STM32F4_SMP13_MASK        GENMASK(11, 9)
>>> +#define STM32F4_SMP12_SHIFT        6
>>> +#define STM32F4_SMP12_MASK        GENMASK(8, 6)
>>> +#define STM32F4_SMP11_SHIFT        3
>>> +#define STM32F4_SMP11_MASK        GENMASK(5, 3)
>>> +#define STM32F4_SMP10_SHIFT        0
>>> +#define STM32F4_SMP10_MASK        GENMASK(2, 0)
>>> +
>>> +/* STM32F4_ADCX_SMPR2 - bit fields */
>>> +#define STM32F4_SMP9_SHIFT        27
>>> +#define STM32F4_SMP9_MASK        GENMASK(29, 27)
>>> +#define STM32F4_SMP8_SHIFT        24
>>> +#define STM32F4_SMP8_MASK        GENMASK(26, 24)
>>> +#define STM32F4_SMP7_SHIFT        21
>>> +#define STM32F4_SMP7_MASK        GENMASK(23, 21)
>>> +#define STM32F4_SMP6_SHIFT        18
>>> +#define STM32F4_SMP6_MASK        GENMASK(20, 18)
>>> +#define STM32F4_SMP5_SHIFT        15
>>> +#define STM32F4_SMP5_MASK        GENMASK(17, 15)
>>> +#define STM32F4_SMP4_SHIFT        12
>>> +#define STM32F4_SMP4_MASK        GENMASK(14, 12)
>>> +#define STM32F4_SMP3_SHIFT        9
>>> +#define STM32F4_SMP3_MASK        GENMASK(11, 9)
>>> +#define STM32F4_SMP2_SHIFT        6
>>> +#define STM32F4_SMP2_MASK        GENMASK(8, 6)
>>> +#define STM32F4_SMP1_SHIFT        3
>>> +#define STM32F4_SMP1_MASK        GENMASK(5, 3)
>>> +#define STM32F4_SMP0_SHIFT        0
>>> +#define STM32F4_SMP0_MASK        GENMASK(2, 0)
>>> +enum stm32f4_adc_smpr {
>>> +    STM32F4_SMPR_3_CK_CYCLES,
>>> +    STM32F4_SMPR_15_CK_CYCLES,
>>> +    STM32F4_SMPR_28_CK_CYCLES,
>>> +    STM32F4_SMPR_56_CK_CYCLES,
>>> +    STM32F4_SMPR_84_CK_CYCLES,
>>> +    STM32F4_SMPR_112_CK_CYCLES,
>>> +    STM32F4_SMPR_144_CK_CYCLES,
>>> +    STM32F4_SMPR_480_CK_CYCLES,
>>> +};
>>> +
>>> +/* STM32F4_ADCX_SQR1 - bit fields */
>>> +#define STM32F4_L_SHIFT            20
>>> +#define STM32F4_L_MASK            GENMASK(23, 20)
>>> +#define STM32F4_SQ16_SHIFT        15
>>> +#define STM32F4_SQ16_MASK        GENMASK(19, 15)
>>> +#define STM32F4_SQ15_SHIFT        10
>>> +#define STM32F4_SQ15_MASK        GENMASK(14, 10)
>>> +#define STM32F4_SQ14_SHIFT        5
>>> +#define STM32F4_SQ14_MASK        GENMASK(9, 5)
>>> +#define STM32F4_SQ13_SHIFT        0
>>> +#define STM32F4_SQ13_MASK        GENMASK(4, 0)
>>> +
>>> +/* STM32F4_ADCX_SQR2 - bit fields */
>>> +#define STM32F4_SQ12_SHIFT        25
>>> +#define STM32F4_SQ12_MASK        GENMASK(29, 25)
>>> +#define STM32F4_SQ11_SHIFT        20
>>> +#define STM32F4_SQ11_MASK        GENMASK(24, 20)
>>> +#define STM32F4_SQ10_SHIFT        15
>>> +#define STM32F4_SQ10_MASK        GENMASK(19, 15)
>>> +#define STM32F4_SQ9_SHIFT        10
>>> +#define STM32F4_SQ9_MASK        GENMASK(14, 10)
>>> +#define STM32F4_SQ8_SHIFT        5
>>> +#define STM32F4_SQ8_MASK        GENMASK(9, 5)
>>> +#define STM32F4_SQ7_SHIFT        0
>>> +#define STM32F4_SQ7_MASK        GENMASK(4, 0)
>>> +
>>> +/* STM32F4_ADCX_SQR3 - bit fields */
>>> +#define STM32F4_SQ6_SHIFT        25
>>> +#define STM32F4_SQ6_MASK        GENMASK(29, 25)
>>> +#define STM32F4_SQ5_SHIFT        20
>>> +#define STM32F4_SQ5_MASK        GENMASK(24, 20)
>>> +#define STM32F4_SQ4_SHIFT        15
>>> +#define STM32F4_SQ4_MASK        GENMASK(19, 15)
>>> +#define STM32F4_SQ3_SHIFT        10
>>> +#define STM32F4_SQ3_MASK        GENMASK(14, 10)
>>> +#define STM32F4_SQ2_SHIFT        5
>>> +#define STM32F4_SQ2_MASK        GENMASK(9, 5)
>>> +#define STM32F4_SQ1_SHIFT        0
>>> +#define STM32F4_SQ1_MASK        GENMASK(4, 0)
>>> +
>>> +/* STM32F4_ADCX_JSQR - bit fields */
>>> +#define STM32F4_JL_SHIFT        20
>>> +#define STM32F4_JL_MASK            GENMASK(21, 20)
>>> +#define STM32F4_JSQ4_SHIFT        15
>>> +#define STM32F4_JSQ4_MASK        GENMASK(19, 15)
>>> +#define STM32F4_JSQ3_SHIFT        10
>>> +#define STM32F4_JSQ3_MASK        GENMASK(14, 10)
>>> +#define STM32F4_JSQ2_SHIFT        5
>>> +#define STM32F4_JSQ2_MASK        GENMASK(9, 5)
>>> +#define STM32F4_JSQ1_SHIFT        0
>>> +#define STM32F4_JSQ1_MASK        GENMASK(4, 0)
>>> +
>>> +/* STM32F4_ADC_CCR - bit fields */
>>> +#define STM32F4_ADC_ADCPRE_SHIFT    16
>>> +#define STM32F4_ADC_ADCPRE_MASK        GENMASK(17, 16)
>>> +
>>> +/*
>>> + * stm32 ADC1, ADC2 & ADC3 are tightly coupled and may be used in multi mode
>>> + * Define here all inputs for all ADC instances
>>> + */
>>> +static const struct stm32_adc_chan_spec stm32f4_adc1_channels[] = {
>>> +    /* master ADC1 */
>>> +    { IIO_VOLTAGE, 0, "in0" },
>>> +    { IIO_VOLTAGE, 1, "in1" },
>>> +    { IIO_VOLTAGE, 2, "in2" },
>>> +    { IIO_VOLTAGE, 3, "in3" },
>>> +    { IIO_VOLTAGE, 4, "in4" },
>>> +    { IIO_VOLTAGE, 5, "in5" },
>>> +    { IIO_VOLTAGE, 6, "in6" },
>>> +    { IIO_VOLTAGE, 7, "in7" },
>>> +    { IIO_VOLTAGE, 8, "in8" },
>>> +    { IIO_VOLTAGE, 9, "in9" },
>>> +    { IIO_VOLTAGE, 10, "in10" },
>>> +    { IIO_VOLTAGE, 11, "in11" },
>>> +    { IIO_VOLTAGE, 12, "in12" },
>>> +    { IIO_VOLTAGE, 13, "in13" },
>>> +    { IIO_VOLTAGE, 14, "in14" },
>>> +    { IIO_VOLTAGE, 15, "in15" },
>>> +    /* internal analog sources available on input 16 to 18 */
>>> +    { IIO_VOLTAGE, 16, "in16" },
>>> +    { IIO_VOLTAGE, 17, "in17" },
>>> +    { IIO_VOLTAGE, 18, "in18" },
>>> +};
>>> +
>>> +static const struct stm32_adc_chan_spec stm32f4_adc23_channels[] = {
>>> +    /* slave ADC2 /    ADC3 */
>>> +    { IIO_VOLTAGE, 0, "in0" },
>>> +    { IIO_VOLTAGE, 1, "in1" },
>>> +    { IIO_VOLTAGE, 2, "in2" },
>>> +    { IIO_VOLTAGE, 3, "in3" },
>>> +    { IIO_VOLTAGE, 4, "in4" },
>>> +    { IIO_VOLTAGE, 5, "in5" },
>>> +    { IIO_VOLTAGE, 6, "in6" },
>>> +    { IIO_VOLTAGE, 7, "in7" },
>>> +    { IIO_VOLTAGE, 8, "in8" },
>>> +    { IIO_VOLTAGE, 9, "in9" },
>>> +    { IIO_VOLTAGE, 10, "in10" },
>>> +    { IIO_VOLTAGE, 11, "in11" },
>>> +    { IIO_VOLTAGE, 12, "in12" },
>>> +    { IIO_VOLTAGE, 13, "in13" },
>>> +    { IIO_VOLTAGE, 14, "in14" },
>>> +    { IIO_VOLTAGE, 15, "in15" },
>>> +};
>>> +
>>> +/* Triggers for regular channels */
>>> +static const struct stm32_adc_trig_info stm32f4_adc_ext_triggers[] = {
>>> +    { STM32_EXT0, "TIM1_CH1" },
>>> +    { STM32_EXT1, "TIM1_CH2" },
>>> +    { STM32_EXT2, "TIM1_CH3" },
>>> +    { STM32_EXT3, "TIM2_CH2" },
>>> +    { STM32_EXT4, "TIM2_CH3" },
>>> +    { STM32_EXT5, "TIM2_CH4" },
>>> +    { STM32_EXT6, "TIM2_TRGO" },
>>> +    { STM32_EXT7, "TIM3_CH1" },
>>> +    { STM32_EXT8, "TIM3_TRGO" },
>>> +    { STM32_EXT9, "TIM4_CH4" },
>>> +    { STM32_EXT10, "TIM5_CH1" },
>>> +    { STM32_EXT11, "TIM5_CH2" },
>>> +    { STM32_EXT12, "TIM5_CH3" },
>>> +    { STM32_EXT13, "TIM8_CH1" },
>>> +    { STM32_EXT14, "TIM8_TRGO" },
>>> +    { STM32_EXT15, "EXTI_11" },
>>> +    {},
>>> +};
>>> +
>>> +/* Triggers for injected channels */
>>> +static const struct stm32_adc_trig_info  stm32f4_adc_jext_triggers[] = {
>>> +    { STM32_JEXT0, "TIM1_CH4" },
>>> +    { STM32_JEXT1, "TIM1_TRGO" },
>>> +    { STM32_JEXT2, "TIM2_CH1" },
>>> +    { STM32_JEXT3, "TIM2_TRGO" },
>>> +    { STM32_JEXT4, "TIM3_CH2" },
>>> +    { STM32_JEXT5, "TIM3_CH4" },
>>> +    { STM32_JEXT6, "TIM4_CH1" },
>>> +    { STM32_JEXT7, "TIM4_CH2" },
>>> +    { STM32_JEXT8, "TIM4_CH3" },
>>> +    { STM32_JEXT9, "TIM4_TRGO" },
>>> +    { STM32_JEXT10, "TIM5_CH4" },
>>> +    { STM32_JEXT11, "TIM5_TRGO" },
>>> +    { STM32_JEXT12, "TIM8_CH2" },
>>> +    { STM32_JEXT13, "TIM8_CH3" },
>>> +    { STM32_JEXT14, "TIM8_CH4" },
>>> +    { STM32_JEXT15, "EXTI_15" },
>>> +    {},
>>> +};
>>> +
>>> +static const struct stm32_adc_info stm32f4_adc_info[] = {
>>> +    {
>>> +        .name = "adc1-master",
>>> +        .reg = 0x0,
>>> +        .channels = stm32f4_adc1_channels,
>>> +        .max_channels = ARRAY_SIZE(stm32f4_adc1_channels),
>>> +    },
>>> +    {
>>> +        .name = "adc2-slave",
>>> +        .reg = 0x100,
>>> +        .channels = stm32f4_adc23_channels,
>>> +        .max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
>>> +    },
>>> +    {
>>> +        .name = "adc3-slave",
>>> +        .reg = 0x200,
>>> +        .channels = stm32f4_adc23_channels,
>>> +        .max_channels = ARRAY_SIZE(stm32f4_adc23_channels),
>>> +    },
>>> +    {},
>>> +};
>>> +
>>> +/**
>>> + * stm32f4_sqr_regs - describe regular sequence registers
>>> + * - L: sequence len (register & bit field)
>>> + * - SQ1..SQ16: sequence entries (register & bit field)
>>> + */
>>> +static const struct stm32_adc_regs stm32f4_sqr_regs[STM32_ADC_MAX_SQ + 1] = {
>>> +    /* L: len bit field description to be kept as first element */
>>> +    { STM32F4_ADCX_SQR1, STM32F4_L_MASK, STM32F4_L_SHIFT },
>>> +    /* SQ1..SQ16 registers & bit fields */
>>> +    { STM32F4_ADCX_SQR3, STM32F4_SQ1_MASK, STM32F4_SQ1_SHIFT },
>>> +    { STM32F4_ADCX_SQR3, STM32F4_SQ2_MASK, STM32F4_SQ2_SHIFT },
>>> +    { STM32F4_ADCX_SQR3, STM32F4_SQ3_MASK, STM32F4_SQ3_SHIFT },
>>> +    { STM32F4_ADCX_SQR3, STM32F4_SQ4_MASK, STM32F4_SQ4_SHIFT },
>>> +    { STM32F4_ADCX_SQR3, STM32F4_SQ5_MASK, STM32F4_SQ5_SHIFT },
>>> +    { STM32F4_ADCX_SQR3, STM32F4_SQ6_MASK, STM32F4_SQ6_SHIFT },
>>> +    { STM32F4_ADCX_SQR2, STM32F4_SQ7_MASK, STM32F4_SQ7_SHIFT },
>>> +    { STM32F4_ADCX_SQR2, STM32F4_SQ8_MASK, STM32F4_SQ8_SHIFT },
>>> +    { STM32F4_ADCX_SQR2, STM32F4_SQ9_MASK, STM32F4_SQ9_SHIFT },
>>> +    { STM32F4_ADCX_SQR2, STM32F4_SQ10_MASK, STM32F4_SQ10_SHIFT },
>>> +    { STM32F4_ADCX_SQR2, STM32F4_SQ11_MASK, STM32F4_SQ11_SHIFT },
>>> +    { STM32F4_ADCX_SQR2, STM32F4_SQ12_MASK, STM32F4_SQ12_SHIFT },
>>> +    { STM32F4_ADCX_SQR1, STM32F4_SQ13_MASK, STM32F4_SQ13_SHIFT },
>>> +    { STM32F4_ADCX_SQR1, STM32F4_SQ14_MASK, STM32F4_SQ14_SHIFT },
>>> +    { STM32F4_ADCX_SQR1, STM32F4_SQ15_MASK, STM32F4_SQ15_SHIFT },
>>> +    { STM32F4_ADCX_SQR1, STM32F4_SQ16_MASK, STM32F4_SQ16_SHIFT },
>>> +};
>>> +
>>> +/**
>>> + * stm32f4_jsqr_reg - describe injected sequence register:
>>> + * - JL: injected sequence len
>>> + * - JSQ4..SQ1: sequence entries
>>> + * When JL == 3, ADC converts JSQ1, JSQ2, JSQ3, JSQ4
>>> + * When JL == 2, ADC converts JSQ2, JSQ3, JSQ4
>>> + * When JL == 1, ADC converts JSQ3, JSQ4
>>> + * When JL == 0, ADC converts JSQ4
>>> + */
>>> +static const struct stm32_adc_regs stm32f4_jsqr_reg[STM32_ADC_MAX_JSQ + 1] = {
>>> +    /* JL: len bit field description to be kept as first element */
>>> +    {STM32F4_ADCX_JSQR, STM32F4_JL_MASK, STM32F4_JL_SHIFT},
>>> +    /* JSQ4..JSQ1 registers & bit fields */
>>> +    {STM32F4_ADCX_JSQR, STM32F4_JSQ4_MASK, STM32F4_JSQ4_SHIFT},
>>> +    {STM32F4_ADCX_JSQR, STM32F4_JSQ3_MASK, STM32F4_JSQ3_SHIFT},
>>> +    {STM32F4_ADCX_JSQR, STM32F4_JSQ2_MASK, STM32F4_JSQ2_SHIFT},
>>> +    {STM32F4_ADCX_JSQR, STM32F4_JSQ1_MASK, STM32F4_JSQ1_SHIFT},
>>> +};
>>> +
>>> +static const struct stm32_adc_trig_reginfo stm32f4_adc_trig_reginfo = {
>>> +    .reg = STM32F4_ADCX_CR2,
>>> +    .exten_mask = STM32F4_EXTEN_MASK,
>>> +    .exten_shift = STM32F4_EXTEN_SHIFT,
>>> +    .extsel_mask = STM32F4_EXTSEL_MASK,
>>> +    .extsel_shift = STM32F4_EXTSEL_SHIFT,
>>> +};
>>> +
>>> +static const struct stm32_adc_trig_reginfo stm32f4_adc_jtrig_reginfo = {
>>> +    .reg = STM32F4_ADCX_CR2,
>>> +    .exten_mask = STM32F4_JEXTEN_MASK,
>>> +    .exten_shift = STM32F4_JEXTEN_SHIFT,
>>> +    .extsel_mask = STM32F4_JEXTSEL_MASK,
>>> +    .extsel_shift = STM32F4_JEXTSEL_SHIFT,
>>> +};
>>> +
>>> +static const struct stm32_adc_reginfo stm32f4_adc_reginfo = {
>>> +    .isr = STM32F4_ADCX_SR,
>>> +    .eoc = STM32F4_EOC,
>>> +    .jeoc = STM32F4_JEOC,
>>> +    .ier = STM32F4_ADCX_CR1,
>>> +    .eocie = STM32F4_EOCIE,
>>> +    .jeocie = STM32F4_JEOCIE,
>>> +    .dr = STM32F4_ADCX_DR,
>>> +    .jdr = {
>>> +        STM32F4_ADCX_JDR1,
>>> +        STM32F4_ADCX_JDR2,
>>> +        STM32F4_ADCX_JDR3,
>>> +        STM32F4_ADCX_JDR4,
>>> +    },
>>> +    .sqr_regs = stm32f4_sqr_regs,
>>> +    .jsqr_reg = stm32f4_jsqr_reg,
>>> +    .trig_reginfo = &stm32f4_adc_trig_reginfo,
>>> +    .jtrig_reginfo = &stm32f4_adc_jtrig_reginfo,
>>> +};
>>> +
>>> +static bool stm32f4_adc_is_started(struct stm32_adc *adc)
>>> +{
>>> +    u32 val = stm32_adc_readl(adc, STM32F4_ADCX_CR2) & STM32F4_ADON;
>>> +
>>> +    return !!val;
>>> +}
>>> +
>>> +static bool stm32f4_adc_regular_started(struct stm32_adc *adc)
>>> +{
>>> +    u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_STRT;
>>> +
>>> +    return !!val;
>>> +}
>>> +
>>> +static bool stm32f4_adc_injected_started(struct stm32_adc *adc)
>>> +{
>>> +    u32 val = stm32_adc_readl(adc, STM32F4_ADCX_SR) & STM32F4_JSTRT;
>>> +
>>> +    return !!val;
>>> +}
>>> +
>>> +/**
>>> + * stm32f4_adc_start_conv() - Start regular or injected conversions
>>> + * @adc: stm32 adc instance
>>> + *
>>> + * Start single conversions for regular or injected channels.
>>> + */
>>> +static int stm32f4_adc_start_conv(struct stm32_adc *adc)
>>> +{
>>> +    u32 trig_msk, start_msk;
>>> +
>>> +    dev_dbg(adc->common->dev, "%s %s\n", __func__,
>>> +        adc->injected ? "injected" : "regular");
>>> +
>>> +    stm32_adc_set_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
>>> +
>>> +    if (!stm32f4_adc_is_started(adc)) {
>>> +        stm32_adc_set_bits(adc, STM32F4_ADCX_CR2,
>>> +                   STM32F4_EOCS | STM32F4_ADON);
>>> +
>>> +        /* Wait for Power-up time (tSTAB from datasheet) */
>>> +        usleep_range(2, 3);
>>> +    }
>>> +
>>> +    if (adc->injected) {
>>> +        trig_msk = STM32F4_JEXTEN_MASK;
>>> +        start_msk = STM32F4_JSWSTART;
>>> +    } else {
>>> +        trig_msk = STM32F4_EXTEN_MASK;
>>> +        start_msk = STM32F4_SWSTART;
>>> +    }
>>> +
>>> +    /* Software start ? (e.g. trigger detection disabled ?) */
>>> +    if (!(stm32_adc_readl(adc, STM32F4_ADCX_CR2) & trig_msk))
>>> +        stm32_adc_set_bits(adc, STM32F4_ADCX_CR2, start_msk);
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static int stm32f4_adc_stop_conv(struct stm32_adc *adc)
>>> +{
>>> +    u32 val;
>>> +
>>> +    dev_dbg(adc->common->dev, "%s %s\n", __func__,
>>> +        adc->injected ? "injected" : "regular");
>>> +
>>> +    /* First disable trigger for either regular or injected channels */
>>> +    if (adc->injected) {
>>> +        stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_JEXTEN_MASK);
>>> +        stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_JSTRT);
>>> +    } else {
>>> +        stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_EXTEN_MASK);
>>> +        stm32_adc_clr_bits(adc, STM32F4_ADCX_SR, STM32F4_STRT);
>>> +    }
>>> +
>>> +    /* Disable adc when all triggered conversion have been disabled */
>>> +    val = stm32_adc_readl(adc, STM32F4_ADCX_CR2);
>>> +    val &= STM32F4_EXTEN_MASK | STM32F4_JEXTEN_MASK;
>>> +    if (!val) {
>>> +        stm32_adc_clr_bits(adc, STM32F4_ADCX_CR1, STM32F4_SCAN);
>>> +        stm32_adc_clr_bits(adc, STM32F4_ADCX_CR2, STM32F4_ADON);
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +/* ADC internal common clock prescaler division ratios */
>>> +static int stm32f4_pclk_div[] = {2, 4, 6, 8};
>>> +
>>> +/**
>>> + * stm32f4_adc_clk_sel() - Select ADC common clock prescaler
>>> + * @adc: stm32 adc instance
>>> + * Select clock prescaler used for analog conversions.
>>> + */
>>> +static int stm32f4_adc_clk_sel(struct stm32_adc *adc)
>>> +{
>>> +    struct stm32_adc_common *common = adc->common;
>>> +    unsigned long rate;
>>> +    u32 val;
>>> +    int i;
>>> +
>>> +    /* Common prescaler is set only once, when 1st ADC instance starts */
>>> +    list_for_each_entry(adc, &common->adc_list, adc_list)
>>> +        if (stm32f4_adc_is_started(adc))
>>> +            return 0;
>>> +
>>> +    rate = clk_get_rate(common->aclk);
>>> +    for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
>>> +        if ((rate / stm32f4_pclk_div[i]) <=
>>> +            common->data->max_clock_rate)
>>> +            break;
>>> +    }
>>> +    if (i >= ARRAY_SIZE(stm32f4_pclk_div))
>>> +        return -EINVAL;
>>> +
>>> +    val = stm32_adc_common_readl(common, STM32F4_ADC_CCR);
>>> +    val &= ~STM32F4_ADC_ADCPRE_MASK;
>>> +    val |= i << STM32F4_ADC_ADCPRE_SHIFT;
>>> +    stm32_adc_common_writel(common, STM32F4_ADC_CCR, val);
>>> +
>>> +    dev_dbg(common->dev, "Using analog clock source at %ld kHz\n",
>>> +        rate / (stm32f4_pclk_div[i] * 1000));
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static const struct stm32_adc_ops stm32f4_adc_ops = {
>>> +    .adc_info = stm32f4_adc_info,
>>> +    .ext_triggers = stm32f4_adc_ext_triggers,
>>> +    .jext_triggers = stm32f4_adc_jext_triggers,
>>> +    .adc_reginfo = &stm32f4_adc_reginfo,
>>> +    .highres = 12,
>>> +    .max_clock_rate = 36000000,
>>> +    .clk_sel = stm32f4_adc_clk_sel,
>>> +    .start_conv = stm32f4_adc_start_conv,
>>> +    .stop_conv = stm32f4_adc_stop_conv,
>>> +    .is_started = stm32f4_adc_is_started,
>>> +    .regular_started = stm32f4_adc_regular_started,
>>> +    .injected_started = stm32f4_adc_injected_started,
>>> +};
>>> +
>>> +static const struct of_device_id stm32f4_adc_of_match[] = {
>>> +    { .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_ops},
>>> +    {},
>>> +};
>>> +MODULE_DEVICE_TABLE(of, stm32f4_adc_of_match);
>>> +
>>> +static struct platform_driver stm32f4_adc_driver = {
>>> +    .probe = stm32_adc_probe,
>>> +    .remove = stm32_adc_remove,
>>> +    .driver = {
>>> +        .name = "stm32f4-adc",
>>> +        .of_match_table = stm32f4_adc_of_match,
>>> +    },
>>> +};
>>> +
>>> +module_platform_driver(stm32f4_adc_driver);
>>> +
>>> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
>>> +MODULE_DESCRIPTION("STMicroelectronics STM32F4 ADC driver");
>>> +MODULE_LICENSE("GPL v2");
>>>
> 
> -- 
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 20+ messages in thread

end of thread, other threads:[~2016-11-05 15:44 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-10-25 16:25 [PATCH 00/10] Add support for STM32 ADC Fabrice Gasnier
2016-10-25 16:25 ` [PATCH 01/10] Documentation: dt-bindings: Document STM32 ADC DT bindings Fabrice Gasnier
2016-10-31  3:02   ` Rob Herring
2016-11-03 11:11     ` Fabrice Gasnier
2016-10-25 16:25 ` [PATCH 02/10] iio: adc: Add stm32 support Fabrice Gasnier
2016-10-30 15:27   ` Jonathan Cameron
2016-11-03  8:20     ` Fabrice Gasnier
2016-11-05 15:44       ` Jonathan Cameron
2016-10-25 16:25 ` [PATCH 03/10] iio: adc: stm32: add optional dma support Fabrice Gasnier
2016-10-30 15:34   ` Jonathan Cameron
2016-10-25 16:25 ` [PATCH 04/10] iio: adc: stm32: add optional support for exti gpios Fabrice Gasnier
2016-10-30 14:35   ` Jonathan Cameron
2016-10-25 16:25 ` [PATCH 05/10] iio: adc: stm32: add trigger polarity ext attr Fabrice Gasnier
2016-10-30 14:33   ` Jonathan Cameron
2016-10-25 16:25 ` [PATCH 06/10] iio: adc: stm32: add ext attrs to configure sampling time Fabrice Gasnier
2016-10-30 14:21   ` Jonathan Cameron
2016-10-25 16:25 ` [PATCH 07/10] ARM: configs: stm32: enable IIO ADC driver Fabrice Gasnier
2016-10-25 16:25 ` [PATCH 08/10] ARM: dts: stm32f429: Add adc support Fabrice Gasnier
2016-10-25 16:25 ` [PATCH 09/10] ARM: dts: stm32f429: enable adc on eval board Fabrice Gasnier
2016-10-25 16:25 ` [PATCH 10/10] ARM: dts: stm32f429: Add dma support to adc Fabrice Gasnier

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).