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


Groups > linux.kernel > #1652380 > unrolled thread

[PATCH v2 0/5] iio: add support for STM32H7 ADC

Started byFabrice Gasnier <fabrice.gasnier@st.com>
First post2017-05-29 11:40 +0200
Last post2017-06-07 22:10 +0200
Articles 6 — 3 participants

Back to article view | Back to linux.kernel


Contents

  [PATCH v2 0/5] iio: add support for STM32H7 ADC Fabrice Gasnier <fabrice.gasnier@st.com> - 2017-05-29 11:40 +0200
    [PATCH v2 3/5] iio: adc: stm32: introduce compatible data cfg Fabrice Gasnier <fabrice.gasnier@st.com> - 2017-05-29 11:40 +0200
      Re: [PATCH v2 3/5] iio: adc: stm32: introduce compatible data cfg Jonathan Cameron <jic23@kernel.org> - 2017-06-03 11:40 +0200
    [PATCH v2 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7 Fabrice Gasnier <fabrice.gasnier@st.com> - 2017-05-29 11:40 +0200
      Re: [PATCH v2 1/5] dt-bindings: iio: stm32-adc: add support for  STM32H7 Jonathan Cameron <jic23@kernel.org> - 2017-06-03 11:40 +0200
      Re: [PATCH v2 1/5] dt-bindings: iio: stm32-adc: add support for  STM32H7 Rob Herring <robh@kernel.org> - 2017-06-07 22:10 +0200

#1652380 — [PATCH v2 0/5] iio: add support for STM32H7 ADC

FromFabrice Gasnier <fabrice.gasnier@st.com>
Date2017-05-29 11:40 +0200
Subject[PATCH v2 0/5] iio: add support for STM32H7 ADC
Message-ID<tMpjA-6jW-15@gated-at.bofh.it>
Add support for STM32H7 Analog to Digital Converter variant.
It has up to 20 external channels, resolution ranges from 8 to 16bits.
Differences are regarding clocking, registers and bitfields have also
been re-arranged, but behavior is similar. It also has additional self
calibration and power on/off procedures.

---
Changes in v2:
- update bindings with Rob's remarks, document stm32h7 resolutions
- Slightly re-arrange channels definition, remove a trivial comment

Fabrice Gasnier (5):
  dt-bindings: iio: stm32-adc: add support for STM32H7
  iio: adc: stm32: make core adc clock optional by default
  iio: adc: stm32: introduce compatible data cfg
  iio: adc: stm32: make per instance bus clock optional
  iio: adc: stm32: add support for STM32H7

 .../devicetree/bindings/iio/adc/st,stm32-adc.txt   |  28 +-
 drivers/iio/adc/stm32-adc-core.c                   | 269 +++++++-
 drivers/iio/adc/stm32-adc-core.h                   |   2 +
 drivers/iio/adc/stm32-adc.c                        | 762 +++++++++++++++++++--
 4 files changed, 972 insertions(+), 89 deletions(-)

-- 
1.9.1

[toc] | [next] | [standalone]


#1652381 — [PATCH v2 3/5] iio: adc: stm32: introduce compatible data cfg

FromFabrice Gasnier <fabrice.gasnier@st.com>
Date2017-05-29 11:40 +0200
Subject[PATCH v2 3/5] iio: adc: stm32: introduce compatible data cfg
Message-ID<tMpjB-6jW-31@gated-at.bofh.it>
In reply to#1652380
Prepare support for stm32h7 adc variant by introducing compatible
configuration data.
Move STM32F4 specific stuff to compatible data structure:
- registers & bit fields
- input channels data
- start/stop procedures
- trigger definitions

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
Changes in v2:
- rename stm32f4_adc123_channels to stm32_adc_channels
---
 drivers/iio/adc/stm32-adc-core.c |  62 ++++++++++--
 drivers/iio/adc/stm32-adc.c      | 202 +++++++++++++++++++++++++++++----------
 2 files changed, 205 insertions(+), 59 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index 597ab7a..c5d292c 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -50,11 +50,38 @@
 #define STM32F4_ADC_MAX_CLK_RATE	36000000
 
 /**
+ * stm32_adc_common_regs - stm32 common registers, compatible dependent data
+ * @csr:	common status register offset
+ * @eoc1:	adc1 end of conversion flag in @csr
+ * @eoc2:	adc2 end of conversion flag in @csr
+ * @eoc3:	adc3 end of conversion flag in @csr
+ */
+struct stm32_adc_common_regs {
+	u32 csr;
+	u32 eoc1_msk;
+	u32 eoc2_msk;
+	u32 eoc3_msk;
+};
+
+struct stm32_adc_priv;
+
+/**
+ * stm32_adc_priv_cfg - stm32 core compatible configuration data
+ * @regs:	common registers for all instances
+ * @clk_sel:	clock selection routine
+ */
+struct stm32_adc_priv_cfg {
+	const struct stm32_adc_common_regs *regs;
+	int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
+};
+
+/**
  * struct stm32_adc_priv - stm32 ADC core private data
  * @irq:		irq for ADC block
  * @domain:		irq domain reference
  * @aclk:		clock reference for the analog circuitry
  * @vref:		regulator reference
+ * @cfg:		compatible configuration data
  * @common:		common data for all ADC instances
  */
 struct stm32_adc_priv {
@@ -62,6 +89,7 @@ struct stm32_adc_priv {
 	struct irq_domain		*domain;
 	struct clk			*aclk;
 	struct regulator		*vref;
+	const struct stm32_adc_priv_cfg	*cfg;
 	struct stm32_adc_common		common;
 };
 
@@ -112,6 +140,14 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
 	return 0;
 }
 
+/* STM32F4 common registers definitions */
+static const struct stm32_adc_common_regs stm32f4_adc_common_regs = {
+	.csr = STM32F4_ADC_CSR,
+	.eoc1_msk = STM32F4_EOC1,
+	.eoc2_msk = STM32F4_EOC2,
+	.eoc3_msk = STM32F4_EOC3,
+};
+
 /* ADC common interrupt for all instances */
 static void stm32_adc_irq_handler(struct irq_desc *desc)
 {
@@ -120,15 +156,15 @@ static void stm32_adc_irq_handler(struct irq_desc *desc)
 	u32 status;
 
 	chained_irq_enter(chip, desc);
-	status = readl_relaxed(priv->common.base + STM32F4_ADC_CSR);
+	status = readl_relaxed(priv->common.base + priv->cfg->regs->csr);
 
-	if (status & STM32F4_EOC1)
+	if (status & priv->cfg->regs->eoc1_msk)
 		generic_handle_irq(irq_find_mapping(priv->domain, 0));
 
-	if (status & STM32F4_EOC2)
+	if (status & priv->cfg->regs->eoc2_msk)
 		generic_handle_irq(irq_find_mapping(priv->domain, 1));
 
-	if (status & STM32F4_EOC3)
+	if (status & priv->cfg->regs->eoc3_msk)
 		generic_handle_irq(irq_find_mapping(priv->domain, 2));
 
 	chained_irq_exit(chip, desc);
@@ -194,6 +230,7 @@ static void stm32_adc_irq_remove(struct platform_device *pdev,
 static int stm32_adc_probe(struct platform_device *pdev)
 {
 	struct stm32_adc_priv *priv;
+	struct device *dev = &pdev->dev;
 	struct device_node *np = pdev->dev.of_node;
 	struct resource *res;
 	int ret;
@@ -205,6 +242,9 @@ static int stm32_adc_probe(struct platform_device *pdev)
 	if (!priv)
 		return -ENOMEM;
 
+	priv->cfg = (const struct stm32_adc_priv_cfg *)
+		of_match_device(dev->driver->of_match_table, dev)->data;
+
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	priv->common.base = devm_ioremap_resource(&pdev->dev, res);
 	if (IS_ERR(priv->common.base))
@@ -251,7 +291,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
 		}
 	}
 
-	ret = stm32f4_adc_clk_sel(pdev, priv);
+	ret = priv->cfg->clk_sel(pdev, priv);
 	if (ret < 0)
 		goto err_clk_disable;
 
@@ -296,9 +336,17 @@ static int stm32_adc_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = {
+	.regs = &stm32f4_adc_common_regs,
+	.clk_sel = stm32f4_adc_clk_sel,
+};
+
 static const struct of_device_id stm32_adc_of_match[] = {
-	{ .compatible = "st,stm32f4-adc-core" },
-	{},
+	{
+		.compatible = "st,stm32f4-adc-core",
+		.data = (void *)&stm32f4_adc_priv_cfg
+	}, {
+	},
 };
 MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
 
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index c28e7ff..50b2538 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -34,6 +34,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 
 #include "stm32-adc-core.h"
 
@@ -133,9 +134,48 @@ struct stm32_adc_regs {
 };
 
 /**
+ * stm32_adc_regspec - stm32 registers definition, compatible dependent data
+ * @dr:			data register offset
+ * @ier_eoc:		interrupt enable register & eocie bitfield
+ * @isr_eoc:		interrupt status register & eoc bitfield
+ * @sqr:		reference to sequence registers array
+ * @exten:		trigger control register & bitfield
+ * @extsel:		trigger selection register & bitfield
+ * @res:		resolution selection register & bitfield
+ */
+struct stm32_adc_regspec {
+	const u32 dr;
+	const struct stm32_adc_regs ier_eoc;
+	const struct stm32_adc_regs isr_eoc;
+	const struct stm32_adc_regs *sqr;
+	const struct stm32_adc_regs exten;
+	const struct stm32_adc_regs extsel;
+	const struct stm32_adc_regs res;
+};
+
+struct stm32_adc;
+
+/**
+ * stm32_adc_cfg - stm32 compatible configuration data
+ * @regs:		registers descriptions
+ * @adc_info:		per instance input channels definitions
+ * @trigs:		external trigger sources
+ * @start_conv:		routine to start conversions
+ * @stop_conv:		routine to stop conversions
+ */
+struct stm32_adc_cfg {
+	const struct stm32_adc_regspec	*regs;
+	const struct stm32_adc_info	*adc_info;
+	struct stm32_adc_trig_info	*trigs;
+	void (*start_conv)(struct stm32_adc *, bool dma);
+	void (*stop_conv)(struct stm32_adc *);
+};
+
+/**
  * 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
+ * @cfg:		compatible configuration data
  * @completion:		end of single conversion completion
  * @buffer:		data buffer
  * @clk:		clock for this adc instance
@@ -153,6 +193,7 @@ struct stm32_adc_regs {
 struct stm32_adc {
 	struct stm32_adc_common	*common;
 	u32			offset;
+	const struct stm32_adc_cfg	*cfg;
 	struct completion	completion;
 	u16			buffer[STM32_ADC_MAX_SQ];
 	struct clk		*clk;
@@ -180,8 +221,25 @@ struct stm32_adc_chan_spec {
 	const char		*name;
 };
 
-/* Input definitions common for all STM32F4 instances */
-static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
+/**
+ * struct stm32_adc_info - stm32 ADC, per instance config data
+ * @channels:		Reference to stm32 channels spec
+ * @max_channels:	Number of channels
+ * @resolutions:	available resolutions
+ * @num_res:		number of available resolutions
+ */
+struct stm32_adc_info {
+	const struct stm32_adc_chan_spec *channels;
+	int max_channels;
+	const unsigned int *resolutions;
+	const unsigned int num_res;
+};
+
+/*
+ * Input definitions common for all instances:
+ * stm32f4 can have up to 16 channels
+ */
+static const struct stm32_adc_chan_spec stm32_adc_channels[] = {
 	{ IIO_VOLTAGE, 0, "in0" },
 	{ IIO_VOLTAGE, 1, "in1" },
 	{ IIO_VOLTAGE, 2, "in2" },
@@ -205,6 +263,13 @@ struct stm32_adc_chan_spec {
 	12, 10, 8, 6,
 };
 
+static const struct stm32_adc_info stm32f4_adc_info = {
+	.channels = stm32_adc_channels,
+	.max_channels = 16,
+	.resolutions = stm32f4_adc_resolutions,
+	.num_res = ARRAY_SIZE(stm32f4_adc_resolutions),
+};
+
 /**
  * stm32f4_sq - describe regular sequence registers
  * - L: sequence len (register & bit field)
@@ -252,6 +317,17 @@ struct stm32_adc_chan_spec {
 	{}, /* sentinel */
 };
 
+static const struct stm32_adc_regspec stm32f4_adc_regspec = {
+	.dr = STM32F4_ADC_DR,
+	.ier_eoc = { STM32F4_ADC_CR1, STM32F4_EOCIE },
+	.isr_eoc = { STM32F4_ADC_SR, STM32F4_EOC },
+	.sqr = stm32f4_sq,
+	.exten = { STM32F4_ADC_CR2, STM32F4_EXTEN_MASK, STM32F4_EXTEN_SHIFT },
+	.extsel = { STM32F4_ADC_CR2, STM32F4_EXTSEL_MASK,
+		    STM32F4_EXTSEL_SHIFT },
+	.res = { STM32F4_ADC_CR1, STM32F4_RES_MASK, STM32F4_RES_SHIFT },
+};
+
 /**
  * STM32 ADC registers access routines
  * @adc: stm32 adc instance
@@ -299,7 +375,8 @@ static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
  */
 static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
 {
-	stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
+	stm32_adc_set_bits(adc, adc->cfg->regs->ier_eoc.reg,
+			   adc->cfg->regs->ier_eoc.mask);
 };
 
 /**
@@ -308,19 +385,22 @@ static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
  */
 static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
 {
-	stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
+	stm32_adc_clr_bits(adc, adc->cfg->regs->ier_eoc.reg,
+			   adc->cfg->regs->ier_eoc.mask);
 }
 
 static void stm32_adc_set_res(struct stm32_adc *adc)
 {
-	u32 val = stm32_adc_readl(adc, STM32F4_ADC_CR1);
+	const struct stm32_adc_regs *res = &adc->cfg->regs->res;
+	u32 val;
 
-	val = (val & ~STM32F4_RES_MASK) | (adc->res << STM32F4_RES_SHIFT);
-	stm32_adc_writel(adc, STM32F4_ADC_CR1, val);
+	val = stm32_adc_readl(adc, res->reg);
+	val = (val & ~res->mask) | (adc->res << res->shift);
+	stm32_adc_writel(adc, res->reg, val);
 }
 
 /**
- * stm32_adc_start_conv() - Start conversions for regular channels.
+ * stm32f4_adc_start_conv() - Start conversions for regular channels.
  * @adc: stm32 adc instance
  * @dma: use dma to transfer conversion result
  *
@@ -329,7 +409,7 @@ static void stm32_adc_set_res(struct stm32_adc *adc)
  * conversions, in IIO buffer modes. Otherwise, use ADC interrupt with direct
  * DR read instead (e.g. read_raw, or triggered buffer mode without DMA).
  */
-static void stm32_adc_start_conv(struct stm32_adc *adc, bool dma)
+static void stm32f4_adc_start_conv(struct stm32_adc *adc, bool dma)
 {
 	stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
 
@@ -347,7 +427,7 @@ static void stm32_adc_start_conv(struct stm32_adc *adc, bool dma)
 		stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_SWSTART);
 }
 
-static void stm32_adc_stop_conv(struct stm32_adc *adc)
+static void stm32f4_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);
@@ -371,6 +451,7 @@ 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 *sqr = adc->cfg->regs->sqr;
 	const struct iio_chan_spec *chan;
 	u32 val, bit;
 	int i = 0;
