lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <1477412722-24061-3-git-send-email-fabrice.gasnier@st.com>
Date:   Tue, 25 Oct 2016 18:25:14 +0200
From:   Fabrice Gasnier <fabrice.gasnier@...com>
To:     <linux-iio@...r.kernel.org>,
        <linux-arm-kernel@...ts.infradead.org>,
        <devicetree@...r.kernel.org>, <linux-kernel@...r.kernel.org>
CC:     <jic23@...nel.org>, <linux@...linux.org.uk>, <robh+dt@...nel.org>,
        <mark.rutland@....com>, <mcoquelin.stm32@...il.com>,
        <alexandre.torgue@...com>, <lars@...afoo.de>, <knaack.h@....de>,
        <pmeerw@...erw.net>, <fabrice.gasnier@...com>
Subject: [PATCH 02/10] iio: adc: Add stm32 support

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

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@...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@...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@...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@...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@...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@...com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32F4 ADC driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