Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1508426 > unrolled thread

[PATCH 00/10] Add support for STM32 ADC

Started byFabrice Gasnier <fabrice.gasnier@st.com>
First post2016-10-25 18:30 +0200
Last post2016-10-25 18:40 +0200
Articles 6 — 1 participant

Back to article view | Back to linux.kernel


Contents

  [PATCH 00/10] Add support for STM32 ADC Fabrice Gasnier <fabrice.gasnier@st.com> - 2016-10-25 18:30 +0200
    [PATCH 10/10] ARM: dts: stm32f429: Add dma support to adc Fabrice Gasnier <fabrice.gasnier@st.com> - 2016-10-25 18:30 +0200
    [PATCH 01/10] Documentation: dt-bindings: Document STM32 ADC DT bindings Fabrice Gasnier <fabrice.gasnier@st.com> - 2016-10-25 18:30 +0200
    [PATCH 06/10] iio: adc: stm32: add ext attrs to configure sampling time Fabrice Gasnier <fabrice.gasnier@st.com> - 2016-10-25 18:30 +0200
    [PATCH 03/10] iio: adc: stm32: add optional dma support Fabrice Gasnier <fabrice.gasnier@st.com> - 2016-10-25 18:30 +0200
    [PATCH 04/10] iio: adc: stm32: add optional support for exti gpios Fabrice Gasnier <fabrice.gasnier@st.com> - 2016-10-25 18:40 +0200

#1508426 — [PATCH 00/10] Add support for STM32 ADC

FromFabrice Gasnier <fabrice.gasnier@st.com>
Date2016-10-25 18:30 +0200
Subject[PATCH 00/10] Add support for STM32 ADC
Message-ID<swcLT-83d-11@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. It has up to 19 multiplexed input channels. Conversions can
be performed in single, continuous, scan or discontinuous mode.
Conversions can be launched in software or using hardware triggers.

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

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

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

-- 
1.9.1

[toc] | [next] | [standalone]


#1508427 — [PATCH 10/10] ARM: dts: stm32f429: Add dma support to adc

FromFabrice Gasnier <fabrice.gasnier@st.com>
Date2016-10-25 18:30 +0200
Subject[PATCH 10/10] ARM: dts: stm32f429: Add dma support to adc
Message-ID<swcLU-83d-47@gated-at.bofh.it>
In reply to#1508426
Configure STM32 ADC to use dma by default.

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

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

[toc] | [prev] | [next] | [standalone]


#1508429 — [PATCH 01/10] Documentation: dt-bindings: Document STM32 ADC DT bindings

FromFabrice Gasnier <fabrice.gasnier@st.com>
Date2016-10-25 18:30 +0200
Subject[PATCH 01/10] Documentation: dt-bindings: Document STM32 ADC DT bindings
Message-ID<swcLU-83d-43@gated-at.bofh.it>
In reply to#1508426
This patch adds documentation of device tree bindings for the STM32 ADC.

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

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

[toc] | [prev] | [next] | [standalone]


#1508432 — [PATCH 06/10] iio: adc: stm32: add ext attrs to configure sampling time

FromFabrice Gasnier <fabrice.gasnier@st.com>
Date2016-10-25 18:30 +0200
Subject[PATCH 06/10] iio: adc: stm32: add ext attrs to configure sampling time
Message-ID<swcLU-83d-49@gated-at.bofh.it>
In reply to#1508426
Add per channel "smpr" IIO extended attribute, to allow sampling
time configuration.

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

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

[toc] | [prev] | [next] | [standalone]


#1508433 — [PATCH 03/10] iio: adc: stm32: add optional dma support

FromFabrice Gasnier <fabrice.gasnier@st.com>
Date2016-10-25 18:30 +0200
Subject[PATCH 03/10] iio: adc: stm32: add optional dma support
Message-ID<swcLU-83d-51@gated-at.bofh.it>
In reply to#1508426
Add optional DMA support to STM32 ADC.
Use dma cyclic mode with at least two periods.

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

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

[toc] | [prev] | [next] | [standalone]


#1508444 — [PATCH 04/10] iio: adc: stm32: add optional support for exti gpios

FromFabrice Gasnier <fabrice.gasnier@st.com>
Date2016-10-25 18:40 +0200
Subject[PATCH 04/10] iio: adc: stm32: add optional support for exti gpios
Message-ID<swcVB-86H-35@gated-at.bofh.it>
In reply to#1508426
STM32 ADC may use EXTi signals routed internally as trigger source
for conversions. Configure them as interrupt to configure this path
in HW to adc.
Note: interrupt handler isn't required here, and corresponding interrupt
can be kept masked at exti controller level.

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

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

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web