@@ -388,20 +469,20 @@ static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
 		dev_dbg(&indio_dev->dev, "%s chan %d to SQ%d\n",
 			__func__, chan->channel, i);
 
-		val = stm32_adc_readl(adc, stm32f4_sq[i].reg);
-		val &= ~stm32f4_sq[i].mask;
-		val |= chan->channel << stm32f4_sq[i].shift;
-		stm32_adc_writel(adc, stm32f4_sq[i].reg, val);
+		val = stm32_adc_readl(adc, sqr[i].reg);
+		val &= ~sqr[i].mask;
+		val |= chan->channel << sqr[i].shift;
+		stm32_adc_writel(adc, sqr[i].reg, val);
 	}
 
 	if (!i)
 		return -EINVAL;
 
 	/* Sequence len */
-	val = stm32_adc_readl(adc, stm32f4_sq[0].reg);
-	val &= ~stm32f4_sq[0].mask;
-	val |= ((i - 1) << stm32f4_sq[0].shift);
-	stm32_adc_writel(adc, stm32f4_sq[0].reg, val);
+	val = stm32_adc_readl(adc, sqr[0].reg);
+	val &= ~sqr[0].mask;
+	val |= ((i - 1) << sqr[0].shift);
+	stm32_adc_writel(adc, sqr[0].reg, val);
 
 	return 0;
 }
