Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1522843 > unrolled thread
| Started by | Fabrice Gasnier <fabrice.gasnier@st.com> |
|---|---|
| First post | 2016-11-15 16:40 +0100 |
| Last post | 2016-11-19 13:30 +0100 |
| Articles | 7 — 2 participants |
Back to article view | Back to linux.kernel
[PATCH v3 0/6] Add support for STM32 ADC Fabrice Gasnier <fabrice.gasnier@st.com> - 2016-11-15 16:40 +0100
[PATCH v3 6/6] ARM: dts: stm32f429: enable adc on eval board Fabrice Gasnier <fabrice.gasnier@st.com> - 2016-11-15 16:40 +0100
[PATCH v3 5/6] ARM: dts: stm32f429: Add adc support Fabrice Gasnier <fabrice.gasnier@st.com> - 2016-11-15 16:40 +0100
[PATCH v3 3/6] iio: adc: Add support for STM32 ADC Fabrice Gasnier <fabrice.gasnier@st.com> - 2016-11-15 16:40 +0100
Re: [PATCH v3 3/6] iio: adc: Add support for STM32 ADC Jonathan Cameron <jic23@kernel.org> - 2016-11-19 13:20 +0100
[PATCH v3 4/6] ARM: configs: stm32: enable ADC driver Fabrice Gasnier <fabrice.gasnier@st.com> - 2016-11-15 16:40 +0100
Re: [PATCH v3 4/6] ARM: configs: stm32: enable ADC driver Jonathan Cameron <jic23@kernel.org> - 2016-11-19 13:30 +0100
| From | Fabrice Gasnier <fabrice.gasnier@st.com> |
|---|---|
| Date | 2016-11-15 16:40 +0100 |
| Subject | [PATCH v3 0/6] Add support for STM32 ADC |
| Message-ID | <sDO01-6JS-5@gated-at.bofh.it> |
This series adds support for STM32F4 ADC into IIO framework. STM32F4 ADC is a 12-bit successive approximation analog-to-digital converter with multiplexed input channels. Conversions can be performed in single, continuous, scan or discontinuous mode. Conversions can be launched in software or using hardware triggers. This driver has been developed and tested on STM32F429 eval board. It consist of a core driver, to manage common resources shared between up to 3 ADC instances and an ADC driver to manage each adc instance. Changes in v3: - Core driver moved to iio/adc. - Build fix. - Updates following Jonathan's and Lars's remarks. - Binding: adc child clock is mandatory. Changes in v2: - Replace single driver model by MFD approach, to handle up to 3 ADCs as separate devices. Each ADC device then registers a unique IIO device. - Make driver as simple as possible for the first instance, to ease review. For now, I dropped complexity by removing injected support, triggered buffer mode, dmas. - Removed abstraction layer (indirection routines, ops) as only stm32f4 is supported. Fabrice Gasnier (6): Documentation: dt-bindings: Document STM32 ADC DT bindings iio: adc: Add support for STM32 ADC core iio: adc: Add support for STM32 ADC ARM: configs: stm32: enable ADC driver ARM: dts: stm32f429: Add adc support ARM: dts: stm32f429: enable adc on eval board .../devicetree/bindings/iio/adc/st,stm32-adc.txt | 83 ++++ arch/arm/boot/dts/stm32429i-eval.dts | 25 + arch/arm/boot/dts/stm32f429.dtsi | 49 ++ arch/arm/configs/stm32_defconfig | 3 + drivers/iio/adc/Kconfig | 23 + drivers/iio/adc/Makefile | 2 + drivers/iio/adc/stm32-adc-core.c | 303 ++++++++++++ drivers/iio/adc/stm32-adc-core.h | 52 +++ drivers/iio/adc/stm32-adc.c | 518 +++++++++++++++++++++ 9 files changed, 1058 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt create mode 100644 drivers/iio/adc/stm32-adc-core.c create mode 100644 drivers/iio/adc/stm32-adc-core.h create mode 100644 drivers/iio/adc/stm32-adc.c -- 1.9.1
[toc] | [next] | [standalone]
| From | Fabrice Gasnier <fabrice.gasnier@st.com> |
|---|---|
| Date | 2016-11-15 16:40 +0100 |
| Subject | [PATCH v3 6/6] ARM: dts: stm32f429: enable adc on eval board |
| Message-ID | <sDO02-6JS-33@gated-at.bofh.it> |
| In reply to | #1522843 |
Enable analog to digital converter on stm32f429i-eval board.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/boot/dts/stm32429i-eval.dts | 25 +++++++++++++++++++++++++
1 file changed, 25 insertions(+)
diff --git a/arch/arm/boot/dts/stm32429i-eval.dts b/arch/arm/boot/dts/stm32429i-eval.dts
index 6bfc595..c144735 100644
--- a/arch/arm/boot/dts/stm32429i-eval.dts
+++ b/arch/arm/boot/dts/stm32429i-eval.dts
@@ -65,6 +65,20 @@
serial0 = &usart1;
};
+ regulators {
+ compatible = "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ reg_vref: regulator@0 {
+ compatible = "regulator-fixed";
+ reg = <0>;
+ regulator-name = "vref";
+ regulator-min-microvolt = <3300000>;
+ regulator-max-microvolt = <3300000>;
+ };
+ };
+
leds {
compatible = "gpio-leds";
green {
@@ -123,3 +137,14 @@
pinctrl-names = "default";
status = "okay";
};
+
+&adc {
+ pinctrl-names = "default";
+ pinctrl-0 = <&adc3_in8_pin>;
+ vref-supply = <®_vref>;
+ status = "okay";
+ adc3: adc@200 {
+ st,adc-channels = <8>;
+ status = "okay";
+ };
+};
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Fabrice Gasnier <fabrice.gasnier@st.com> |
|---|---|
| Date | 2016-11-15 16:40 +0100 |
| Subject | [PATCH v3 5/6] ARM: dts: stm32f429: Add adc support |
| Message-ID | <sDO03-6JS-59@gated-at.bofh.it> |
| In reply to | #1522843 |
Add adc support & pinctrl analog phandle (adc3_in8) to stm32f429.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
arch/arm/boot/dts/stm32f429.dtsi | 49 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 49 insertions(+)
diff --git a/arch/arm/boot/dts/stm32f429.dtsi b/arch/arm/boot/dts/stm32f429.dtsi
index 336ee4f..f198132 100644
--- a/arch/arm/boot/dts/stm32f429.dtsi
+++ b/arch/arm/boot/dts/stm32f429.dtsi
@@ -172,6 +172,49 @@
status = "disabled";
};
+ adc: adc@40012000 {
+ compatible = "st,stm32f4-adc-core";
+ reg = <0x40012000 0x400>;
+ interrupts = <18>;
+ clocks = <&rcc 0 168>;
+ clock-names = "adc";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+
+ adc1: adc@0 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x0>;
+ clocks = <&rcc 0 168>;
+ interrupt-parent = <&adc>;
+ interrupts = <0>;
+ status = "disabled";
+ };
+
+ adc2: adc@100 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x100>;
+ clocks = <&rcc 0 169>;
+ interrupt-parent = <&adc>;
+ interrupts = <1>;
+ status = "disabled";
+ };
+
+ adc3: adc@200 {
+ compatible = "st,stm32f4-adc";
+ #io-channel-cells = <1>;
+ reg = <0x200>;
+ clocks = <&rcc 0 170>;
+ interrupt-parent = <&adc>;
+ interrupts = <2>;
+ status = "disabled";
+ };
+ };
+
syscfg: system-config@40013800 {
compatible = "syscon";
reg = <0x40013800 0x400>;
@@ -332,6 +375,12 @@
slew-rate = <2>;
};
};
+
+ adc3_in8_pin: adc@200 {
+ pins {
+ pinmux = <STM32F429_PF10_FUNC_ANALOG>;
+ };
+ };
};
rcc: rcc@40023810 {
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Fabrice Gasnier <fabrice.gasnier@st.com> |
|---|---|
| Date | 2016-11-15 16:40 +0100 |
| Subject | [PATCH v3 3/6] iio: adc: Add support for STM32 ADC |
| Message-ID | <sDO03-6JS-57@gated-at.bofh.it> |
| In reply to | #1522843 |
This patch adds support for STMicroelectronics STM32 MCU's analog to
digital converter.
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
drivers/iio/adc/Kconfig | 10 +
drivers/iio/adc/Makefile | 1 +
drivers/iio/adc/stm32-adc.c | 518 ++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 529 insertions(+)
create mode 100644 drivers/iio/adc/stm32-adc.c
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index ff30239..f93b990 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -432,6 +432,16 @@ config STM32_ADC_CORE
This driver can also be built as a module. If so, the module
will be called stm32-adc-core.
+config STM32_ADC
+ tristate "STMicroelectronics STM32 adc"
+ depends on STM32_ADC_CORE
+ help
+ Say yes here to build support for 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 STX104
tristate "Apex Embedded Systems STX104 driver"
depends on X86 && ISA_BUS_API
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index a1e8f44..8e02a94 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -42,6 +42,7 @@ obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
obj-$(CONFIG_STX104) += stx104.o
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
+obj-$(CONFIG_STM32_ADC) += stm32-adc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
new file mode 100644
index 0000000..5715e79
--- /dev/null
+++ b/drivers/iio/adc/stm32-adc.c
@@ -0,0 +1,518 @@
+/*
+ * This file is part of STM32 ADC driver
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * License type: GPLv2
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE.
+ * See the GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+
+#include "stm32-adc-core.h"
+
+/* STM32F4 - Registers for each ADC instance */
+#define STM32F4_ADC_SR 0x00
+#define STM32F4_ADC_CR1 0x04
+#define STM32F4_ADC_CR2 0x08
+#define STM32F4_ADC_SMPR1 0x0C
+#define STM32F4_ADC_SMPR2 0x10
+#define STM32F4_ADC_HTR 0x24
+#define STM32F4_ADC_LTR 0x28
+#define STM32F4_ADC_SQR1 0x2C
+#define STM32F4_ADC_SQR2 0x30
+#define STM32F4_ADC_SQR3 0x34
+#define STM32F4_ADC_JSQR 0x38
+#define STM32F4_ADC_JDR1 0x3C
+#define STM32F4_ADC_JDR2 0x40
+#define STM32F4_ADC_JDR3 0x44
+#define STM32F4_ADC_JDR4 0x48
+#define STM32F4_ADC_DR 0x4C
+
+/* STM32F4_ADC_SR - bit fields */
+#define STM32F4_STRT BIT(4)
+#define STM32F4_EOC BIT(1)
+
+/* STM32F4_ADC_CR1 - bit fields */
+#define STM32F4_SCAN BIT(8)
+#define STM32F4_EOCIE BIT(5)
+
+/* STM32F4_ADC_CR2 - bit fields */
+#define STM32F4_SWSTART BIT(30)
+#define STM32F4_EXTEN_MASK GENMASK(29, 28)
+#define STM32F4_EOCS BIT(10)
+#define STM32F4_ADON BIT(0)
+
+/* STM32F4_ADC_SQR1 - bit fields */
+#define STM32F4_L_SHIFT 20
+#define STM32F4_L_MASK GENMASK(23, 20)
+
+/* STM32F4_ADC_SQR3 - bit fields */
+#define STM32F4_SQ1_SHIFT 0
+#define STM32F4_SQ1_MASK GENMASK(4, 0)
+
+#define STM32_ADC_TIMEOUT_US 100000
+#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
+
+/**
+ * struct stm32_adc - private data of each ADC IIO instance
+ * @common: reference to ADC block common data
+ * @offset: ADC instance register offset in ADC block
+ * @completion: end of single conversion completion
+ * @buffer: data buffer
+ * @clk: clock for this adc instance
+ * @irq: interrupt for this adc instance
+ * @lock: spinlock
+ */
+struct stm32_adc {
+ struct stm32_adc_common *common;
+ u32 offset;
+ struct completion completion;
+ u16 *buffer;
+ struct clk *clk;
+ int irq;
+ spinlock_t lock; /* interrupt lock */
+};
+
+/**
+ * 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;
+};
+
+/* Input definitions common for all STM32F4 instances */
+static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
+ { 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" },
+};
+
+/**
+ * STM32 ADC registers access routines
+ * @adc: stm32 adc instance
+ * @reg: reg offset in adc instance
+ *
+ * Note: All instances share same base, with 0x0, 0x100 or 0x200 offset resp.
+ * for adc1, adc2 and adc3.
+ */
+static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
+{
+ return readl_relaxed(adc->common->base + adc->offset + reg);
+}
+
+static u16 stm32_adc_readw(struct stm32_adc *adc, u32 reg)
+{
+ return readw_relaxed(adc->common->base + adc->offset + reg);
+}
+
+static void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
+{
+ writel_relaxed(val, adc->common->base + adc->offset + reg);
+}
+
+static 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 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_adc_conv_irq_enable() - Enable end of conversion interrupt
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
+{
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
+};
+
+/**
+ * stm32_adc_conv_irq_disable() - Disable end of conversion interrupt
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
+{
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
+}
+
+/**
+ * stm32_adc_start_conv() - Start conversions for regular channels.
+ * @adc: stm32 adc instance
+ */
+static void stm32_adc_start_conv(struct stm32_adc *adc)
+{
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON);
+
+ /* Wait for Power-up time (tSTAB from datasheet) */
+ usleep_range(2, 3);
+
+ /* Software start ? (e.g. trigger detection disabled ?) */
+ if (!(stm32_adc_readl(adc, STM32F4_ADC_CR2) & STM32F4_EXTEN_MASK))
+ stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_SWSTART);
+}
+
+static void stm32_adc_stop_conv(struct stm32_adc *adc)
+{
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
+ stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT);
+
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_ADON);
+}
+
+/**
+ * stm32_adc_single_conv() - Performs a single conversion
+ * @indio_dev: IIO device
+ * @chan: IIO channel
+ * @res: conversion result
+ *
+ * The function performs a single conversion on a given channel:
+ * - Program sequencer with one channel (e.g. in SQ1 with len = 1)
+ * - Use SW trigger
+ * - Start conversion, then wait for interrupt completion.
+ */
+static int stm32_adc_single_conv(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *res)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ long timeout;
+ u32 val;
+ u16 result;
+ int ret;
+
+ reinit_completion(&adc->completion);
+
+ adc->buffer = &result;
+
+ /* Program chan number in regular sequence */
+ val = stm32_adc_readl(adc, STM32F4_ADC_SQR3);
+ val &= ~STM32F4_SQ1_MASK;
+ val |= chan->channel << STM32F4_SQ1_SHIFT;
+ stm32_adc_writel(adc, STM32F4_ADC_SQR3, val);
+
+ /* Set regular sequence len (0 for 1 conversion) */
+ stm32_adc_clr_bits(adc, STM32F4_ADC_SQR1, STM32F4_L_MASK);
+
+ /* Trigger detection disabled (conversion can be launched in SW) */
+ stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
+
+ stm32_adc_conv_irq_enable(adc);
+
+ stm32_adc_start_conv(adc);
+
+ timeout = wait_for_completion_interruptible_timeout(
+ &adc->completion, STM32_ADC_TIMEOUT);
+ if (timeout == 0) {
+ ret = -ETIMEDOUT;
+ } else if (timeout < 0) {
+ ret = timeout;
+ } else {
+ *res = result;
+ ret = IIO_VAL_INT;
+ }
+
+ stm32_adc_stop_conv(adc);
+
+ stm32_adc_conv_irq_disable(adc);
+
+ 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;
+
+ 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);
+ else
+ ret = -EINVAL;
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = adc->common->vref_mv;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static irqreturn_t stm32_adc_isr(int irq, void *data)
+{
+ struct stm32_adc *adc = data;
+ u32 status = stm32_adc_readl(adc, STM32F4_ADC_SR);
+
+ if (status & STM32F4_EOC) {
+ *adc->buffer = stm32_adc_readw(adc, STM32F4_ADC_DR);
+ complete(&adc->completion);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+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,
+ .debugfs_reg_access = stm32_adc_debugfs_reg_access,
+ .of_xlate = stm32_adc_of_xlate,
+ .driver_module = THIS_MODULE,
+};
+
+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)
+{
+ chan->type = channel->type;
+ chan->channel = channel->channel;
+ chan->datasheet_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 = 12;
+ chan->scan_type.storagebits = 16;
+}
+
+static int stm32_adc_chan_of_init(struct iio_dev *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;
+ u32 val;
+
+ num_channels = of_property_count_u32_elems(node, "st,adc-channels");
+ if (num_channels < 0 ||
+ num_channels >= ARRAY_SIZE(stm32f4_adc123_channels)) {
+ dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
+ return num_channels < 0 ? num_channels : -EINVAL;
+ }
+
+ 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) {
+ if (val >= ARRAY_SIZE(stm32f4_adc123_channels)) {
+ dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
+ return -EINVAL;
+ }
+ stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+ &stm32f4_adc123_channels[val],
+ scan_index);
+ scan_index++;
+ }
+
+ indio_dev->num_channels = scan_index;
+ indio_dev->channels = channels;
+
+ return 0;
+}
+
+static int stm32_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct stm32_adc *adc;
+ int ret;
+
+ if (!pdev->dev.of_node)
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->common = dev_get_drvdata(pdev->dev.parent);
+ spin_lock_init(&adc->lock);
+ init_completion(&adc->completion);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->dev.of_node = pdev->dev.of_node;
+ indio_dev->info = &stm32_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ platform_set_drvdata(pdev, adc);
+
+ ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset);
+ if (ret != 0) {
+ dev_err(&pdev->dev, "missing reg property\n");
+ return -EINVAL;
+ }
+
+ adc->irq = platform_get_irq(pdev, 0);
+ if (adc->irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ return adc->irq;
+ }
+
+ ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
+ 0, pdev->name, adc);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ return ret;
+ }
+
+ adc->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(adc->clk)) {
+ dev_err(&pdev->dev, "Can't get clock\n");
+ return PTR_ERR(adc->clk);
+ }
+
+ ret = clk_prepare_enable(adc->clk);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "clk enable failed\n");
+ return ret;
+ }
+
+ ret = stm32_adc_chan_of_init(indio_dev);
+ if (ret < 0)
+ goto err_clk_disable;
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "iio dev register failed\n");
+ goto err_clk_disable;
+ }
+
+ return 0;
+
+err_clk_disable:
+ clk_disable_unprepare(adc->clk);
+
+ return ret;
+}
+
+static int stm32_adc_remove(struct platform_device *pdev)
+{
+ struct stm32_adc *adc = platform_get_drvdata(pdev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ iio_device_unregister(indio_dev);
+ clk_disable_unprepare(adc->clk);
+
+ return 0;
+}
+
+static const struct of_device_id stm32_adc_of_match[] = {
+ { .compatible = "st,stm32f4-adc" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
+
+static struct platform_driver stm32_adc_driver = {
+ .probe = stm32_adc_probe,
+ .remove = stm32_adc_remove,
+ .driver = {
+ .name = "stm32-adc",
+ .of_match_table = stm32_adc_of_match,
+ },
+};
+module_platform_driver(stm32_adc_driver);
+
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 ADC IIO driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stm32-adc");
--
1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Jonathan Cameron <jic23@kernel.org> |
|---|---|
| Date | 2016-11-19 13:20 +0100 |
| Subject | Re: [PATCH v3 3/6] iio: adc: Add support for STM32 ADC |
| Message-ID | <sFcMG-4Qx-7@gated-at.bofh.it> |
| In reply to | #1522849 |
On 15/11/16 15:30, Fabrice Gasnier wrote:
> This patch adds support for STMicroelectronics STM32 MCU's analog to
> digital converter.
>
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Applied to the togreg branch of iio.git and pushed out as testing
for the autobuilders to play with it.
Very nice driver!
Thanks,
Jonathan
> ---
> drivers/iio/adc/Kconfig | 10 +
> drivers/iio/adc/Makefile | 1 +
> drivers/iio/adc/stm32-adc.c | 518 ++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 529 insertions(+)
> create mode 100644 drivers/iio/adc/stm32-adc.c
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index ff30239..f93b990 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -432,6 +432,16 @@ config STM32_ADC_CORE
> This driver can also be built as a module. If so, the module
> will be called stm32-adc-core.
>
> +config STM32_ADC
> + tristate "STMicroelectronics STM32 adc"
> + depends on STM32_ADC_CORE
> + help
> + Say yes here to build support for 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 STX104
> tristate "Apex Embedded Systems STX104 driver"
> depends on X86 && ISA_BUS_API
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index a1e8f44..8e02a94 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -42,6 +42,7 @@ obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
> obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
> obj-$(CONFIG_STX104) += stx104.o
> obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
> +obj-$(CONFIG_STM32_ADC) += stm32-adc.o
> obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
> obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
> obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> new file mode 100644
> index 0000000..5715e79
> --- /dev/null
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -0,0 +1,518 @@
> +/*
> + * This file is part of STM32 ADC driver
> + *
> + * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
> + * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
> + *
> + * License type: GPLv2
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms of the GNU General Public License version 2 as published by
> + * the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
> + * or FITNESS FOR A PARTICULAR PURPOSE.
> + * See the GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License along with
> + * this program. If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/iio/iio.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/of.h>
> +
> +#include "stm32-adc-core.h"
> +
> +/* STM32F4 - Registers for each ADC instance */
> +#define STM32F4_ADC_SR 0x00
> +#define STM32F4_ADC_CR1 0x04
> +#define STM32F4_ADC_CR2 0x08
> +#define STM32F4_ADC_SMPR1 0x0C
> +#define STM32F4_ADC_SMPR2 0x10
> +#define STM32F4_ADC_HTR 0x24
> +#define STM32F4_ADC_LTR 0x28
> +#define STM32F4_ADC_SQR1 0x2C
> +#define STM32F4_ADC_SQR2 0x30
> +#define STM32F4_ADC_SQR3 0x34
> +#define STM32F4_ADC_JSQR 0x38
> +#define STM32F4_ADC_JDR1 0x3C
> +#define STM32F4_ADC_JDR2 0x40
> +#define STM32F4_ADC_JDR3 0x44
> +#define STM32F4_ADC_JDR4 0x48
> +#define STM32F4_ADC_DR 0x4C
> +
> +/* STM32F4_ADC_SR - bit fields */
> +#define STM32F4_STRT BIT(4)
> +#define STM32F4_EOC BIT(1)
> +
> +/* STM32F4_ADC_CR1 - bit fields */
> +#define STM32F4_SCAN BIT(8)
> +#define STM32F4_EOCIE BIT(5)
> +
> +/* STM32F4_ADC_CR2 - bit fields */
> +#define STM32F4_SWSTART BIT(30)
> +#define STM32F4_EXTEN_MASK GENMASK(29, 28)
> +#define STM32F4_EOCS BIT(10)
> +#define STM32F4_ADON BIT(0)
> +
> +/* STM32F4_ADC_SQR1 - bit fields */
> +#define STM32F4_L_SHIFT 20
> +#define STM32F4_L_MASK GENMASK(23, 20)
> +
> +/* STM32F4_ADC_SQR3 - bit fields */
> +#define STM32F4_SQ1_SHIFT 0
> +#define STM32F4_SQ1_MASK GENMASK(4, 0)
> +
> +#define STM32_ADC_TIMEOUT_US 100000
> +#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
> +
> +/**
> + * struct stm32_adc - private data of each ADC IIO instance
> + * @common: reference to ADC block common data
> + * @offset: ADC instance register offset in ADC block
> + * @completion: end of single conversion completion
> + * @buffer: data buffer
> + * @clk: clock for this adc instance
> + * @irq: interrupt for this adc instance
> + * @lock: spinlock
> + */
> +struct stm32_adc {
> + struct stm32_adc_common *common;
> + u32 offset;
> + struct completion completion;
> + u16 *buffer;
> + struct clk *clk;
> + int irq;
> + spinlock_t lock; /* interrupt lock */
> +};
> +
> +/**
> + * 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;
> +};
> +
> +/* Input definitions common for all STM32F4 instances */
> +static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
> + { 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" },
> +};
> +
> +/**
> + * STM32 ADC registers access routines
> + * @adc: stm32 adc instance
> + * @reg: reg offset in adc instance
> + *
> + * Note: All instances share same base, with 0x0, 0x100 or 0x200 offset resp.
> + * for adc1, adc2 and adc3.
> + */
> +static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
> +{
> + return readl_relaxed(adc->common->base + adc->offset + reg);
> +}
> +
> +static u16 stm32_adc_readw(struct stm32_adc *adc, u32 reg)
> +{
> + return readw_relaxed(adc->common->base + adc->offset + reg);
> +}
> +
> +static void stm32_adc_writel(struct stm32_adc *adc, u32 reg, u32 val)
> +{
> + writel_relaxed(val, adc->common->base + adc->offset + reg);
> +}
> +
> +static 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 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_adc_conv_irq_enable() - Enable end of conversion interrupt
> + * @adc: stm32 adc instance
> + */
> +static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
> +{
> + stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
> +};
> +
> +/**
> + * stm32_adc_conv_irq_disable() - Disable end of conversion interrupt
> + * @adc: stm32 adc instance
> + */
> +static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
> +{
> + stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
> +}
> +
> +/**
> + * stm32_adc_start_conv() - Start conversions for regular channels.
> + * @adc: stm32 adc instance
> + */
> +static void stm32_adc_start_conv(struct stm32_adc *adc)
> +{
> + stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
> + stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_EOCS | STM32F4_ADON);
> +
> + /* Wait for Power-up time (tSTAB from datasheet) */
> + usleep_range(2, 3);
> +
> + /* Software start ? (e.g. trigger detection disabled ?) */
> + if (!(stm32_adc_readl(adc, STM32F4_ADC_CR2) & STM32F4_EXTEN_MASK))
> + stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_SWSTART);
> +}
> +
> +static void stm32_adc_stop_conv(struct stm32_adc *adc)
> +{
> + stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
> + stm32_adc_clr_bits(adc, STM32F4_ADC_SR, STM32F4_STRT);
> +
> + stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
> + stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_ADON);
> +}
> +
> +/**
> + * stm32_adc_single_conv() - Performs a single conversion
> + * @indio_dev: IIO device
> + * @chan: IIO channel
> + * @res: conversion result
> + *
> + * The function performs a single conversion on a given channel:
> + * - Program sequencer with one channel (e.g. in SQ1 with len = 1)
> + * - Use SW trigger
> + * - Start conversion, then wait for interrupt completion.
> + */
> +static int stm32_adc_single_conv(struct iio_dev *indio_dev,
> + const struct iio_chan_spec *chan,
> + int *res)
> +{
> + struct stm32_adc *adc = iio_priv(indio_dev);
> + long timeout;
> + u32 val;
> + u16 result;
> + int ret;
> +
> + reinit_completion(&adc->completion);
> +
> + adc->buffer = &result;
> +
> + /* Program chan number in regular sequence */
> + val = stm32_adc_readl(adc, STM32F4_ADC_SQR3);
> + val &= ~STM32F4_SQ1_MASK;
> + val |= chan->channel << STM32F4_SQ1_SHIFT;
> + stm32_adc_writel(adc, STM32F4_ADC_SQR3, val);
> +
> + /* Set regular sequence len (0 for 1 conversion) */
> + stm32_adc_clr_bits(adc, STM32F4_ADC_SQR1, STM32F4_L_MASK);
> +
> + /* Trigger detection disabled (conversion can be launched in SW) */
> + stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
> +
> + stm32_adc_conv_irq_enable(adc);
> +
> + stm32_adc_start_conv(adc);
> +
> + timeout = wait_for_completion_interruptible_timeout(
> + &adc->completion, STM32_ADC_TIMEOUT);
> + if (timeout == 0) {
> + ret = -ETIMEDOUT;
> + } else if (timeout < 0) {
> + ret = timeout;
> + } else {
> + *res = result;
> + ret = IIO_VAL_INT;
> + }
> +
> + stm32_adc_stop_conv(adc);
> +
> + stm32_adc_conv_irq_disable(adc);
> +
> + 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;
> +
> + 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);
> + else
> + ret = -EINVAL;
> + iio_device_release_direct_mode(indio_dev);
> + return ret;
> +
> + case IIO_CHAN_INFO_SCALE:
> + *val = adc->common->vref_mv;
> + *val2 = chan->scan_type.realbits;
> + return IIO_VAL_FRACTIONAL_LOG2;
> +
> + default:
> + return -EINVAL;
> + }
> +}
> +
> +static irqreturn_t stm32_adc_isr(int irq, void *data)
> +{
> + struct stm32_adc *adc = data;
> + u32 status = stm32_adc_readl(adc, STM32F4_ADC_SR);
> +
> + if (status & STM32F4_EOC) {
> + *adc->buffer = stm32_adc_readw(adc, STM32F4_ADC_DR);
> + complete(&adc->completion);
> + return IRQ_HANDLED;
> + }
> +
> + return IRQ_NONE;
> +}
> +
> +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,
> + .debugfs_reg_access = stm32_adc_debugfs_reg_access,
> + .of_xlate = stm32_adc_of_xlate,
> + .driver_module = THIS_MODULE,
> +};
> +
> +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)
> +{
> + chan->type = channel->type;
> + chan->channel = channel->channel;
> + chan->datasheet_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 = 12;
> + chan->scan_type.storagebits = 16;
> +}
> +
> +static int stm32_adc_chan_of_init(struct iio_dev *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;
> + u32 val;
> +
> + num_channels = of_property_count_u32_elems(node, "st,adc-channels");
> + if (num_channels < 0 ||
> + num_channels >= ARRAY_SIZE(stm32f4_adc123_channels)) {
> + dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
> + return num_channels < 0 ? num_channels : -EINVAL;
> + }
> +
> + 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) {
> + if (val >= ARRAY_SIZE(stm32f4_adc123_channels)) {
> + dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
> + return -EINVAL;
> + }
> + stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
> + &stm32f4_adc123_channels[val],
> + scan_index);
> + scan_index++;
> + }
> +
> + indio_dev->num_channels = scan_index;
> + indio_dev->channels = channels;
> +
> + return 0;
> +}
> +
> +static int stm32_adc_probe(struct platform_device *pdev)
> +{
> + struct iio_dev *indio_dev;
> + struct stm32_adc *adc;
> + int ret;
> +
> + if (!pdev->dev.of_node)
> + return -ENODEV;
> +
> + indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + adc = iio_priv(indio_dev);
> + adc->common = dev_get_drvdata(pdev->dev.parent);
> + spin_lock_init(&adc->lock);
> + init_completion(&adc->completion);
> +
> + indio_dev->name = dev_name(&pdev->dev);
> + indio_dev->dev.parent = &pdev->dev;
> + indio_dev->dev.of_node = pdev->dev.of_node;
> + indio_dev->info = &stm32_adc_iio_info;
> + indio_dev->modes = INDIO_DIRECT_MODE;
> +
> + platform_set_drvdata(pdev, adc);
> +
> + ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset);
> + if (ret != 0) {
> + dev_err(&pdev->dev, "missing reg property\n");
> + return -EINVAL;
> + }
> +
> + adc->irq = platform_get_irq(pdev, 0);
> + if (adc->irq < 0) {
> + dev_err(&pdev->dev, "failed to get irq\n");
> + return adc->irq;
> + }
> +
> + ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
> + 0, pdev->name, adc);
> + if (ret) {
> + dev_err(&pdev->dev, "failed to request IRQ\n");
> + return ret;
> + }
> +
> + adc->clk = devm_clk_get(&pdev->dev, NULL);
> + if (IS_ERR(adc->clk)) {
> + dev_err(&pdev->dev, "Can't get clock\n");
> + return PTR_ERR(adc->clk);
> + }
> +
> + ret = clk_prepare_enable(adc->clk);
> + if (ret < 0) {
> + dev_err(&pdev->dev, "clk enable failed\n");
> + return ret;
> + }
> +
> + ret = stm32_adc_chan_of_init(indio_dev);
> + if (ret < 0)
> + goto err_clk_disable;
> +
> + ret = iio_device_register(indio_dev);
> + if (ret) {
> + dev_err(&pdev->dev, "iio dev register failed\n");
> + goto err_clk_disable;
> + }
> +
> + return 0;
> +
> +err_clk_disable:
> + clk_disable_unprepare(adc->clk);
> +
> + return ret;
> +}
> +
> +static int stm32_adc_remove(struct platform_device *pdev)
> +{
> + struct stm32_adc *adc = platform_get_drvdata(pdev);
> + struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +
> + iio_device_unregister(indio_dev);
> + clk_disable_unprepare(adc->clk);
> +
> + return 0;
> +}
> +
> +static const struct of_device_id stm32_adc_of_match[] = {
> + { .compatible = "st,stm32f4-adc" },
> + {},
> +};
> +MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
> +
> +static struct platform_driver stm32_adc_driver = {
> + .probe = stm32_adc_probe,
> + .remove = stm32_adc_remove,
> + .driver = {
> + .name = "stm32-adc",
> + .of_match_table = stm32_adc_of_match,
> + },
> +};
> +module_platform_driver(stm32_adc_driver);
> +
> +MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
> +MODULE_DESCRIPTION("STMicroelectronics STM32 ADC IIO driver");
> +MODULE_LICENSE("GPL v2");
> +MODULE_ALIAS("platform:stm32-adc");
>
[toc] | [prev] | [next] | [standalone]
| From | Fabrice Gasnier <fabrice.gasnier@st.com> |
|---|---|
| Date | 2016-11-15 16:40 +0100 |
| Subject | [PATCH v3 4/6] ARM: configs: stm32: enable ADC driver |
| Message-ID | <sDO03-6JS-63@gated-at.bofh.it> |
| In reply to | #1522843 |
Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com> --- arch/arm/configs/stm32_defconfig | 3 +++ 1 file changed, 3 insertions(+) diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig index 1e5ec2a..5d241e0 100644 --- a/arch/arm/configs/stm32_defconfig +++ b/arch/arm/configs/stm32_defconfig @@ -57,6 +57,9 @@ CONFIG_LEDS_TRIGGERS=y CONFIG_LEDS_TRIGGER_HEARTBEAT=y CONFIG_DMADEVICES=y CONFIG_STM32_DMA=y +CONFIG_IIO=y +CONFIG_STM32_ADC_CORE=y +CONFIG_STM32_ADC=y # CONFIG_FILE_LOCKING is not set # CONFIG_DNOTIFY is not set # CONFIG_INOTIFY_USER is not set -- 1.9.1
[toc] | [prev] | [next] | [standalone]
| From | Jonathan Cameron <jic23@kernel.org> |
|---|---|
| Date | 2016-11-19 13:30 +0100 |
| Subject | Re: [PATCH v3 4/6] ARM: configs: stm32: enable ADC driver |
| Message-ID | <sFcWm-4Tx-9@gated-at.bofh.it> |
| In reply to | #1522851 |
On 15/11/16 15:30, Fabrice Gasnier wrote: > Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com> The driver is now on it's way in. I'm assuming this and the two device tree patches will go via the relevant route to arm-soc. Thanks, Jonathan > --- > arch/arm/configs/stm32_defconfig | 3 +++ > 1 file changed, 3 insertions(+) > > diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig > index 1e5ec2a..5d241e0 100644 > --- a/arch/arm/configs/stm32_defconfig > +++ b/arch/arm/configs/stm32_defconfig > @@ -57,6 +57,9 @@ CONFIG_LEDS_TRIGGERS=y > CONFIG_LEDS_TRIGGER_HEARTBEAT=y > CONFIG_DMADEVICES=y > CONFIG_STM32_DMA=y > +CONFIG_IIO=y > +CONFIG_STM32_ADC_CORE=y > +CONFIG_STM32_ADC=y > # CONFIG_FILE_LOCKING is not set > # CONFIG_DNOTIFY is not set > # CONFIG_INOTIFY_USER is not set >
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web