[PATCH 02/10] iio: adc: Add stm32 support
Fabrice Gasnier
fabrice.gasnier at st.com
Tue Oct 25 09:25:14 PDT 2016
This patch adds support for STMicroelectronics STM32 MCU's analog to
digital converter.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier at 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 at st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/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", ®);
+ 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 at 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 at st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#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 at st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/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 at st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32F4 ADC driver");
+MODULE_LICENSE("GPL v2");
--
1.9.1
More information about the linux-arm-kernel
mailing list