@@ -412,19 +493,21 @@ static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
  *
  * Returns trigger extsel value, if trig matches, -EINVAL otherwise.
  */
-static int stm32_adc_get_trig_extsel(struct iio_trigger *trig)
+static int stm32_adc_get_trig_extsel(struct iio_dev *indio_dev,
+				     struct iio_trigger *trig)
 {
+	struct stm32_adc *adc = iio_priv(indio_dev);
 	int i;
 
 	/* lookup triggers registered by stm32 timer trigger driver */
-	for (i = 0; stm32f4_adc_trigs[i].name; i++) {
+	for (i = 0; adc->cfg->trigs[i].name; i++) {
 		/**
 		 * Checking both stm32 timer trigger type and trig name
 		 * should be safe against arbitrary trigger names.
 		 */
 		if (is_stm32_timer_trigger(trig) &&
-		    !strcmp(stm32f4_adc_trigs[i].name, trig->name)) {
-			return stm32f4_adc_trigs[i].extsel;
+		    !strcmp(adc->cfg->trigs[i].name, trig->name)) {
+			return adc->cfg->trigs[i].extsel;
 		}
 	}
 
@@ -449,7 +532,7 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
 	int ret;
 
 	if (trig) {
-		ret = stm32_adc_get_trig_extsel(trig);
+		ret = stm32_adc_get_trig_extsel(indio_dev, trig);
 		if (ret < 0)
 			return ret;
 
@@ -459,11 +542,11 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
 	}
 
 	spin_lock_irqsave(&adc->lock, flags);
-	val = stm32_adc_readl(adc, STM32F4_ADC_CR2);
-	val &= ~(STM32F4_EXTEN_MASK | STM32F4_EXTSEL_MASK);
-	val |= exten << STM32F4_EXTEN_SHIFT;
-	val |= extsel << STM32F4_EXTSEL_SHIFT;
-	stm32_adc_writel(adc, STM32F4_ADC_CR2, val);
+	val = stm32_adc_readl(adc, adc->cfg->regs->exten.reg);
+	val &= ~(adc->cfg->regs->exten.mask | adc->cfg->regs->extsel.mask);
+	val |= exten << adc->cfg->regs->exten.shift;
+	val |= extsel << adc->cfg->regs->extsel.shift;
+	stm32_adc_writel(adc,  adc->cfg->regs->exten.reg, val);
 	spin_unlock_irqrestore(&adc->lock, flags);
 
 	return 0;
@@ -515,6 +598,7 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
 				 int *res)
 {
 	struct stm32_adc *adc = iio_priv(indio_dev);
+	const struct stm32_adc_regspec *regs = adc->cfg->regs;
 	long timeout;
 	u32 val;
 	int ret;
@@ -524,20 +608,20 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
 	adc->bufi = 0;
 
 	/* Program chan number in regular sequence (SQ1) */
-	val = stm32_adc_readl(adc, stm32f4_sq[1].reg);
-	val &= ~stm32f4_sq[1].mask;
-	val |= chan->channel << stm32f4_sq[1].shift;
-	stm32_adc_writel(adc, stm32f4_sq[1].reg, val);
+	val = stm32_adc_readl(adc, regs->sqr[1].reg);
+	val &= ~regs->sqr[1].mask;
+	val |= chan->channel << regs->sqr[1].shift;
+	stm32_adc_writel(adc, regs->sqr[1].reg, val);
 
 	/* Set regular sequence len (0 for 1 conversion) */
-	stm32_adc_clr_bits(adc, stm32f4_sq[0].reg, stm32f4_sq[0].mask);
+	stm32_adc_clr_bits(adc, regs->sqr[0].reg, regs->sqr[0].mask);
 
 	/* Trigger detection disabled (conversion can be launched in SW) */
-	stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
+	stm32_adc_clr_bits(adc, regs->exten.reg, regs->exten.mask);
 
 	stm32_adc_conv_irq_enable(adc);
 
-	stm32_adc_start_conv(adc, false);
+	adc->cfg->start_conv(adc, false);
 
 	timeout = wait_for_completion_interruptible_timeout(
 					&adc->completion, STM32_ADC_TIMEOUT);
@@ -550,7 +634,7 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
 		ret = IIO_VAL_INT;
 	}
 
-	stm32_adc_stop_conv(adc);
+	adc->cfg->stop_conv(adc);
 
 	stm32_adc_conv_irq_disable(adc);
 
@@ -590,11 +674,12 @@ static irqreturn_t stm32_adc_isr(int irq, void *data)
 {
 	struct stm32_adc *adc = data;
 	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
-	u32 status = stm32_adc_readl(adc, STM32F4_ADC_SR);
+	const struct stm32_adc_regspec *regs = adc->cfg->regs;
+	u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg);
 
-	if (status & STM32F4_EOC) {
+	if (status & regs->isr_eoc.mask) {
 		/* Reading DR also clears EOC status flag */
-		adc->buffer[adc->bufi] = stm32_adc_readw(adc, STM32F4_ADC_DR);
+		adc->buffer[adc->bufi] = stm32_adc_readw(adc, regs->dr);
 		if (iio_buffer_enabled(indio_dev)) {
 			adc->bufi++;
 			if (adc->bufi >= adc->num_conv) {
@@ -621,7 +706,7 @@ static irqreturn_t stm32_adc_isr(int irq, void *data)
 static int stm32_adc_validate_trigger(struct iio_dev *indio_dev,
 				      struct iio_trigger *trig)
 {
-	return stm32_adc_get_trig_extsel(trig) < 0 ? -EINVAL : 0;
+	return stm32_adc_get_trig_extsel(indio_dev, trig) < 0 ? -EINVAL : 0;
 }
 
 static int stm32_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val)
@@ -799,7 +884,7 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
 	if (!adc->dma_chan)
 		stm32_adc_conv_irq_enable(adc);
 
-	stm32_adc_start_conv(adc, !!adc->dma_chan);
+	adc->cfg->start_conv(adc, !!adc->dma_chan);
 
 	return 0;
 
@@ -817,7 +902,7 @@ static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
 	struct stm32_adc *adc = iio_priv(indio_dev);
 	int ret;
 
-	stm32_adc_stop_conv(adc);
+	adc->cfg->stop_conv(adc);
 	if (!adc->dma_chan)
 		stm32_adc_conv_irq_disable(adc);
 
@@ -895,12 +980,12 @@ static int stm32_adc_of_get_resolution(struct iio_dev *indio_dev)
 	u32 res;
 
 	if (of_property_read_u32(node, "assigned-resolution-bits", &res))
-		res = stm32f4_adc_resolutions[0];
+		res = adc->cfg->adc_info->resolutions[0];
 
-	for (i = 0; i < ARRAY_SIZE(stm32f4_adc_resolutions); i++)
-		if (res == stm32f4_adc_resolutions[i])
+	for (i = 0; i < adc->cfg->adc_info->num_res; i++)
+		if (res == adc->cfg->adc_info->resolutions[i])
 			break;
-	if (i >= ARRAY_SIZE(stm32f4_adc_resolutions)) {
+	if (i >= adc->cfg->adc_info->num_res) {
 		dev_err(&indio_dev->dev, "Bad resolution: %u bits\n", res);
 		return -EINVAL;
 	}
@@ -926,7 +1011,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
 	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 = stm32f4_adc_resolutions[adc->res];
+	chan->scan_type.realbits = adc->cfg->adc_info->resolutions[adc->res];
 	chan->scan_type.storagebits = 16;
 	chan->ext_info = stm32_adc_ext_info;
 }
@@ -934,6 +1019,8 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
 static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
 {
 	struct device_node *node = indio_dev->dev.of_node;
+	struct stm32_adc *adc = iio_priv(indio_dev);
+	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
 	struct property *prop;
 	const __be32 *cur;
 	struct iio_chan_spec *channels;
@@ -942,7 +1029,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
 
 	num_channels = of_property_count_u32_elems(node, "st,adc-channels");
 	if (num_channels < 0 ||
-	    num_channels >= ARRAY_SIZE(stm32f4_adc123_channels)) {
+	    num_channels >= adc_info->max_channels) {
 		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
 		return num_channels < 0 ? num_channels : -EINVAL;
 	}
@@ -953,12 +1040,12 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
 		return -ENOMEM;
 
 	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
-		if (val >= ARRAY_SIZE(stm32f4_adc123_channels)) {
+		if (val >= adc_info->max_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],
+					&adc_info->channels[val],
 					scan_index);
 		scan_index++;
 	}
@@ -990,7 +1077,7 @@ static int stm32_adc_dma_request(struct iio_dev *indio_dev)
 	/* 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 + STM32F4_ADC_DR;
+	config.src_addr += adc->offset + adc->cfg->regs->dr;
 	config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
 
 	ret = dmaengine_slave_config(adc->dma_chan, &config);
@@ -1011,6 +1098,7 @@ static int stm32_adc_dma_request(struct iio_dev *indio_dev)
 static int stm32_adc_probe(struct platform_device *pdev)
 {
 	struct iio_dev *indio_dev;
+	struct device *dev = &pdev->dev;
 	struct stm32_adc *adc;
 	int ret;
 
@@ -1025,6 +1113,8 @@ static int stm32_adc_probe(struct platform_device *pdev)
 	adc->common = dev_get_drvdata(pdev->dev.parent);
 	spin_lock_init(&adc->lock);
 	init_completion(&adc->completion);
+	adc->cfg = (const struct stm32_adc_cfg *)
+		of_match_device(dev->driver->of_match_table, dev)->data;
 
 	indio_dev->name = dev_name(&pdev->dev);
 	indio_dev->dev.parent = &pdev->dev;
@@ -1129,8 +1219,16 @@ static int stm32_adc_remove(struct platform_device *pdev)
 	return 0;
 }
 
+static const struct stm32_adc_cfg stm32f4_adc_cfg = {
+	.regs = &stm32f4_adc_regspec,
+	.adc_info = &stm32f4_adc_info,
+	.trigs = stm32f4_adc_trigs,
+	.start_conv = stm32f4_adc_start_conv,
+	.stop_conv = stm32f4_adc_stop_conv,
+};
+
 static const struct of_device_id stm32_adc_of_match[] = {
-	{ .compatible = "st,stm32f4-adc" },
+	{ .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg },
 	{},
 };
 MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
-- 
1.9.1

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


#1656768 — Re: [PATCH v2 3/5] iio: adc: stm32: introduce compatible data cfg

FromJonathan Cameron <jic23@kernel.org>
Date2017-06-03 11:40 +0200
SubjectRe: [PATCH v2 3/5] iio: adc: stm32: introduce compatible data cfg
Message-ID<tOdHk-5hr-7@gated-at.bofh.it>
In reply to#1652381
On Mon, 29 May 2017 11:28:18 +0200
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:

> Prepare support for stm32h7 adc variant by introducing compatible
> configuration data.
> Move STM32F4 specific stuff to compatible data structure:
> - registers & bit fields
> - input channels data
> - start/stop procedures
> - trigger definitions
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Applied.
> ---
> Changes in v2:
> - rename stm32f4_adc123_channels to stm32_adc_channels
> ---
>  drivers/iio/adc/stm32-adc-core.c |  62 ++++++++++--
>  drivers/iio/adc/stm32-adc.c      | 202 +++++++++++++++++++++++++++++----------
>  2 files changed, 205 insertions(+), 59 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
> index 597ab7a..c5d292c 100644
> --- a/drivers/iio/adc/stm32-adc-core.c
> +++ b/drivers/iio/adc/stm32-adc-core.c
> @@ -50,11 +50,38 @@
>  #define STM32F4_ADC_MAX_CLK_RATE	36000000
>  
>  /**
> + * stm32_adc_common_regs - stm32 common registers, compatible dependent data
> + * @csr:	common status register offset
> + * @eoc1:	adc1 end of conversion flag in @csr
> + * @eoc2:	adc2 end of conversion flag in @csr
> + * @eoc3:	adc3 end of conversion flag in @csr
> + */
> +struct stm32_adc_common_regs {
> +	u32 csr;
> +	u32 eoc1_msk;
> +	u32 eoc2_msk;
> +	u32 eoc3_msk;
> +};
> +
> +struct stm32_adc_priv;
> +
> +/**
> + * stm32_adc_priv_cfg - stm32 core compatible configuration data
> + * @regs:	common registers for all instances
> + * @clk_sel:	clock selection routine
> + */
> +struct stm32_adc_priv_cfg {
> +	const struct stm32_adc_common_regs *regs;
> +	int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
> +};
> +
> +/**
>   * struct stm32_adc_priv - stm32 ADC core private data
>   * @irq:		irq for ADC block
>   * @domain:		irq domain reference
>   * @aclk:		clock reference for the analog circuitry
>   * @vref:		regulator reference
> + * @cfg:		compatible configuration data
>   * @common:		common data for all ADC instances
>   */
>  struct stm32_adc_priv {
> @@ -62,6 +89,7 @@ struct stm32_adc_priv {
>  	struct irq_domain		*domain;
>  	struct clk			*aclk;
>  	struct regulator		*vref;
> +	const struct stm32_adc_priv_cfg	*cfg;
>  	struct stm32_adc_common		common;
>  };
>  
> @@ -112,6 +140,14 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
>  	return 0;
>  }
>  
> +/* STM32F4 common registers definitions */
> +static const struct stm32_adc_common_regs stm32f4_adc_common_regs = {
> +	.csr = STM32F4_ADC_CSR,
> +	.eoc1_msk = STM32F4_EOC1,
> +	.eoc2_msk = STM32F4_EOC2,
> +	.eoc3_msk = STM32F4_EOC3,
> +};
> +
>  /* ADC common interrupt for all instances */
>  static void stm32_adc_irq_handler(struct irq_desc *desc)
>  {
> @@ -120,15 +156,15 @@ static void stm32_adc_irq_handler(struct irq_desc *desc)
>  	u32 status;
>  
>  	chained_irq_enter(chip, desc);
> -	status = readl_relaxed(priv->common.base + STM32F4_ADC_CSR);
> +	status = readl_relaxed(priv->common.base + priv->cfg->regs->csr);
>  
> -	if (status & STM32F4_EOC1)
> +	if (status & priv->cfg->regs->eoc1_msk)
>  		generic_handle_irq(irq_find_mapping(priv->domain, 0));
>  
> -	if (status & STM32F4_EOC2)
> +	if (status & priv->cfg->regs->eoc2_msk)
>  		generic_handle_irq(irq_find_mapping(priv->domain, 1));
>  
> -	if (status & STM32F4_EOC3)
> +	if (status & priv->cfg->regs->eoc3_msk)
>  		generic_handle_irq(irq_find_mapping(priv->domain, 2));
>  
>  	chained_irq_exit(chip, desc);
> @@ -194,6 +230,7 @@ static void stm32_adc_irq_remove(struct platform_device *pdev,
>  static int stm32_adc_probe(struct platform_device *pdev)
>  {
>  	struct stm32_adc_priv *priv;
> +	struct device *dev = &pdev->dev;
>  	struct device_node *np = pdev->dev.of_node;
>  	struct resource *res;
>  	int ret;
> @@ -205,6 +242,9 @@ static int stm32_adc_probe(struct platform_device *pdev)
>  	if (!priv)
>  		return -ENOMEM;
>  
> +	priv->cfg = (const struct stm32_adc_priv_cfg *)
> +		of_match_device(dev->driver->of_match_table, dev)->data;
> +
>  	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>  	priv->common.base = devm_ioremap_resource(&pdev->dev, res);
>  	if (IS_ERR(priv->common.base))
> @@ -251,7 +291,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
>  		}
>  	}
>  
> -	ret = stm32f4_adc_clk_sel(pdev, priv);
> +	ret = priv->cfg->clk_sel(pdev, priv);
>  	if (ret < 0)
>  		goto err_clk_disable;
>  
> @@ -296,9 +336,17 @@ static int stm32_adc_remove(struct platform_device *pdev)
>  	return 0;
>  }
>  
> +static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = {
> +	.regs = &stm32f4_adc_common_regs,
> +	.clk_sel = stm32f4_adc_clk_sel,
> +};
> +
>  static const struct of_device_id stm32_adc_of_match[] = {
> -	{ .compatible = "st,stm32f4-adc-core" },
> -	{},
> +	{
> +		.compatible = "st,stm32f4-adc-core",
> +		.data = (void *)&stm32f4_adc_priv_cfg
> +	}, {
> +	},
>  };
>  MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
>  
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index c28e7ff..50b2538 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -34,6 +34,7 @@
>  #include <linux/module.h>
>  #include <linux/platform_device.h>
>  #include <linux/of.h>
> +#include <linux/of_device.h>
>  
>  #include "stm32-adc-core.h"
>  
> @@ -133,9 +134,48 @@ struct stm32_adc_regs {
>  };
>  
>  /**
> + * stm32_adc_regspec - stm32 registers definition, compatible dependent data
> + * @dr:			data register offset
> + * @ier_eoc:		interrupt enable register & eocie bitfield
> + * @isr_eoc:		interrupt status register & eoc bitfield
> + * @sqr:		reference to sequence registers array
> + * @exten:		trigger control register & bitfield
> + * @extsel:		trigger selection register & bitfield
> + * @res:		resolution selection register & bitfield
> + */
> +struct stm32_adc_regspec {
> +	const u32 dr;
> +	const struct stm32_adc_regs ier_eoc;
> +	const struct stm32_adc_regs isr_eoc;
> +	const struct stm32_adc_regs *sqr;
> +	const struct stm32_adc_regs exten;
> +	const struct stm32_adc_regs extsel;
> +	const struct stm32_adc_regs res;
> +};
> +
> +struct stm32_adc;
> +
> +/**
> + * stm32_adc_cfg - stm32 compatible configuration data
> + * @regs:		registers descriptions
> + * @adc_info:		per instance input channels definitions
> + * @trigs:		external trigger sources
> + * @start_conv:		routine to start conversions
> + * @stop_conv:		routine to stop conversions
> + */
> +struct stm32_adc_cfg {
> +	const struct stm32_adc_regspec	*regs;
> +	const struct stm32_adc_info	*adc_info;
> +	struct stm32_adc_trig_info	*trigs;
> +	void (*start_conv)(struct stm32_adc *, bool dma);
> +	void (*stop_conv)(struct stm32_adc *);
> +};
> +
> +/**
>   * 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
> + * @cfg:		compatible configuration data
>   * @completion:		end of single conversion completion
>   * @buffer:		data buffer
>   * @clk:		clock for this adc instance
> @@ -153,6 +193,7 @@ struct stm32_adc_regs {
>  struct stm32_adc {
>  	struct stm32_adc_common	*common;
>  	u32			offset;
> +	const struct stm32_adc_cfg	*cfg;
>  	struct completion	completion;
>  	u16			buffer[STM32_ADC_MAX_SQ];
>  	struct clk		*clk;
> @@ -180,8 +221,25 @@ struct stm32_adc_chan_spec {
>  	const char		*name;
>  };
>  
> -/* Input definitions common for all STM32F4 instances */
> -static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
> +/**
> + * struct stm32_adc_info - stm32 ADC, per instance config data
> + * @channels:		Reference to stm32 channels spec
> + * @max_channels:	Number of channels
> + * @resolutions:	available resolutions
> + * @num_res:		number of available resolutions
> + */
> +struct stm32_adc_info {
> +	const struct stm32_adc_chan_spec *channels;
> +	int max_channels;
> +	const unsigned int *resolutions;
> +	const unsigned int num_res;
> +};
> +
> +/*
> + * Input definitions common for all instances:
> + * stm32f4 can have up to 16 channels
> + */
> +static const struct stm32_adc_chan_spec stm32_adc_channels[] = {
>  	{ IIO_VOLTAGE, 0, "in0" },
>  	{ IIO_VOLTAGE, 1, "in1" },
>  	{ IIO_VOLTAGE, 2, "in2" },
> @@ -205,6 +263,13 @@ struct stm32_adc_chan_spec {
>  	12, 10, 8, 6,
>  };
>  
> +static const struct stm32_adc_info stm32f4_adc_info = {
> +	.channels = stm32_adc_channels,
> +	.max_channels = 16,
> +	.resolutions = stm32f4_adc_resolutions,
> +	.num_res = ARRAY_SIZE(stm32f4_adc_resolutions),
> +};
> +
>  /**
>   * stm32f4_sq - describe regular sequence registers
>   * - L: sequence len (register & bit field)
> @@ -252,6 +317,17 @@ struct stm32_adc_chan_spec {
>  	{}, /* sentinel */
>  };
>  
> +static const struct stm32_adc_regspec stm32f4_adc_regspec = {
> +	.dr = STM32F4_ADC_DR,
> +	.ier_eoc = { STM32F4_ADC_CR1, STM32F4_EOCIE },
> +	.isr_eoc = { STM32F4_ADC_SR, STM32F4_EOC },
> +	.sqr = stm32f4_sq,
> +	.exten = { STM32F4_ADC_CR2, STM32F4_EXTEN_MASK, STM32F4_EXTEN_SHIFT },
> +	.extsel = { STM32F4_ADC_CR2, STM32F4_EXTSEL_MASK,
> +		    STM32F4_EXTSEL_SHIFT },
> +	.res = { STM32F4_ADC_CR1, STM32F4_RES_MASK, STM32F4_RES_SHIFT },
> +};
> +
>  /**
>   * STM32 ADC registers access routines
>   * @adc: stm32 adc instance
> @@ -299,7 +375,8 @@ static void stm32_adc_clr_bits(struct stm32_adc *adc, u32 reg, u32 bits)
>   */
>  static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
>  {
> -	stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
> +	stm32_adc_set_bits(adc, adc->cfg->regs->ier_eoc.reg,
> +			   adc->cfg->regs->ier_eoc.mask);
>  };
>  
>  /**
> @@ -308,19 +385,22 @@ static void stm32_adc_conv_irq_enable(struct stm32_adc *adc)
>   */
>  static void stm32_adc_conv_irq_disable(struct stm32_adc *adc)
>  {
> -	stm32_adc_clr_bits(adc, STM32F4_ADC_CR1, STM32F4_EOCIE);
> +	stm32_adc_clr_bits(adc, adc->cfg->regs->ier_eoc.reg,
> +			   adc->cfg->regs->ier_eoc.mask);
>  }
>  
>  static void stm32_adc_set_res(struct stm32_adc *adc)
>  {
> -	u32 val = stm32_adc_readl(adc, STM32F4_ADC_CR1);
> +	const struct stm32_adc_regs *res = &adc->cfg->regs->res;
> +	u32 val;
>  
> -	val = (val & ~STM32F4_RES_MASK) | (adc->res << STM32F4_RES_SHIFT);
> -	stm32_adc_writel(adc, STM32F4_ADC_CR1, val);
> +	val = stm32_adc_readl(adc, res->reg);
> +	val = (val & ~res->mask) | (adc->res << res->shift);
> +	stm32_adc_writel(adc, res->reg, val);
>  }
>  
>  /**
> - * stm32_adc_start_conv() - Start conversions for regular channels.
> + * stm32f4_adc_start_conv() - Start conversions for regular channels.
>   * @adc: stm32 adc instance
>   * @dma: use dma to transfer conversion result
>   *
> @@ -329,7 +409,7 @@ static void stm32_adc_set_res(struct stm32_adc *adc)
>   * conversions, in IIO buffer modes. Otherwise, use ADC interrupt with direct
>   * DR read instead (e.g. read_raw, or triggered buffer mode without DMA).
>   */
> -static void stm32_adc_start_conv(struct stm32_adc *adc, bool dma)
> +static void stm32f4_adc_start_conv(struct stm32_adc *adc, bool dma)
>  {
>  	stm32_adc_set_bits(adc, STM32F4_ADC_CR1, STM32F4_SCAN);
>  
> @@ -347,7 +427,7 @@ static void stm32_adc_start_conv(struct stm32_adc *adc, bool dma)
>  		stm32_adc_set_bits(adc, STM32F4_ADC_CR2, STM32F4_SWSTART);
>  }
>  
> -static void stm32_adc_stop_conv(struct stm32_adc *adc)
> +static void stm32f4_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);
> @@ -371,6 +451,7 @@ 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 *sqr = adc->cfg->regs->sqr;
>  	const struct iio_chan_spec *chan;
>  	u32 val, bit;
>  	int i = 0;
> @@ -388,20 +469,20 @@ static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
>  		dev_dbg(&indio_dev->dev, "%s chan %d to SQ%d\n",
>  			__func__, chan->channel, i);
>  
> -		val = stm32_adc_readl(adc, stm32f4_sq[i].reg);
> -		val &= ~stm32f4_sq[i].mask;
> -		val |= chan->channel << stm32f4_sq[i].shift;
> -		stm32_adc_writel(adc, stm32f4_sq[i].reg, val);
> +		val = stm32_adc_readl(adc, sqr[i].reg);
> +		val &= ~sqr[i].mask;
> +		val |= chan->channel << sqr[i].shift;
> +		stm32_adc_writel(adc, sqr[i].reg, val);
>  	}
>  
>  	if (!i)
>  		return -EINVAL;
>  
>  	/* Sequence len */
> -	val = stm32_adc_readl(adc, stm32f4_sq[0].reg);
> -	val &= ~stm32f4_sq[0].mask;
> -	val |= ((i - 1) << stm32f4_sq[0].shift);
> -	stm32_adc_writel(adc, stm32f4_sq[0].reg, val);
> +	val = stm32_adc_readl(adc, sqr[0].reg);
> +	val &= ~sqr[0].mask;
> +	val |= ((i - 1) << sqr[0].shift);
> +	stm32_adc_writel(adc, sqr[0].reg, val);
>  
>  	return 0;
>  }
> @@ -412,19 +493,21 @@ static int stm32_adc_conf_scan_seq(struct iio_dev *indio_dev,
>   *
>   * Returns trigger extsel value, if trig matches, -EINVAL otherwise.
>   */
> -static int stm32_adc_get_trig_extsel(struct iio_trigger *trig)
> +static int stm32_adc_get_trig_extsel(struct iio_dev *indio_dev,
> +				     struct iio_trigger *trig)
>  {
> +	struct stm32_adc *adc = iio_priv(indio_dev);
>  	int i;
>  
>  	/* lookup triggers registered by stm32 timer trigger driver */
> -	for (i = 0; stm32f4_adc_trigs[i].name; i++) {
> +	for (i = 0; adc->cfg->trigs[i].name; i++) {
>  		/**
>  		 * Checking both stm32 timer trigger type and trig name
>  		 * should be safe against arbitrary trigger names.
>  		 */
>  		if (is_stm32_timer_trigger(trig) &&
> -		    !strcmp(stm32f4_adc_trigs[i].name, trig->name)) {
> -			return stm32f4_adc_trigs[i].extsel;
> +		    !strcmp(adc->cfg->trigs[i].name, trig->name)) {
> +			return adc->cfg->trigs[i].extsel;
>  		}
>  	}
>  
> @@ -449,7 +532,7 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
>  	int ret;
>  
>  	if (trig) {
> -		ret = stm32_adc_get_trig_extsel(trig);
> +		ret = stm32_adc_get_trig_extsel(indio_dev, trig);
>  		if (ret < 0)
>  			return ret;
>  
> @@ -459,11 +542,11 @@ static int stm32_adc_set_trig(struct iio_dev *indio_dev,
>  	}
>  
>  	spin_lock_irqsave(&adc->lock, flags);
> -	val = stm32_adc_readl(adc, STM32F4_ADC_CR2);
> -	val &= ~(STM32F4_EXTEN_MASK | STM32F4_EXTSEL_MASK);
> -	val |= exten << STM32F4_EXTEN_SHIFT;
> -	val |= extsel << STM32F4_EXTSEL_SHIFT;
> -	stm32_adc_writel(adc, STM32F4_ADC_CR2, val);
> +	val = stm32_adc_readl(adc, adc->cfg->regs->exten.reg);
> +	val &= ~(adc->cfg->regs->exten.mask | adc->cfg->regs->extsel.mask);
> +	val |= exten << adc->cfg->regs->exten.shift;
> +	val |= extsel << adc->cfg->regs->extsel.shift;
> +	stm32_adc_writel(adc,  adc->cfg->regs->exten.reg, val);
>  	spin_unlock_irqrestore(&adc->lock, flags);
>  
>  	return 0;
> @@ -515,6 +598,7 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
>  				 int *res)
>  {
>  	struct stm32_adc *adc = iio_priv(indio_dev);
> +	const struct stm32_adc_regspec *regs = adc->cfg->regs;
>  	long timeout;
>  	u32 val;
>  	int ret;
> @@ -524,20 +608,20 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
>  	adc->bufi = 0;
>  
>  	/* Program chan number in regular sequence (SQ1) */
> -	val = stm32_adc_readl(adc, stm32f4_sq[1].reg);
> -	val &= ~stm32f4_sq[1].mask;
> -	val |= chan->channel << stm32f4_sq[1].shift;
> -	stm32_adc_writel(adc, stm32f4_sq[1].reg, val);
> +	val = stm32_adc_readl(adc, regs->sqr[1].reg);
> +	val &= ~regs->sqr[1].mask;
> +	val |= chan->channel << regs->sqr[1].shift;
> +	stm32_adc_writel(adc, regs->sqr[1].reg, val);
>  
>  	/* Set regular sequence len (0 for 1 conversion) */
> -	stm32_adc_clr_bits(adc, stm32f4_sq[0].reg, stm32f4_sq[0].mask);
> +	stm32_adc_clr_bits(adc, regs->sqr[0].reg, regs->sqr[0].mask);
>  
>  	/* Trigger detection disabled (conversion can be launched in SW) */
> -	stm32_adc_clr_bits(adc, STM32F4_ADC_CR2, STM32F4_EXTEN_MASK);
> +	stm32_adc_clr_bits(adc, regs->exten.reg, regs->exten.mask);
>  
>  	stm32_adc_conv_irq_enable(adc);
>  
> -	stm32_adc_start_conv(adc, false);
> +	adc->cfg->start_conv(adc, false);
>  
>  	timeout = wait_for_completion_interruptible_timeout(
>  					&adc->completion, STM32_ADC_TIMEOUT);
> @@ -550,7 +634,7 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
>  		ret = IIO_VAL_INT;
>  	}
>  
> -	stm32_adc_stop_conv(adc);
> +	adc->cfg->stop_conv(adc);
>  
>  	stm32_adc_conv_irq_disable(adc);
>  
> @@ -590,11 +674,12 @@ static irqreturn_t stm32_adc_isr(int irq, void *data)
>  {
>  	struct stm32_adc *adc = data;
>  	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> -	u32 status = stm32_adc_readl(adc, STM32F4_ADC_SR);
> +	const struct stm32_adc_regspec *regs = adc->cfg->regs;
> +	u32 status = stm32_adc_readl(adc, regs->isr_eoc.reg);
>  
> -	if (status & STM32F4_EOC) {
> +	if (status & regs->isr_eoc.mask) {
>  		/* Reading DR also clears EOC status flag */
> -		adc->buffer[adc->bufi] = stm32_adc_readw(adc, STM32F4_ADC_DR);
> +		adc->buffer[adc->bufi] = stm32_adc_readw(adc, regs->dr);
>  		if (iio_buffer_enabled(indio_dev)) {
>  			adc->bufi++;
>  			if (adc->bufi >= adc->num_conv) {
> @@ -621,7 +706,7 @@ static irqreturn_t stm32_adc_isr(int irq, void *data)
>  static int stm32_adc_validate_trigger(struct iio_dev *indio_dev,
>  				      struct iio_trigger *trig)
>  {
> -	return stm32_adc_get_trig_extsel(trig) < 0 ? -EINVAL : 0;
> +	return stm32_adc_get_trig_extsel(indio_dev, trig) < 0 ? -EINVAL : 0;
>  }
>  
>  static int stm32_adc_set_watermark(struct iio_dev *indio_dev, unsigned int val)
> @@ -799,7 +884,7 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
>  	if (!adc->dma_chan)
>  		stm32_adc_conv_irq_enable(adc);
>  
> -	stm32_adc_start_conv(adc, !!adc->dma_chan);
> +	adc->cfg->start_conv(adc, !!adc->dma_chan);
>  
>  	return 0;
>  
> @@ -817,7 +902,7 @@ static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
>  	struct stm32_adc *adc = iio_priv(indio_dev);
>  	int ret;
>  
> -	stm32_adc_stop_conv(adc);
> +	adc->cfg->stop_conv(adc);
>  	if (!adc->dma_chan)
>  		stm32_adc_conv_irq_disable(adc);
>  
> @@ -895,12 +980,12 @@ static int stm32_adc_of_get_resolution(struct iio_dev *indio_dev)
>  	u32 res;
>  
>  	if (of_property_read_u32(node, "assigned-resolution-bits", &res))
> -		res = stm32f4_adc_resolutions[0];
> +		res = adc->cfg->adc_info->resolutions[0];
>  
> -	for (i = 0; i < ARRAY_SIZE(stm32f4_adc_resolutions); i++)
> -		if (res == stm32f4_adc_resolutions[i])
> +	for (i = 0; i < adc->cfg->adc_info->num_res; i++)
> +		if (res == adc->cfg->adc_info->resolutions[i])
>  			break;
> -	if (i >= ARRAY_SIZE(stm32f4_adc_resolutions)) {
> +	if (i >= adc->cfg->adc_info->num_res) {
>  		dev_err(&indio_dev->dev, "Bad resolution: %u bits\n", res);
>  		return -EINVAL;
>  	}
> @@ -926,7 +1011,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
>  	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 = stm32f4_adc_resolutions[adc->res];
> +	chan->scan_type.realbits = adc->cfg->adc_info->resolutions[adc->res];
>  	chan->scan_type.storagebits = 16;
>  	chan->ext_info = stm32_adc_ext_info;
>  }
> @@ -934,6 +1019,8 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
>  static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
>  {
>  	struct device_node *node = indio_dev->dev.of_node;
> +	struct stm32_adc *adc = iio_priv(indio_dev);
> +	const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
>  	struct property *prop;
>  	const __be32 *cur;
>  	struct iio_chan_spec *channels;
> @@ -942,7 +1029,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
>  
>  	num_channels = of_property_count_u32_elems(node, "st,adc-channels");
>  	if (num_channels < 0 ||
> -	    num_channels >= ARRAY_SIZE(stm32f4_adc123_channels)) {
> +	    num_channels >= adc_info->max_channels) {
>  		dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
>  		return num_channels < 0 ? num_channels : -EINVAL;
>  	}
> @@ -953,12 +1040,12 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
>  		return -ENOMEM;
>  
>  	of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
> -		if (val >= ARRAY_SIZE(stm32f4_adc123_channels)) {
> +		if (val >= adc_info->max_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],
> +					&adc_info->channels[val],
>  					scan_index);
>  		scan_index++;
>  	}
> @@ -990,7 +1077,7 @@ static int stm32_adc_dma_request(struct iio_dev *indio_dev)
>  	/* 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 + STM32F4_ADC_DR;
> +	config.src_addr += adc->offset + adc->cfg->regs->dr;
>  	config.src_addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
>  
>  	ret = dmaengine_slave_config(adc->dma_chan, &config);
> @@ -1011,6 +1098,7 @@ static int stm32_adc_dma_request(struct iio_dev *indio_dev)
>  static int stm32_adc_probe(struct platform_device *pdev)
>  {
>  	struct iio_dev *indio_dev;
> +	struct device *dev = &pdev->dev;
>  	struct stm32_adc *adc;
>  	int ret;
>  
> @@ -1025,6 +1113,8 @@ static int stm32_adc_probe(struct platform_device *pdev)
>  	adc->common = dev_get_drvdata(pdev->dev.parent);
>  	spin_lock_init(&adc->lock);
>  	init_completion(&adc->completion);
> +	adc->cfg = (const struct stm32_adc_cfg *)
> +		of_match_device(dev->driver->of_match_table, dev)->data;
>  
>  	indio_dev->name = dev_name(&pdev->dev);
>  	indio_dev->dev.parent = &pdev->dev;
> @@ -1129,8 +1219,16 @@ static int stm32_adc_remove(struct platform_device *pdev)
>  	return 0;
>  }
>  
> +static const struct stm32_adc_cfg stm32f4_adc_cfg = {
> +	.regs = &stm32f4_adc_regspec,
> +	.adc_info = &stm32f4_adc_info,
> +	.trigs = stm32f4_adc_trigs,
> +	.start_conv = stm32f4_adc_start_conv,
> +	.stop_conv = stm32f4_adc_stop_conv,
> +};
> +
>  static const struct of_device_id stm32_adc_of_match[] = {
> -	{ .compatible = "st,stm32f4-adc" },
> +	{ .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg },
>  	{},
>  };
>  MODULE_DEVICE_TABLE(of, stm32_adc_of_match);

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


#1652382 — [PATCH v2 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7

FromFabrice Gasnier <fabrice.gasnier@st.com>
Date2017-05-29 11:40 +0200
Subject[PATCH v2 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7
Message-ID<tMpjB-6jW-33@gated-at.bofh.it>
In reply to#1652380
Document support for STM32H7 Analog to Digital Converter.
Main difference is regarding compatible, clock definitions and new
features like differential channels support:
STM32H7 ADC block has two clock inputs, common clock for all ADCs.
One 'bus' clock for registers access, and one optional 'adc' clock
for analog circuitry (bus clock may be used for conversions).

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
Changes in v2:
- remarks from Rob: one compatible per line, s/unused/not present/
- document resolutions available on stm32h7
---
 .../devicetree/bindings/iio/adc/st,stm32-adc.txt   | 28 ++++++++++++++++------
 1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
index e35f9f1..8310073 100644
--- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
@@ -21,11 +21,19 @@ own configurable sequence and trigger:
 Contents of a stm32 adc root node:
 -----------------------------------
 Required properties:
-- compatible: Should be "st,stm32f4-adc-core".
+- compatible: Should be one of:
+  "st,stm32f4-adc-core"
+  "st,stm32h7-adc-core"
 - reg: Offset and length of the ADC block register set.
 - interrupts: Must contain the interrupt for ADC block.
-- clocks: Clock for the analog circuitry (common to all ADCs).
-- clock-names: Must be "adc".
+- clocks: Core can use up to two clocks, depending on part used:
+  - "adc" clock: for the analog circuitry, common to all ADCs.
+    It's required on stm32f4.
+    It's optional on stm32h7.
+  - "bus" clock: for registers access, common to all ADCs.
+    It's not present on stm32f4.
+    It's required on stm32h7.
+- clock-names: Must be "adc" and/or "bus" depending on part used.
 - interrupt-controller: Identifies the controller node as interrupt-parent
 - vref-supply: Phandle to the vref input analog reference voltage.
 - #interrupt-cells = <1>;
@@ -42,14 +50,18 @@ An ADC block node should contain at least one subnode, representing an
 ADC instance available on the machine.
 
 Required properties:
-- compatible: Should be "st,stm32f4-adc".
+- compatible: Should be one of:
+  "st,stm32f4-adc"
+  "st,stm32h7-adc"
 - reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
-- clocks: Input clock private to this ADC instance.
+- clocks: Input clock private to this ADC instance. It's required only on
+  stm32f4, that has per instance clock input for registers access.
 - interrupt-parent: Phandle to the parent interrupt controller.
 - interrupts: IRQ Line for the ADC (e.g. may be 0 for adc@0, 1 for adc@100 or
   2 for adc@200).
 - st,adc-channels: List of single-ended channels muxed for this ADC.
-  It can have up to 16 channels, numbered from 0 to 15 (resp. for in0..in15).
+  It can have up to 16 channels on stm32f4 or 20 channels on stm32h7, numbered
+  from 0 to 15 or 19 (resp. for in0..in15 or in0..in19).
 - #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
   Documentation/devicetree/bindings/iio/iio-bindings.txt
 
@@ -58,7 +70,9 @@ Optional properties:
   See ../../dma/dma.txt for details.
 - dma-names: Must be "rx" when dmas property is being used.
 - assigned-resolution-bits: Resolution (bits) to use for conversions. Must
-  match device available resolutions (e.g. can be 6, 8, 10 or 12 on stm32f4).
+  match device available resolutions:
+  * can be 6, 8, 10 or 12 on stm32f4
+  * can be 8, 10, 12, 14 or 16 on stm32h7
   Default is maximum resolution if unset.
 
 Example:
-- 
1.9.1

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


#1656765 — Re: [PATCH v2 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7

FromJonathan Cameron <jic23@kernel.org>
Date2017-06-03 11:40 +0200
SubjectRe: [PATCH v2 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7
Message-ID<tOdHj-5hr-1@gated-at.bofh.it>
In reply to#1652382
On Mon, 29 May 2017 11:28:16 +0200
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:

> Document support for STM32H7 Analog to Digital Converter.
> Main difference is regarding compatible, clock definitions and new
> features like differential channels support:
> STM32H7 ADC block has two clock inputs, common clock for all ADCs.
> One 'bus' clock for registers access, and one optional 'adc' clock
> for analog circuitry (bus clock may be used for conversions).
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Given the nature of Robs requested changes I think we are fairly safe
that he will be happy with this.  As it's going to be stuck exposed
only in my testing branch for at least a few days (which I will happily
rebase) there is still time for Rob to take a final look.

Applied to the togreg branch of iio.git and pushed out as testing
for the autobuilders to play with it.

Thanks,

Jonathan
> ---
> Changes in v2:
> - remarks from Rob: one compatible per line, s/unused/not present/
> - document resolutions available on stm32h7
> ---
>  .../devicetree/bindings/iio/adc/st,stm32-adc.txt   | 28 ++++++++++++++++------
>  1 file changed, 21 insertions(+), 7 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
> index e35f9f1..8310073 100644
> --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
> @@ -21,11 +21,19 @@ own configurable sequence and trigger:
>  Contents of a stm32 adc root node:
>  -----------------------------------
>  Required properties:
> -- compatible: Should be "st,stm32f4-adc-core".
> +- compatible: Should be one of:
> +  "st,stm32f4-adc-core"
> +  "st,stm32h7-adc-core"
>  - reg: Offset and length of the ADC block register set.
>  - interrupts: Must contain the interrupt for ADC block.
> -- clocks: Clock for the analog circuitry (common to all ADCs).
> -- clock-names: Must be "adc".
> +- clocks: Core can use up to two clocks, depending on part used:
> +  - "adc" clock: for the analog circuitry, common to all ADCs.
> +    It's required on stm32f4.
> +    It's optional on stm32h7.
> +  - "bus" clock: for registers access, common to all ADCs.
> +    It's not present on stm32f4.
> +    It's required on stm32h7.
> +- clock-names: Must be "adc" and/or "bus" depending on part used.
>  - interrupt-controller: Identifies the controller node as interrupt-parent
>  - vref-supply: Phandle to the vref input analog reference voltage.
>  - #interrupt-cells = <1>;
> @@ -42,14 +50,18 @@ An ADC block node should contain at least one subnode, representing an
>  ADC instance available on the machine.
>  
>  Required properties:
> -- compatible: Should be "st,stm32f4-adc".
> +- compatible: Should be one of:
> +  "st,stm32f4-adc"
> +  "st,stm32h7-adc"
>  - reg: Offset of ADC instance in ADC block (e.g. may be 0x0, 0x100, 0x200).
> -- clocks: Input clock private to this ADC instance.
> +- clocks: Input clock private to this ADC instance. It's required only on
> +  stm32f4, that has per instance clock input for registers access.
>  - interrupt-parent: Phandle to the parent interrupt controller.
>  - interrupts: IRQ Line for the ADC (e.g. may be 0 for adc@0, 1 for adc@100 or
>    2 for adc@200).
>  - st,adc-channels: List of single-ended channels muxed for this ADC.
> -  It can have up to 16 channels, numbered from 0 to 15 (resp. for in0..in15).
> +  It can have up to 16 channels on stm32f4 or 20 channels on stm32h7, numbered
> +  from 0 to 15 or 19 (resp. for in0..in15 or in0..in19).
>  - #io-channel-cells = <1>: See the IIO bindings section "IIO consumers" in
>    Documentation/devicetree/bindings/iio/iio-bindings.txt
>  
> @@ -58,7 +70,9 @@ Optional properties:
>    See ../../dma/dma.txt for details.
>  - dma-names: Must be "rx" when dmas property is being used.
>  - assigned-resolution-bits: Resolution (bits) to use for conversions. Must
> -  match device available resolutions (e.g. can be 6, 8, 10 or 12 on stm32f4).
> +  match device available resolutions:
> +  * can be 6, 8, 10 or 12 on stm32f4
> +  * can be 8, 10, 12, 14 or 16 on stm32h7
>    Default is maximum resolution if unset.
>  
>  Example:

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


#1660175 — Re: [PATCH v2 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7

FromRob Herring <robh@kernel.org>
Date2017-06-07 22:10 +0200
SubjectRe: [PATCH v2 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7
Message-ID<tPPrd-2PB-55@gated-at.bofh.it>
In reply to#1652382
On Mon, May 29, 2017 at 11:28:16AM +0200, Fabrice Gasnier wrote:
> Document support for STM32H7 Analog to Digital Converter.
> Main difference is regarding compatible, clock definitions and new
> features like differential channels support:
> STM32H7 ADC block has two clock inputs, common clock for all ADCs.
> One 'bus' clock for registers access, and one optional 'adc' clock
> for analog circuitry (bus clock may be used for conversions).
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> ---
> Changes in v2:
> - remarks from Rob: one compatible per line, s/unused/not present/
> - document resolutions available on stm32h7
> ---
>  .../devicetree/bindings/iio/adc/st,stm32-adc.txt   | 28 ++++++++++++++++------
>  1 file changed, 21 insertions(+), 7 deletions(-)

Acked-by: Rob Herring <robh@kernel.org>

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web