linux-arm-kernel.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/5] iio: add support for STM32H7 ADC
@ 2017-05-19 12:45 Fabrice Gasnier
  2017-05-19 12:45 ` [PATCH 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7 Fabrice Gasnier
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Fabrice Gasnier @ 2017-05-19 12:45 UTC (permalink / raw)
  To: linux-arm-kernel

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.

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   |  20 +-
 drivers/iio/adc/stm32-adc-core.c                   | 269 ++++++-
 drivers/iio/adc/stm32-adc-core.h                   |   2 +
 drivers/iio/adc/stm32-adc.c                        | 775 +++++++++++++++++++--
 4 files changed, 980 insertions(+), 86 deletions(-)

-- 
1.9.1

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7
  2017-05-19 12:45 [PATCH 0/5] iio: add support for STM32H7 ADC Fabrice Gasnier
@ 2017-05-19 12:45 ` Fabrice Gasnier
  2017-05-23 15:07   ` Rob Herring
  2017-05-19 12:45 ` [PATCH 2/5] iio: adc: stm32: make core adc clock optional by default Fabrice Gasnier
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Fabrice Gasnier @ 2017-05-19 12:45 UTC (permalink / raw)
  To: linux-arm-kernel

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>
---
 .../devicetree/bindings/iio/adc/st,stm32-adc.txt     | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 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..9519d2e 100644
--- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
@@ -21,11 +21,17 @@ 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" or "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 will be used by default if not set.
+  - "bus" clock: for registers access, common to all ADCs.
+    It's unused 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 +48,16 @@ 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" or "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 at 0, 1 for adc at 100 or
   2 for adc at 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
 
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/5] iio: adc: stm32: make core adc clock optional by default
  2017-05-19 12:45 [PATCH 0/5] iio: add support for STM32H7 ADC Fabrice Gasnier
  2017-05-19 12:45 ` [PATCH 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7 Fabrice Gasnier
@ 2017-05-19 12:45 ` Fabrice Gasnier
  2017-05-19 12:45 ` [PATCH 3/5] iio: adc: stm32: introduce compatible data cfg Fabrice Gasnier
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Fabrice Gasnier @ 2017-05-19 12:45 UTC (permalink / raw)
  To: linux-arm-kernel

Analog clock input is mandatory on stm32f4. But newer version of
ADC hardware block allow to select either bus clock or asynchronous
clock, for analog circuitry.

So, make it optional by default, but enforce clk presence on stm32f4.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 drivers/iio/adc/stm32-adc-core.c | 38 ++++++++++++++++++++++++++------------
 1 file changed, 26 insertions(+), 12 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index 22b7c93..597ab7a 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -85,13 +85,21 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
 	u32 val;
 	int i;
 
+	/* stm32f4 has one clk input for analog (mandatory), enforce it here */
+	if (!priv->aclk) {
+		dev_err(&pdev->dev, "No 'adc' clock found\n");
+		return -ENOENT;
+	}
+
 	rate = clk_get_rate(priv->aclk);
 	for (i = 0; i < ARRAY_SIZE(stm32f4_pclk_div); i++) {
 		if ((rate / stm32f4_pclk_div[i]) <= STM32F4_ADC_MAX_CLK_RATE)
 			break;
 	}
-	if (i >= ARRAY_SIZE(stm32f4_pclk_div))
+	if (i >= ARRAY_SIZE(stm32f4_pclk_div)) {
+		dev_err(&pdev->dev, "adc clk selection failed\n");
 		return -EINVAL;
+	}
 
 	val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
 	val &= ~STM32F4_ADC_ADCPRE_MASK;
@@ -227,21 +235,25 @@ static int stm32_adc_probe(struct platform_device *pdev)
 	priv->aclk = devm_clk_get(&pdev->dev, "adc");
 	if (IS_ERR(priv->aclk)) {
 		ret = PTR_ERR(priv->aclk);
-		dev_err(&pdev->dev, "Can't get 'adc' clock\n");
-		goto err_regulator_disable;
+		if (ret == -ENOENT) {
+			priv->aclk = NULL;
+		} else {
+			dev_err(&pdev->dev, "Can't get 'adc' clock\n");
+			goto err_regulator_disable;
+		}
 	}
 
-	ret = clk_prepare_enable(priv->aclk);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "adc clk enable failed\n");
-		goto err_regulator_disable;
+	if (priv->aclk) {
+		ret = clk_prepare_enable(priv->aclk);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "adc clk enable failed\n");
+			goto err_regulator_disable;
+		}
 	}
 
 	ret = stm32f4_adc_clk_sel(pdev, priv);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "adc clk selection failed\n");
+	if (ret < 0)
 		goto err_clk_disable;
-	}
 
 	ret = stm32_adc_irq_probe(pdev, priv);
 	if (ret < 0)
@@ -261,7 +273,8 @@ static int stm32_adc_probe(struct platform_device *pdev)
 	stm32_adc_irq_remove(pdev, priv);
 
 err_clk_disable:
-	clk_disable_unprepare(priv->aclk);
+	if (priv->aclk)
+		clk_disable_unprepare(priv->aclk);
 
 err_regulator_disable:
 	regulator_disable(priv->vref);
@@ -276,7 +289,8 @@ static int stm32_adc_remove(struct platform_device *pdev)
 
 	of_platform_depopulate(&pdev->dev);
 	stm32_adc_irq_remove(pdev, priv);
-	clk_disable_unprepare(priv->aclk);
+	if (priv->aclk)
+		clk_disable_unprepare(priv->aclk);
 	regulator_disable(priv->vref);
 
 	return 0;
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 3/5] iio: adc: stm32: introduce compatible data cfg
  2017-05-19 12:45 [PATCH 0/5] iio: add support for STM32H7 ADC Fabrice Gasnier
  2017-05-19 12:45 ` [PATCH 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7 Fabrice Gasnier
  2017-05-19 12:45 ` [PATCH 2/5] iio: adc: stm32: make core adc clock optional by default Fabrice Gasnier
@ 2017-05-19 12:45 ` Fabrice Gasnier
  2017-05-19 12:45 ` [PATCH 4/5] iio: adc: stm32: make per instance bus clock optional Fabrice Gasnier
  2017-05-19 12:45 ` [PATCH 5/5] iio: adc: stm32: add support for STM32H7 Fabrice Gasnier
  4 siblings, 0 replies; 9+ messages in thread
From: Fabrice Gasnier @ 2017-05-19 12:45 UTC (permalink / raw)
  To: linux-arm-kernel

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>
---
 drivers/iio/adc/stm32-adc-core.c |  62 +++++++++++--
 drivers/iio/adc/stm32-adc.c      | 195 +++++++++++++++++++++++++++++----------
 2 files changed, 200 insertions(+), 57 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..065189a 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,6 +221,20 @@ struct stm32_adc_chan_spec {
 	const char		*name;
 };
 
+/**
+ * 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 STM32F4 instances */
 static const struct stm32_adc_chan_spec stm32f4_adc123_channels[] = {
 	{ IIO_VOLTAGE, 0, "in0" },
@@ -205,6 +260,13 @@ struct stm32_adc_chan_spec {
 	12, 10, 8, 6,
 };
 
+static const struct stm32_adc_info stm32f4_adc_info = {
+	.channels = stm32f4_adc123_channels,
+	.max_channels = ARRAY_SIZE(stm32f4_adc123_channels),
+	.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 +314,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 +372,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 +382,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 +406,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 +424,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 +448,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 +466,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 +490,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 +529,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 +539,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 +595,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 +605,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 +631,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 +671,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 +703,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 +881,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 +899,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 +977,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 +1008,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 +1016,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 +1026,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 +1037,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 +1074,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 +1095,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 +1110,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 +1216,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

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 4/5] iio: adc: stm32: make per instance bus clock optional
  2017-05-19 12:45 [PATCH 0/5] iio: add support for STM32H7 ADC Fabrice Gasnier
                   ` (2 preceding siblings ...)
  2017-05-19 12:45 ` [PATCH 3/5] iio: adc: stm32: introduce compatible data cfg Fabrice Gasnier
@ 2017-05-19 12:45 ` Fabrice Gasnier
  2017-05-19 12:45 ` [PATCH 5/5] iio: adc: stm32: add support for STM32H7 Fabrice Gasnier
  4 siblings, 0 replies; 9+ messages in thread
From: Fabrice Gasnier @ 2017-05-19 12:45 UTC (permalink / raw)
  To: linux-arm-kernel

STM32F4 requires one clock per ADC instance for register access. But,
newer version of ADC hardware block have common bus clock for all
instances (per instance driver isn't responsible for getting it).
So, make it optional by default. Still, enforce it's required on STM32F4.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 drivers/iio/adc/stm32-adc.c | 28 ++++++++++++++++++++--------
 1 file changed, 20 insertions(+), 8 deletions(-)

diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 065189a..ad8fdb9 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -160,6 +160,7 @@ struct stm32_adc_regspec {
  * @regs:		registers descriptions
  * @adc_info:		per instance input channels definitions
  * @trigs:		external trigger sources
+ * @clk_required:	clock is required
  * @start_conv:		routine to start conversions
  * @stop_conv:		routine to stop conversions
  */
@@ -167,6 +168,7 @@ struct stm32_adc_cfg {
 	const struct stm32_adc_regspec	*regs;
 	const struct stm32_adc_info	*adc_info;
 	struct stm32_adc_trig_info	*trigs;
+	bool clk_required;
 	void (*start_conv)(struct stm32_adc *, bool dma);
 	void (*stop_conv)(struct stm32_adc *);
 };
@@ -1142,14 +1144,21 @@ static int stm32_adc_probe(struct platform_device *pdev)
 
 	adc->clk = devm_clk_get(&pdev->dev, NULL);
 	if (IS_ERR(adc->clk)) {
-		dev_err(&pdev->dev, "Can't get clock\n");
-		return PTR_ERR(adc->clk);
+		ret = PTR_ERR(adc->clk);
+		if (ret == -ENOENT && !adc->cfg->clk_required) {
+			adc->clk = NULL;
+		} else {
+			dev_err(&pdev->dev, "Can't get clock\n");
+			return ret;
+		}
 	}
 
-	ret = clk_prepare_enable(adc->clk);
-	if (ret < 0) {
-		dev_err(&pdev->dev, "clk enable failed\n");
-		return ret;
+	if (adc->clk) {
+		ret = clk_prepare_enable(adc->clk);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "clk enable failed\n");
+			return ret;
+		}
 	}
 
 	ret = stm32_adc_of_get_resolution(indio_dev);
@@ -1193,7 +1202,8 @@ static int stm32_adc_probe(struct platform_device *pdev)
 		dma_release_channel(adc->dma_chan);
 	}
 err_clk_disable:
-	clk_disable_unprepare(adc->clk);
+	if (adc->clk)
+		clk_disable_unprepare(adc->clk);
 
 	return ret;
 }
@@ -1211,7 +1221,8 @@ static int stm32_adc_remove(struct platform_device *pdev)
 				  adc->rx_buf, adc->rx_dma_buf);
 		dma_release_channel(adc->dma_chan);
 	}
-	clk_disable_unprepare(adc->clk);
+	if (adc->clk)
+		clk_disable_unprepare(adc->clk);
 
 	return 0;
 }
@@ -1220,6 +1231,7 @@ static int stm32_adc_remove(struct platform_device *pdev)
 	.regs = &stm32f4_adc_regspec,
 	.adc_info = &stm32f4_adc_info,
 	.trigs = stm32f4_adc_trigs,
+	.clk_required = true,
 	.start_conv = stm32f4_adc_start_conv,
 	.stop_conv = stm32f4_adc_stop_conv,
 };
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 5/5] iio: adc: stm32: add support for STM32H7
  2017-05-19 12:45 [PATCH 0/5] iio: add support for STM32H7 ADC Fabrice Gasnier
                   ` (3 preceding siblings ...)
  2017-05-19 12:45 ` [PATCH 4/5] iio: adc: stm32: make per instance bus clock optional Fabrice Gasnier
@ 2017-05-19 12:45 ` Fabrice Gasnier
  2017-05-20 17:42   ` Jonathan Cameron
  4 siblings, 1 reply; 9+ messages in thread
From: Fabrice Gasnier @ 2017-05-19 12:45 UTC (permalink / raw)
  To: linux-arm-kernel

Add support for STM32H7 Analog to Digital Converter. It has up
to 20 external channels, resolution ranges from 8 to 16bits.
Either bus or asynchronous adc clock may be used.

Add registers & bitfields definition. Also add new configuration
options to enter/exit powerdown and perform self-calibration.

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

diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index c5d292c..e09233b 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -49,6 +49,23 @@
 /* STM32 F4 maximum analog clock rate (from datasheet) */
 #define STM32F4_ADC_MAX_CLK_RATE	36000000
 
+/* STM32H7 - common registers for all ADC instances */
+#define STM32H7_ADC_CSR			(STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32H7_ADC_CCR			(STM32_ADCX_COMN_OFFSET + 0x08)
+
+/* STM32H7_ADC_CSR - bit fields */
+#define STM32H7_EOC_SLV			BIT(18)
+#define STM32H7_EOC_MST			BIT(2)
+
+/* STM32H7_ADC_CCR - bit fields */
+#define STM32H7_PRESC_SHIFT		18
+#define STM32H7_PRESC_MASK		GENMASK(21, 18)
+#define STM32H7_CKMODE_SHIFT		16
+#define STM32H7_CKMODE_MASK		GENMASK(17, 16)
+
+/* STM32 H7 maximum analog clock rate (from datasheet) */
+#define STM32H7_ADC_MAX_CLK_RATE	72000000
+
 /**
  * stm32_adc_common_regs - stm32 common registers, compatible dependent data
  * @csr:	common status register offset
@@ -80,6 +97,7 @@ struct stm32_adc_priv_cfg {
  * @irq:		irq for ADC block
  * @domain:		irq domain reference
  * @aclk:		clock reference for the analog circuitry
+ * @bclk:		bus clock common for all ADCs, depends on part used
  * @vref:		regulator reference
  * @cfg:		compatible configuration data
  * @common:		common data for all ADC instances
@@ -88,6 +106,7 @@ struct stm32_adc_priv {
 	int				irq;
 	struct irq_domain		*domain;
 	struct clk			*aclk;
+	struct clk			*bclk;
 	struct regulator		*vref;
 	const struct stm32_adc_priv_cfg	*cfg;
 	struct stm32_adc_common		common;
@@ -129,6 +148,7 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
 		return -EINVAL;
 	}
 
+	priv->common.rate = rate;
 	val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
 	val &= ~STM32F4_ADC_ADCPRE_MASK;
 	val |= i << STM32F4_ADC_ADCPRE_SHIFT;
@@ -140,6 +160,111 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
 	return 0;
 }
 
+/**
+ * struct stm32h7_adc_ck_spec - specification for stm32h7 adc clock
+ * @ckmode: ADC clock mode, Async or sync with prescaler.
+ * @presc: prescaler bitfield for async clock mode
+ * @div: prescaler division ratio
+ */
+struct stm32h7_adc_ck_spec {
+	u32 ckmode;
+	u32 presc;
+	int div;
+};
+
+const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = {
+	/* 00: CK_ADC[1..3]: Asynchronous clock modes */
+	{ 0, 0, 1 },
+	{ 0, 1, 2 },
+	{ 0, 2, 4 },
+	{ 0, 3, 6 },
+	{ 0, 4, 8 },
+	{ 0, 5, 10 },
+	{ 0, 6, 12 },
+	{ 0, 7, 16 },
+	{ 0, 8, 32 },
+	{ 0, 9, 64 },
+	{ 0, 10, 128 },
+	{ 0, 11, 256 },
+	/* HCLK used: Synchronous clock modes (1, 2 or 4 prescaler) */
+	{ 1, 0, 1 },
+	{ 2, 0, 2 },
+	{ 3, 0, 4 },
+};
+
+static int stm32h7_adc_clk_sel(struct platform_device *pdev,
+			       struct stm32_adc_priv *priv)
+{
+	u32 ckmode, presc, val;
+	unsigned long rate;
+	int i, div;
+
+	/* stm32h7 bus clock is common for all ADC instances (mandatory) */
+	if (!priv->bclk) {
+		dev_err(&pdev->dev, "No 'bus' clock found\n");
+		return -ENOENT;
+	}
+
+	/*
+	 * stm32h7 can use either 'bus' or 'adc' clock for analog circuitry.
+	 * So, choice is to have bus clock mandatory and adc clock optional.
+	 * If optional 'adc' clock has been found, then try to use it first.
+	 */
+	if (priv->aclk) {
+		/*
+		 * Asynchronous clock modes (e.g. ckmode == 0)
+		 * From spec: PLL output musn't exceed max rate
+		 */
+		rate = clk_get_rate(priv->aclk);
+
+		for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
+			ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
+			presc = stm32h7_adc_ckmodes_spec[i].presc;
+			div = stm32h7_adc_ckmodes_spec[i].div;
+
+			if (ckmode)
+				continue;
+
+			if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
+				goto out;
+		}
+	}
+
+	/* Synchronous clock modes (e.g. ckmode is 1, 2 or 3) */
+	rate = clk_get_rate(priv->bclk);
+
+	for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
+		ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
+		presc = stm32h7_adc_ckmodes_spec[i].presc;
+		div = stm32h7_adc_ckmodes_spec[i].div;
+
+		if (!ckmode)
+			continue;
+
+		if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
+			goto out;
+	}
+
+	dev_err(&pdev->dev, "adc clk selection failed\n");
+	return -EINVAL;
+
+out:
+	/* rate used later by each ADC instance to control BOOST mode */
+	priv->common.rate = rate;
+
+	/* Set common clock mode and prescaler */
+	val = readl_relaxed(priv->common.base + STM32H7_ADC_CCR);
+	val &= ~(STM32H7_CKMODE_MASK | STM32H7_PRESC_MASK);
+	val |= ckmode << STM32H7_CKMODE_SHIFT;
+	val |= presc << STM32H7_PRESC_SHIFT;
+	writel_relaxed(val, priv->common.base + STM32H7_ADC_CCR);
+
+	dev_dbg(&pdev->dev, "Using %s clock/%d source at %ld kHz\n",
+		ckmode ? "bus" : "adc", div, rate / (div * 1000));
+
+	return 0;
+}
+
 /* STM32F4 common registers definitions */
 static const struct stm32_adc_common_regs stm32f4_adc_common_regs = {
 	.csr = STM32F4_ADC_CSR,
@@ -148,6 +273,13 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
 	.eoc3_msk = STM32F4_EOC3,
 };
 
+/* STM32H7 common registers definitions */
+static const struct stm32_adc_common_regs stm32h7_adc_common_regs = {
+	.csr = STM32H7_ADC_CSR,
+	.eoc1_msk = STM32H7_EOC_MST,
+	.eoc2_msk = STM32H7_EOC_SLV,
+};
+
 /* ADC common interrupt for all instances */
 static void stm32_adc_irq_handler(struct irq_desc *desc)
 {
@@ -291,13 +423,32 @@ static int stm32_adc_probe(struct platform_device *pdev)
 		}
 	}
 
+	priv->bclk = devm_clk_get(&pdev->dev, "bus");
+	if (IS_ERR(priv->bclk)) {
+		ret = PTR_ERR(priv->bclk);
+		if (ret == -ENOENT) {
+			priv->bclk = NULL;
+		} else {
+			dev_err(&pdev->dev, "Can't get 'bus' clock\n");
+			goto err_aclk_disable;
+		}
+	}
+
+	if (priv->bclk) {
+		ret = clk_prepare_enable(priv->bclk);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "adc clk enable failed\n");
+			goto err_aclk_disable;
+		}
+	}
+
 	ret = priv->cfg->clk_sel(pdev, priv);
 	if (ret < 0)
-		goto err_clk_disable;
+		goto err_bclk_disable;
 
 	ret = stm32_adc_irq_probe(pdev, priv);
 	if (ret < 0)
-		goto err_clk_disable;
+		goto err_bclk_disable;
 
 	platform_set_drvdata(pdev, &priv->common);
 
@@ -312,7 +463,11 @@ static int stm32_adc_probe(struct platform_device *pdev)
 err_irq_remove:
 	stm32_adc_irq_remove(pdev, priv);
 
-err_clk_disable:
+err_bclk_disable:
+	if (priv->bclk)
+		clk_disable_unprepare(priv->bclk);
+
+err_aclk_disable:
 	if (priv->aclk)
 		clk_disable_unprepare(priv->aclk);
 
@@ -329,6 +484,8 @@ static int stm32_adc_remove(struct platform_device *pdev)
 
 	of_platform_depopulate(&pdev->dev);
 	stm32_adc_irq_remove(pdev, priv);
+	if (priv->bclk)
+		clk_disable_unprepare(priv->bclk);
 	if (priv->aclk)
 		clk_disable_unprepare(priv->aclk);
 	regulator_disable(priv->vref);
@@ -341,11 +498,19 @@ static int stm32_adc_remove(struct platform_device *pdev)
 	.clk_sel = stm32f4_adc_clk_sel,
 };
 
+static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
+	.regs = &stm32h7_adc_common_regs,
+	.clk_sel = stm32h7_adc_clk_sel,
+};
+
 static const struct of_device_id stm32_adc_of_match[] = {
 	{
 		.compatible = "st,stm32f4-adc-core",
 		.data = (void *)&stm32f4_adc_priv_cfg
 	}, {
+		.compatible = "st,stm32h7-adc-core",
+		.data = (void *)&stm32h7_adc_priv_cfg
+	}, {
 	},
 };
 MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
index 2ec7abb..250ee95 100644
--- a/drivers/iio/adc/stm32-adc-core.h
+++ b/drivers/iio/adc/stm32-adc-core.h
@@ -43,11 +43,13 @@
  * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
  * @base:		control registers base cpu addr
  * @phys_base:		control registers base physical addr
+ * @rate:		clock rate used for analog circuitry
  * @vref_mv:		vref voltage (mv)
  */
 struct stm32_adc_common {
 	void __iomem			*base;
 	phys_addr_t			phys_base;
+	unsigned long			rate;
 	int				vref_mv;
 };
 
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index ad8fdb9..dba89c7 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -31,6 +31,7 @@
 #include <linux/iio/triggered_buffer.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/of.h>
@@ -77,6 +78,78 @@
 #define STM32F4_DMA			BIT(8)
 #define STM32F4_ADON			BIT(0)
 
+/* STM32H7 - Registers for each ADC instance */
+#define STM32H7_ADC_ISR			0x00
+#define STM32H7_ADC_IER			0x04
+#define STM32H7_ADC_CR			0x08
+#define STM32H7_ADC_CFGR		0x0C
+#define STM32H7_ADC_PCSEL		0x1C
+#define STM32H7_ADC_SQR1		0x30
+#define STM32H7_ADC_SQR2		0x34
+#define STM32H7_ADC_SQR3		0x38
+#define STM32H7_ADC_SQR4		0x3C
+#define STM32H7_ADC_DR			0x40
+#define STM32H7_ADC_CALFACT		0xC4
+#define STM32H7_ADC_CALFACT2		0xC8
+
+/* STM32H7_ADC_ISR - bit fields */
+#define STM32H7_EOC			BIT(2)
+#define STM32H7_ADRDY			BIT(0)
+
+/* STM32H7_ADC_IER - bit fields */
+#define STM32H7_EOCIE			STM32H7_EOC
+
+/* STM32H7_ADC_CR - bit fields */
+#define STM32H7_ADCAL			BIT(31)
+#define STM32H7_ADCALDIF		BIT(30)
+#define STM32H7_DEEPPWD			BIT(29)
+#define STM32H7_ADVREGEN		BIT(28)
+#define STM32H7_LINCALRDYW6		BIT(27)
+#define STM32H7_LINCALRDYW5		BIT(26)
+#define STM32H7_LINCALRDYW4		BIT(25)
+#define STM32H7_LINCALRDYW3		BIT(24)
+#define STM32H7_LINCALRDYW2		BIT(23)
+#define STM32H7_LINCALRDYW1		BIT(22)
+#define STM32H7_ADCALLIN		BIT(16)
+#define STM32H7_BOOST			BIT(8)
+#define STM32H7_ADSTP			BIT(4)
+#define STM32H7_ADSTART			BIT(2)
+#define STM32H7_ADDIS			BIT(1)
+#define STM32H7_ADEN			BIT(0)
+
+/* STM32H7_ADC_CFGR bit fields */
+#define STM32H7_EXTEN_SHIFT		10
+#define STM32H7_EXTEN_MASK		GENMASK(11, 10)
+#define STM32H7_EXTSEL_SHIFT		5
+#define STM32H7_EXTSEL_MASK		GENMASK(9, 5)
+#define STM32H7_RES_SHIFT		2
+#define STM32H7_RES_MASK		GENMASK(4, 2)
+#define STM32H7_DMNGT_SHIFT		0
+#define STM32H7_DMNGT_MASK		GENMASK(1, 0)
+
+enum stm32h7_adc_dmngt {
+	STM32H7_DMNGT_DR_ONLY,		/* Regular data in DR only */
+	STM32H7_DMNGT_DMA_ONESHOT,	/* DMA one shot mode */
+	STM32H7_DMNGT_DFSDM,		/* DFSDM mode */
+	STM32H7_DMNGT_DMA_CIRC,		/* DMA circular mode */
+};
+
+/* STM32H7_ADC_CALFACT - bit fields */
+#define STM32H7_CALFACT_D_SHIFT		16
+#define STM32H7_CALFACT_D_MASK		GENMASK(26, 16)
+#define STM32H7_CALFACT_S_SHIFT		0
+#define STM32H7_CALFACT_S_MASK		GENMASK(10, 0)
+
+/* STM32H7_ADC_CALFACT2 - bit fields */
+#define STM32H7_LINCALFACT_SHIFT	0
+#define STM32H7_LINCALFACT_MASK		GENMASK(29, 0)
+
+/* Number of linear calibration shadow registers / LINCALRDYW control bits */
+#define STM32H7_LINCALFACT_NUM		6
+
+/* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */
+#define STM32H7_BOOST_CLKRATE		20000000UL
+
 #define STM32_ADC_MAX_SQ		16	/* SQ1..SQ16 */
 #define STM32_ADC_TIMEOUT_US		100000
 #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
@@ -122,6 +195,18 @@ struct stm32_adc_trig_info {
 };
 
 /**
+ * struct stm32_adc_calib - optional adc calibration data
+ * @calfact_s: Calibration offset for single ended channels
+ * @calfact_d: Calibration offset in differential
+ * @lincalfact: Linearity calibration factor
+ */
+struct stm32_adc_calib {
+	u32			calfact_s;
+	u32			calfact_d;
+	u32			lincalfact[STM32H7_LINCALFACT_NUM];
+};
+
+/**
  * stm32_adc_regs - stm32 ADC misc registers & bitfield desc
  * @reg:		register offset
  * @mask:		bitfield mask
@@ -161,16 +246,22 @@ struct stm32_adc_regspec {
  * @adc_info:		per instance input channels definitions
  * @trigs:		external trigger sources
  * @clk_required:	clock is required
+ * @selfcalib:		optional routine for self-calibration
+ * @prepare:		optional prepare routine (power-up, enable)
  * @start_conv:		routine to start conversions
  * @stop_conv:		routine to stop conversions
+ * @unprepare:		optional unprepare routine (disable, power-down)
  */
 struct stm32_adc_cfg {
 	const struct stm32_adc_regspec	*regs;
 	const struct stm32_adc_info	*adc_info;
 	struct stm32_adc_trig_info	*trigs;
 	bool clk_required;
+	int (*selfcalib)(struct stm32_adc *);
+	int (*prepare)(struct stm32_adc *);
 	void (*start_conv)(struct stm32_adc *, bool dma);
 	void (*stop_conv)(struct stm32_adc *);
+	void (*unprepare)(struct stm32_adc *);
 };
 
 /**
@@ -191,6 +282,8 @@ struct stm32_adc_cfg {
  * @rx_buf:		dma rx buffer cpu address
  * @rx_dma_buf:		dma rx buffer bus address
  * @rx_buf_sz:		dma rx buffer size
+ * @pcsel		bitmask to preselect channels on some devices
+ * @cal:		optional calibration data on some devices
  */
 struct stm32_adc {
 	struct stm32_adc_common	*common;
@@ -209,6 +302,8 @@ struct stm32_adc {
 	u8			*rx_buf;
 	dma_addr_t		rx_dma_buf;
 	unsigned int		rx_buf_sz;
+	u32			pcsel;
+	struct stm32_adc_calib	cal;
 };
 
 /**
@@ -269,6 +364,42 @@ struct stm32_adc_info {
 	.num_res = ARRAY_SIZE(stm32f4_adc_resolutions),
 };
 
+/* Input definitions for stm32h7 */
+static const struct stm32_adc_chan_spec stm32h7_adc_channels[] = {
+	{ IIO_VOLTAGE, 0, "in0" },
+	{ IIO_VOLTAGE, 1, "in1" },
+	{ IIO_VOLTAGE, 2, "in2" },
+	{ IIO_VOLTAGE, 3, "in3" },
+	{ IIO_VOLTAGE, 4, "in4" },
+	{ IIO_VOLTAGE, 5, "in5" },
+	{ IIO_VOLTAGE, 6, "in6" },
+	{ IIO_VOLTAGE, 7, "in7" },
+	{ IIO_VOLTAGE, 8, "in8" },
+	{ IIO_VOLTAGE, 9, "in9" },
+	{ IIO_VOLTAGE, 10, "in10" },
+	{ IIO_VOLTAGE, 11, "in11" },
+	{ IIO_VOLTAGE, 12, "in12" },
+	{ IIO_VOLTAGE, 13, "in13" },
+	{ IIO_VOLTAGE, 14, "in14" },
+	{ IIO_VOLTAGE, 15, "in15" },
+	{ IIO_VOLTAGE, 16, "in16" },
+	{ IIO_VOLTAGE, 17, "in17" },
+	{ IIO_VOLTAGE, 18, "in18" },
+	{ IIO_VOLTAGE, 19, "in19" },
+};
+
+static const unsigned int stm32h7_adc_resolutions[] = {
+	/* sorted values so the index matches RES[2:0] in STM32H7_ADC_CFGR */
+	16, 14, 12, 10, 8,
+};
+
+static const struct stm32_adc_info stm32h7_adc_info = {
+	.channels = stm32h7_adc_channels,
+	.max_channels = ARRAY_SIZE(stm32h7_adc_channels),
+	.resolutions = stm32h7_adc_resolutions,
+	.num_res = ARRAY_SIZE(stm32h7_adc_resolutions),
+};
+
 /**
  * stm32f4_sq - describe regular sequence registers
  * - L: sequence len (register & bit field)
@@ -327,6 +458,58 @@ struct stm32_adc_info {
 	.res = { STM32F4_ADC_CR1, STM32F4_RES_MASK, STM32F4_RES_SHIFT },
 };
 
+static const struct stm32_adc_regs stm32h7_sq[STM32_ADC_MAX_SQ + 1] = {
+	/* L: len bit field description to be kept as first element */
+	{ STM32H7_ADC_SQR1, GENMASK(3, 0), 0 },
+	/* SQ1..SQ16 registers & bit fields (reg, mask, shift) */
+	{ STM32H7_ADC_SQR1, GENMASK(10, 6), 6 },
+	{ STM32H7_ADC_SQR1, GENMASK(16, 12), 12 },
+	{ STM32H7_ADC_SQR1, GENMASK(22, 18), 18 },
+	{ STM32H7_ADC_SQR1, GENMASK(28, 24), 24 },
+	{ STM32H7_ADC_SQR2, GENMASK(4, 0), 0 },
+	{ STM32H7_ADC_SQR2, GENMASK(10, 6), 6 },
+	{ STM32H7_ADC_SQR2, GENMASK(16, 12), 12 },
+	{ STM32H7_ADC_SQR2, GENMASK(22, 18), 18 },
+	{ STM32H7_ADC_SQR2, GENMASK(28, 24), 24 },
+	{ STM32H7_ADC_SQR3, GENMASK(4, 0), 0 },
+	{ STM32H7_ADC_SQR3, GENMASK(10, 6), 6 },
+	{ STM32H7_ADC_SQR3, GENMASK(16, 12), 12 },
+	{ STM32H7_ADC_SQR3, GENMASK(22, 18), 18 },
+	{ STM32H7_ADC_SQR3, GENMASK(28, 24), 24 },
+	{ STM32H7_ADC_SQR4, GENMASK(4, 0), 0 },
+	{ STM32H7_ADC_SQR4, GENMASK(10, 6), 6 },
+};
+
+/* STM32H7 external trigger sources for all instances */
+static struct stm32_adc_trig_info stm32h7_adc_trigs[] = {
+	{ TIM1_CH1, STM32_EXT0 },
+	{ TIM1_CH2, STM32_EXT1 },
+	{ TIM1_CH3, STM32_EXT2 },
+	{ TIM2_CH2, STM32_EXT3 },
+	{ TIM3_TRGO, STM32_EXT4 },
+	{ TIM4_CH4, STM32_EXT5 },
+	{ TIM8_TRGO, STM32_EXT7 },
+	{ TIM8_TRGO2, STM32_EXT8 },
+	{ TIM1_TRGO, STM32_EXT9 },
+	{ TIM1_TRGO2, STM32_EXT10 },
+	{ TIM2_TRGO, STM32_EXT11 },
+	{ TIM4_TRGO, STM32_EXT12 },
+	{ TIM6_TRGO, STM32_EXT13 },
+	{ TIM3_CH4, STM32_EXT15 },
+	{},
+};
+
+static const struct stm32_adc_regspec stm32h7_adc_regspec = {
+	.dr = STM32H7_ADC_DR,
+	.ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
+	.isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
+	.sqr = stm32h7_sq,
+	.exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
+	.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
+		    STM32H7_EXTSEL_SHIFT },
+	.res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
+};
+
 /**
  * STM32 ADC registers access routines
  * @adc: stm32 adc instance
@@ -340,6 +523,12 @@ static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
 	return readl_relaxed(adc->common->base + adc->offset + reg);
 }
 
+#define stm32_adc_readl_addr(addr)	stm32_adc_readl(adc, addr)
+
+#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \
+	readx_poll_timeout(stm32_adc_readl_addr, reg, val, \
+			   cond, sleep_us, timeout_us)
+
 static u16 stm32_adc_readw(struct stm32_adc *adc, u32 reg)
 {
 	return readw_relaxed(adc->common->base + adc->offset + reg);
@@ -436,6 +625,324 @@ static void stm32f4_adc_stop_conv(struct stm32_adc *adc)
 			   STM32F4_ADON | STM32F4_DMA | STM32F4_DDS);
 }
 
+static void stm32h7_adc_start_conv(struct stm32_adc *adc, bool dma)
+{
+	enum stm32h7_adc_dmngt dmngt;
+	unsigned long flags;
+	u32 val;
+
+	if (dma)
+		dmngt = STM32H7_DMNGT_DMA_CIRC;
+	else
+		dmngt = STM32H7_DMNGT_DR_ONLY;
+
+	spin_lock_irqsave(&adc->lock, flags);
+	val = stm32_adc_readl(adc, STM32H7_ADC_CFGR);
+	val = (val & ~STM32H7_DMNGT_MASK) | (dmngt << STM32H7_DMNGT_SHIFT);
+	stm32_adc_writel(adc, STM32H7_ADC_CFGR, val);
+	spin_unlock_irqrestore(&adc->lock, flags);
+
+	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTART);
+}
+
+static void stm32h7_adc_stop_conv(struct stm32_adc *adc)
+{
+	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+	int ret;
+	u32 val;
+
+	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTP);
+
+	ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+					   !(val & (STM32H7_ADSTART)),
+					   100, STM32_ADC_TIMEOUT_US);
+	if (ret)
+		dev_warn(&indio_dev->dev, "stop failed\n");
+
+	stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK);
+}
+
+static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
+{
+	/* Exit deep power down, then enable ADC voltage regulator */
+	stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
+	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN);
+
+	if (adc->common->rate > STM32H7_BOOST_CLKRATE)
+		stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
+
+	/* Wait for startup time */
+	usleep_range(10, 20);
+}
+
+static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc)
+{
+	stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
+
+	/* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */
+	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
+}
+
+static int stm32h7_adc_enable(struct stm32_adc *adc)
+{
+	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+	int ret;
+	u32 val;
+
+	/* Clear ADRDY by writing one, then enable ADC */
+	stm32_adc_set_bits(adc, STM32H7_ADC_ISR, STM32H7_ADRDY);
+	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADEN);
+
+	/* Poll for ADRDY to be set (after adc startup time) */
+	ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val,
+					   val & STM32H7_ADRDY,
+					   100, STM32_ADC_TIMEOUT_US);
+	if (ret) {
+		stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADEN);
+		dev_err(&indio_dev->dev, "Failed to enable ADC\n");
+	}
+
+	return ret;
+}
+
+static void stm32h7_adc_disable(struct stm32_adc *adc)
+{
+	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+	int ret;
+	u32 val;
+
+	/* Disable ADC and wait until it's effectively disabled */
+	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADDIS);
+	ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+					   !(val & STM32H7_ADEN), 100,
+					   STM32_ADC_TIMEOUT_US);
+	if (ret)
+		dev_warn(&indio_dev->dev, "Failed to disable\n");
+}
+
+/**
+ * stm32h7_adc_read_selfcalib() - read calibration shadow regs, save result
+ * @adc: stm32 adc instance
+ */
+static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc)
+{
+	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+	int i, ret;
+	u32 lincalrdyw_mask, val;
+
+	/* Enable adc so LINCALRDYW1..6 bits are writable */
+	ret = stm32h7_adc_enable(adc);
+	if (ret)
+		return ret;
+
+	/* Read linearity calibration */
+	lincalrdyw_mask = STM32H7_LINCALRDYW6;
+	for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
+		/* Clear STM32H7_LINCALRDYW[6..1]: transfer calib to CALFACT2 */
+		stm32_adc_clr_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask);
+
+		/* Poll: wait calib data to be ready in CALFACT2 register */
+		ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+						   !(val & lincalrdyw_mask),
+						   100, STM32_ADC_TIMEOUT_US);
+		if (ret) {
+			dev_err(&indio_dev->dev, "Failed to read calfact\n");
+			goto disable;
+		}
+
+		val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2);
+		adc->cal.lincalfact[i] = (val & STM32H7_LINCALFACT_MASK);
+		adc->cal.lincalfact[i] >>= STM32H7_LINCALFACT_SHIFT;
+
+		lincalrdyw_mask >>= 1;
+	}
+
+	/* Read offset calibration */
+	val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT);
+	adc->cal.calfact_s = (val & STM32H7_CALFACT_S_MASK);
+	adc->cal.calfact_s >>= STM32H7_CALFACT_S_SHIFT;
+	adc->cal.calfact_d = (val & STM32H7_CALFACT_D_MASK);
+	adc->cal.calfact_d >>= STM32H7_CALFACT_D_SHIFT;
+
+disable:
+	stm32h7_adc_disable(adc);
+
+	return ret;
+}
+
+/**
+ * stm32h7_adc_restore_selfcalib() - Restore saved self-calibration result
+ * @adc: stm32 adc instance
+ * Note: ADC must be enabled, with no on-going conversions.
+ */
+static int stm32h7_adc_restore_selfcalib(struct stm32_adc *adc)
+{
+	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+	int i, ret;
+	u32 lincalrdyw_mask, val;
+
+	val = (adc->cal.calfact_s << STM32H7_CALFACT_S_SHIFT) |
+		(adc->cal.calfact_d << STM32H7_CALFACT_D_SHIFT);
+	stm32_adc_writel(adc, STM32H7_ADC_CALFACT, val);
+
+	lincalrdyw_mask = STM32H7_LINCALRDYW6;
+	for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
+		/*
+		 * Write saved calibration data to shadow registers:
+		 * Write CALFACT2, and set LINCALRDYW[6..1] bit to trigger
+		 * data write. Then poll to wait for complete transfer.
+		 */
+		val = adc->cal.lincalfact[i] << STM32H7_LINCALFACT_SHIFT;
+		stm32_adc_writel(adc, STM32H7_ADC_CALFACT2, val);
+		stm32_adc_set_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask);
+		ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+						   val & lincalrdyw_mask,
+						   100, STM32_ADC_TIMEOUT_US);
+		if (ret) {
+			dev_err(&indio_dev->dev, "Failed to write calfact\n");
+			return ret;
+		}
+
+		/*
+		 * Read back calibration data, has two effects:
+		 * - It ensures bits LINCALRDYW[6..1] are kept cleared
+		 *   for next time calibration needs to be restored.
+		 * - BTW, bit clear triggers a read, then check data has been
+		 *   correctly written.
+		 */
+		stm32_adc_clr_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask);
+		ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+						   !(val & lincalrdyw_mask),
+						   100, STM32_ADC_TIMEOUT_US);
+		if (ret) {
+			dev_err(&indio_dev->dev, "Failed to read calfact\n");
+			return ret;
+		}
+		val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2);
+		if (val != adc->cal.lincalfact[i] << STM32H7_LINCALFACT_SHIFT) {
+			dev_err(&indio_dev->dev, "calfact not consistent\n");
+			return -EIO;
+		}
+
+		lincalrdyw_mask >>= 1;
+	}
+
+	return 0;
+}
+
+/**
+ * Fixed timeout value for ADC calibration.
+ * worst cases:
+ * - low clock frequency
+ * - maximum prescalers
+ * Calibration requires:
+ * - 131,072 ADC clock cycle for the linear calibration
+ * - 20 ADC clock cycle for the offset calibration
+ *
+ * Set to 100ms for now
+ */
+#define STM32H7_ADC_CALIB_TIMEOUT_US		100000
+
+/**
+ * stm32h7_adc_selfcalib() - Procedure to calibrate ADC (from power down)
+ * @adc: stm32 adc instance
+ * Exit from power down, calibrate ADC, then return to power down.
+ */
+static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
+{
+	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+	int ret;
+	u32 val;
+
+	stm32h7_adc_exit_pwr_down(adc);
+
+	/*
+	 * Select calibration mode:
+	 * - Offset calibration for single ended inputs
+	 * - No linearity calibration (do it later, before reading it)
+	 */
+	stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALDIF);
+	stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALLIN);
+
+	/* Start calibration, then wait for completion */
+	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL);
+	ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+					   !(val & STM32H7_ADCAL), 100,
+					   STM32H7_ADC_CALIB_TIMEOUT_US);
+	if (ret) {
+		dev_err(&indio_dev->dev, "calibration failed\n");
+		goto pwr_dwn;
+	}
+
+	/*
+	 * Select calibration mode, then start calibration:
+	 * - Offset calibration for differential input
+	 * - Linearity calibration (needs to be done only once for single/diff)
+	 *   will run simultaneously with offset calibration.
+	 */
+	stm32_adc_set_bits(adc, STM32H7_ADC_CR,
+			   STM32H7_ADCALDIF | STM32H7_ADCALLIN);
+	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL);
+	ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
+					   !(val & STM32H7_ADCAL), 100,
+					   STM32H7_ADC_CALIB_TIMEOUT_US);
+	if (ret) {
+		dev_err(&indio_dev->dev, "calibration failed\n");
+		goto pwr_dwn;
+	}
+
+	stm32_adc_clr_bits(adc, STM32H7_ADC_CR,
+			   STM32H7_ADCALDIF | STM32H7_ADCALLIN);
+
+	/* Read calibration result for future reference */
+	ret = stm32h7_adc_read_selfcalib(adc);
+
+pwr_dwn:
+	stm32h7_adc_enter_pwr_down(adc);
+
+	return ret;
+}
+
+/**
+ * stm32h7_adc_prepare() - Leave power down mode to enable ADC.
+ * @adc: stm32 adc instance
+ * Leave power down mode.
+ * Enable ADC.
+ * Restore calibration data.
+ * Pre-select channels that may be used in PCSEL (required by input MUX / IO).
+ */
+static int stm32h7_adc_prepare(struct stm32_adc *adc)
+{
+	int ret;
+
+	stm32h7_adc_exit_pwr_down(adc);
+
+	ret = stm32h7_adc_enable(adc);
+	if (ret)
+		goto pwr_dwn;
+
+	ret = stm32h7_adc_restore_selfcalib(adc);
+	if (ret)
+		goto disable;
+
+	stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel);
+
+	return 0;
+
+disable:
+	stm32h7_adc_disable(adc);
+pwr_dwn:
+	stm32h7_adc_enter_pwr_down(adc);
+
+	return ret;
+}
+
+static void stm32h7_adc_unprepare(struct stm32_adc *adc)
+{
+	stm32h7_adc_disable(adc);
+	stm32h7_adc_enter_pwr_down(adc);
+}
+
 /**
  * stm32_adc_conf_scan_seq() - Build regular channels scan sequence
  * @indio_dev: IIO device
@@ -606,6 +1113,12 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
 
 	adc->bufi = 0;
 
+	if (adc->cfg->prepare) {
+		ret = adc->cfg->prepare(adc);
+		if (ret)
+			return ret;
+	}
+
 	/* Program chan number in regular sequence (SQ1) */
 	val = stm32_adc_readl(adc, regs->sqr[1].reg);
 	val &= ~regs->sqr[1].mask;
@@ -637,6 +1150,9 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
 
 	stm32_adc_conv_irq_disable(adc);
 
+	if (adc->cfg->unprepare)
+		adc->cfg->unprepare(adc);
+
 	return ret;
 }
 
@@ -861,10 +1377,16 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
 	struct stm32_adc *adc = iio_priv(indio_dev);
 	int ret;
 
+	if (adc->cfg->prepare) {
+		ret = adc->cfg->prepare(adc);
+		if (ret)
+			return ret;
+	}
+
 	ret = stm32_adc_set_trig(indio_dev, indio_dev->trig);
 	if (ret) {
 		dev_err(&indio_dev->dev, "Can't set trigger\n");
-		return ret;
+		goto err_unprepare;
 	}
 
 	ret = stm32_adc_dma_start(indio_dev);
@@ -892,6 +1414,9 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
 		dmaengine_terminate_all(adc->dma_chan);
 err_clr_trig:
 	stm32_adc_set_trig(indio_dev, NULL);
+err_unprepare:
+	if (adc->cfg->unprepare)
+		adc->cfg->unprepare(adc);
 
 	return ret;
 }
@@ -915,6 +1440,9 @@ static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
 	if (stm32_adc_set_trig(indio_dev, NULL))
 		dev_err(&indio_dev->dev, "Can't clear trigger\n");
 
+	if (adc->cfg->unprepare)
+		adc->cfg->unprepare(adc);
+
 	return ret;
 }
 
@@ -1002,6 +1530,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
 {
 	struct stm32_adc *adc = iio_priv(indio_dev);
 
+	/* Initialize IIO channel */
 	chan->type = channel->type;
 	chan->channel = channel->channel;
 	chan->datasheet_name = channel->name;
@@ -1013,6 +1542,9 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
 	chan->scan_type.realbits = adc->cfg->adc_info->resolutions[adc->res];
 	chan->scan_type.storagebits = 16;
 	chan->ext_info = stm32_adc_ext_info;
+
+	/* pre-build selected channels mask */
+	adc->pcsel |= BIT(chan->channel);
 }
 
 static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
@@ -1166,6 +1698,12 @@ static int stm32_adc_probe(struct platform_device *pdev)
 		goto err_clk_disable;
 	stm32_adc_set_res(adc);
 
+	if (adc->cfg->selfcalib) {
+		ret = adc->cfg->selfcalib(adc);
+		if (ret)
+			goto err_clk_disable;
+	}
+
 	ret = stm32_adc_chan_of_init(indio_dev);
 	if (ret < 0)
 		goto err_clk_disable;
@@ -1236,8 +1774,20 @@ static int stm32_adc_remove(struct platform_device *pdev)
 	.stop_conv = stm32f4_adc_stop_conv,
 };
 
+static const struct stm32_adc_cfg stm32h7_adc_cfg = {
+	.regs = &stm32h7_adc_regspec,
+	.adc_info = &stm32h7_adc_info,
+	.trigs = stm32h7_adc_trigs,
+	.selfcalib = stm32h7_adc_selfcalib,
+	.start_conv = stm32h7_adc_start_conv,
+	.stop_conv = stm32h7_adc_stop_conv,
+	.prepare = stm32h7_adc_prepare,
+	.unprepare = stm32h7_adc_unprepare,
+};
+
 static const struct of_device_id stm32_adc_of_match[] = {
 	{ .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg },
+	{ .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg },
 	{},
 };
 MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
-- 
1.9.1

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 5/5] iio: adc: stm32: add support for STM32H7
  2017-05-19 12:45 ` [PATCH 5/5] iio: adc: stm32: add support for STM32H7 Fabrice Gasnier
@ 2017-05-20 17:42   ` Jonathan Cameron
  0 siblings, 0 replies; 9+ messages in thread
From: Jonathan Cameron @ 2017-05-20 17:42 UTC (permalink / raw)
  To: linux-arm-kernel

On 19/05/17 13:45, Fabrice Gasnier wrote:
> Add support for STM32H7 Analog to Digital Converter. It has up
> to 20 external channels, resolution ranges from 8 to 16bits.
> Either bus or asynchronous adc clock may be used.
> 
> Add registers & bitfields definition. Also add new configuration
> options to enter/exit powerdown and perform self-calibration.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
A couple of trivial bits inline.

Nothing that would prevent me applying this, but I would like to
leave it on the list for a little longer first, purely because
its a substantial addition to the driver, and we might get
some more eyes on it. (I was very close to just taking it but
I'd also like to give time for the devicetree lot to have time
to comment if they wish to.

Do give me a poke if I seem to have forgotten this one
(it wouldn't be the first time that I'd demonstrated that level
of forgetfulness!)

Thanks,

Jonathan
> ---
>   drivers/iio/adc/stm32-adc-core.c | 171 +++++++++++-
>   drivers/iio/adc/stm32-adc-core.h |   2 +
>   drivers/iio/adc/stm32-adc.c      | 552 ++++++++++++++++++++++++++++++++++++++-
>   3 files changed, 721 insertions(+), 4 deletions(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
> index c5d292c..e09233b 100644
> --- a/drivers/iio/adc/stm32-adc-core.c
> +++ b/drivers/iio/adc/stm32-adc-core.c
> @@ -49,6 +49,23 @@
>   /* STM32 F4 maximum analog clock rate (from datasheet) */
>   #define STM32F4_ADC_MAX_CLK_RATE	36000000
>   
> +/* STM32H7 - common registers for all ADC instances */
> +#define STM32H7_ADC_CSR			(STM32_ADCX_COMN_OFFSET + 0x00)
> +#define STM32H7_ADC_CCR			(STM32_ADCX_COMN_OFFSET + 0x08)
> +
> +/* STM32H7_ADC_CSR - bit fields */
> +#define STM32H7_EOC_SLV			BIT(18)
> +#define STM32H7_EOC_MST			BIT(2)
> +
> +/* STM32H7_ADC_CCR - bit fields */
> +#define STM32H7_PRESC_SHIFT		18
> +#define STM32H7_PRESC_MASK		GENMASK(21, 18)
> +#define STM32H7_CKMODE_SHIFT		16
> +#define STM32H7_CKMODE_MASK		GENMASK(17, 16)
> +
> +/* STM32 H7 maximum analog clock rate (from datasheet) */
> +#define STM32H7_ADC_MAX_CLK_RATE	72000000
> +
>   /**
>    * stm32_adc_common_regs - stm32 common registers, compatible dependent data
>    * @csr:	common status register offset
> @@ -80,6 +97,7 @@ struct stm32_adc_priv_cfg {
>    * @irq:		irq for ADC block
>    * @domain:		irq domain reference
>    * @aclk:		clock reference for the analog circuitry
> + * @bclk:		bus clock common for all ADCs, depends on part used
>    * @vref:		regulator reference
>    * @cfg:		compatible configuration data
>    * @common:		common data for all ADC instances
> @@ -88,6 +106,7 @@ struct stm32_adc_priv {
>   	int				irq;
>   	struct irq_domain		*domain;
>   	struct clk			*aclk;
> +	struct clk			*bclk;
>   	struct regulator		*vref;
>   	const struct stm32_adc_priv_cfg	*cfg;
>   	struct stm32_adc_common		common;
> @@ -129,6 +148,7 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
>   		return -EINVAL;
>   	}
>   
> +	priv->common.rate = rate;
>   	val = readl_relaxed(priv->common.base + STM32F4_ADC_CCR);
>   	val &= ~STM32F4_ADC_ADCPRE_MASK;
>   	val |= i << STM32F4_ADC_ADCPRE_SHIFT;
> @@ -140,6 +160,111 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
>   	return 0;
>   }
>   
> +/**
> + * struct stm32h7_adc_ck_spec - specification for stm32h7 adc clock
> + * @ckmode: ADC clock mode, Async or sync with prescaler.
> + * @presc: prescaler bitfield for async clock mode
> + * @div: prescaler division ratio
> + */
> +struct stm32h7_adc_ck_spec {
> +	u32 ckmode;
> +	u32 presc;
> +	int div;
> +};
> +
> +const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = {
> +	/* 00: CK_ADC[1..3]: Asynchronous clock modes */
> +	{ 0, 0, 1 },
> +	{ 0, 1, 2 },
> +	{ 0, 2, 4 },
> +	{ 0, 3, 6 },
> +	{ 0, 4, 8 },
> +	{ 0, 5, 10 },
> +	{ 0, 6, 12 },
> +	{ 0, 7, 16 },
> +	{ 0, 8, 32 },
> +	{ 0, 9, 64 },
> +	{ 0, 10, 128 },
> +	{ 0, 11, 256 },
> +	/* HCLK used: Synchronous clock modes (1, 2 or 4 prescaler) */
> +	{ 1, 0, 1 },
> +	{ 2, 0, 2 },
> +	{ 3, 0, 4 },
> +};
> +
> +static int stm32h7_adc_clk_sel(struct platform_device *pdev,
> +			       struct stm32_adc_priv *priv)
> +{
> +	u32 ckmode, presc, val;
> +	unsigned long rate;
> +	int i, div;
> +
> +	/* stm32h7 bus clock is common for all ADC instances (mandatory) */
> +	if (!priv->bclk) {
> +		dev_err(&pdev->dev, "No 'bus' clock found\n");
> +		return -ENOENT;
> +	}
> +
> +	/*
> +	 * stm32h7 can use either 'bus' or 'adc' clock for analog circuitry.
> +	 * So, choice is to have bus clock mandatory and adc clock optional.
> +	 * If optional 'adc' clock has been found, then try to use it first.
> +	 */
> +	if (priv->aclk) {
> +		/*
> +		 * Asynchronous clock modes (e.g. ckmode == 0)
> +		 * From spec: PLL output musn't exceed max rate
> +		 */
> +		rate = clk_get_rate(priv->aclk);
> +
> +		for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
> +			ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
> +			presc = stm32h7_adc_ckmodes_spec[i].presc;
> +			div = stm32h7_adc_ckmodes_spec[i].div;
> +
> +			if (ckmode)
> +				continue;
> +
> +			if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
> +				goto out;
> +		}
> +	}
> +
> +	/* Synchronous clock modes (e.g. ckmode is 1, 2 or 3) */
> +	rate = clk_get_rate(priv->bclk);
> +
> +	for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
> +		ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
> +		presc = stm32h7_adc_ckmodes_spec[i].presc;
> +		div = stm32h7_adc_ckmodes_spec[i].div;
> +
> +		if (!ckmode)
> +			continue;
> +
> +		if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
> +			goto out;
> +	}
> +
> +	dev_err(&pdev->dev, "adc clk selection failed\n");
> +	return -EINVAL;
> +
> +out:
> +	/* rate used later by each ADC instance to control BOOST mode */
> +	priv->common.rate = rate;
> +
> +	/* Set common clock mode and prescaler */
> +	val = readl_relaxed(priv->common.base + STM32H7_ADC_CCR);
> +	val &= ~(STM32H7_CKMODE_MASK | STM32H7_PRESC_MASK);
> +	val |= ckmode << STM32H7_CKMODE_SHIFT;
> +	val |= presc << STM32H7_PRESC_SHIFT;
> +	writel_relaxed(val, priv->common.base + STM32H7_ADC_CCR);
> +
> +	dev_dbg(&pdev->dev, "Using %s clock/%d source at %ld kHz\n",
> +		ckmode ? "bus" : "adc", div, rate / (div * 1000));
> +
> +	return 0;
> +}
> +
>   /* STM32F4 common registers definitions */
>   static const struct stm32_adc_common_regs stm32f4_adc_common_regs = {
>   	.csr = STM32F4_ADC_CSR,
> @@ -148,6 +273,13 @@ static int stm32f4_adc_clk_sel(struct platform_device *pdev,
>   	.eoc3_msk = STM32F4_EOC3,
>   };
>   
> +/* STM32H7 common registers definitions */
> +static const struct stm32_adc_common_regs stm32h7_adc_common_regs = {
> +	.csr = STM32H7_ADC_CSR,
> +	.eoc1_msk = STM32H7_EOC_MST,
> +	.eoc2_msk = STM32H7_EOC_SLV,
> +};
> +
>   /* ADC common interrupt for all instances */
>   static void stm32_adc_irq_handler(struct irq_desc *desc)
>   {
> @@ -291,13 +423,32 @@ static int stm32_adc_probe(struct platform_device *pdev)
>   		}
>   	}
>   
> +	priv->bclk = devm_clk_get(&pdev->dev, "bus");
> +	if (IS_ERR(priv->bclk)) {
> +		ret = PTR_ERR(priv->bclk);
> +		if (ret == -ENOENT) {
> +			priv->bclk = NULL;
> +		} else {
> +			dev_err(&pdev->dev, "Can't get 'bus' clock\n");
> +			goto err_aclk_disable;
> +		}
> +	}
> +
> +	if (priv->bclk) {
> +		ret = clk_prepare_enable(priv->bclk);
> +		if (ret < 0) {
> +			dev_err(&pdev->dev, "adc clk enable failed\n");
> +			goto err_aclk_disable;
> +		}
> +	}
> +
>   	ret = priv->cfg->clk_sel(pdev, priv);
>   	if (ret < 0)
> -		goto err_clk_disable;
> +		goto err_bclk_disable;
>   
>   	ret = stm32_adc_irq_probe(pdev, priv);
>   	if (ret < 0)
> -		goto err_clk_disable;
> +		goto err_bclk_disable;
>   
>   	platform_set_drvdata(pdev, &priv->common);
>   
> @@ -312,7 +463,11 @@ static int stm32_adc_probe(struct platform_device *pdev)
>   err_irq_remove:
>   	stm32_adc_irq_remove(pdev, priv);
>   
> -err_clk_disable:
> +err_bclk_disable:
> +	if (priv->bclk)
> +		clk_disable_unprepare(priv->bclk);
> +
> +err_aclk_disable:
>   	if (priv->aclk)
>   		clk_disable_unprepare(priv->aclk);
>   
> @@ -329,6 +484,8 @@ static int stm32_adc_remove(struct platform_device *pdev)
>   
>   	of_platform_depopulate(&pdev->dev);
>   	stm32_adc_irq_remove(pdev, priv);
> +	if (priv->bclk)
> +		clk_disable_unprepare(priv->bclk);
>   	if (priv->aclk)
>   		clk_disable_unprepare(priv->aclk);
>   	regulator_disable(priv->vref);
> @@ -341,11 +498,19 @@ static int stm32_adc_remove(struct platform_device *pdev)
>   	.clk_sel = stm32f4_adc_clk_sel,
>   };
>   
> +static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
> +	.regs = &stm32h7_adc_common_regs,
> +	.clk_sel = stm32h7_adc_clk_sel,
> +};
> +
>   static const struct of_device_id stm32_adc_of_match[] = {
>   	{
>   		.compatible = "st,stm32f4-adc-core",
>   		.data = (void *)&stm32f4_adc_priv_cfg
>   	}, {
> +		.compatible = "st,stm32h7-adc-core",
> +		.data = (void *)&stm32h7_adc_priv_cfg
> +	}, {
>   	},
>   };
>   MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
> diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
> index 2ec7abb..250ee95 100644
> --- a/drivers/iio/adc/stm32-adc-core.h
> +++ b/drivers/iio/adc/stm32-adc-core.h
> @@ -43,11 +43,13 @@
>    * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
>    * @base:		control registers base cpu addr
>    * @phys_base:		control registers base physical addr
> + * @rate:		clock rate used for analog circuitry
>    * @vref_mv:		vref voltage (mv)
>    */
>   struct stm32_adc_common {
>   	void __iomem			*base;
>   	phys_addr_t			phys_base;
> +	unsigned long			rate;
>   	int				vref_mv;
>   };
>   
> diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
> index ad8fdb9..dba89c7 100644
> --- a/drivers/iio/adc/stm32-adc.c
> +++ b/drivers/iio/adc/stm32-adc.c
> @@ -31,6 +31,7 @@
>   #include <linux/iio/triggered_buffer.h>
>   #include <linux/interrupt.h>
>   #include <linux/io.h>
> +#include <linux/iopoll.h>
>   #include <linux/module.h>
>   #include <linux/platform_device.h>
>   #include <linux/of.h>
> @@ -77,6 +78,78 @@
>   #define STM32F4_DMA			BIT(8)
>   #define STM32F4_ADON			BIT(0)
>   
> +/* STM32H7 - Registers for each ADC instance */
> +#define STM32H7_ADC_ISR			0x00
> +#define STM32H7_ADC_IER			0x04
> +#define STM32H7_ADC_CR			0x08
> +#define STM32H7_ADC_CFGR		0x0C
> +#define STM32H7_ADC_PCSEL		0x1C
> +#define STM32H7_ADC_SQR1		0x30
> +#define STM32H7_ADC_SQR2		0x34
> +#define STM32H7_ADC_SQR3		0x38
> +#define STM32H7_ADC_SQR4		0x3C
> +#define STM32H7_ADC_DR			0x40
> +#define STM32H7_ADC_CALFACT		0xC4
> +#define STM32H7_ADC_CALFACT2		0xC8
> +
> +/* STM32H7_ADC_ISR - bit fields */
> +#define STM32H7_EOC			BIT(2)
> +#define STM32H7_ADRDY			BIT(0)
> +
> +/* STM32H7_ADC_IER - bit fields */
> +#define STM32H7_EOCIE			STM32H7_EOC
> +
> +/* STM32H7_ADC_CR - bit fields */
> +#define STM32H7_ADCAL			BIT(31)
> +#define STM32H7_ADCALDIF		BIT(30)
> +#define STM32H7_DEEPPWD			BIT(29)
> +#define STM32H7_ADVREGEN		BIT(28)
> +#define STM32H7_LINCALRDYW6		BIT(27)
> +#define STM32H7_LINCALRDYW5		BIT(26)
> +#define STM32H7_LINCALRDYW4		BIT(25)
> +#define STM32H7_LINCALRDYW3		BIT(24)
> +#define STM32H7_LINCALRDYW2		BIT(23)
> +#define STM32H7_LINCALRDYW1		BIT(22)
> +#define STM32H7_ADCALLIN		BIT(16)
> +#define STM32H7_BOOST			BIT(8)
> +#define STM32H7_ADSTP			BIT(4)
> +#define STM32H7_ADSTART			BIT(2)
> +#define STM32H7_ADDIS			BIT(1)
> +#define STM32H7_ADEN			BIT(0)
> +
> +/* STM32H7_ADC_CFGR bit fields */
> +#define STM32H7_EXTEN_SHIFT		10
> +#define STM32H7_EXTEN_MASK		GENMASK(11, 10)
> +#define STM32H7_EXTSEL_SHIFT		5
> +#define STM32H7_EXTSEL_MASK		GENMASK(9, 5)
> +#define STM32H7_RES_SHIFT		2
> +#define STM32H7_RES_MASK		GENMASK(4, 2)
> +#define STM32H7_DMNGT_SHIFT		0
> +#define STM32H7_DMNGT_MASK		GENMASK(1, 0)
> +
> +enum stm32h7_adc_dmngt {
> +	STM32H7_DMNGT_DR_ONLY,		/* Regular data in DR only */
> +	STM32H7_DMNGT_DMA_ONESHOT,	/* DMA one shot mode */
> +	STM32H7_DMNGT_DFSDM,		/* DFSDM mode */
> +	STM32H7_DMNGT_DMA_CIRC,		/* DMA circular mode */
> +};
> +
> +/* STM32H7_ADC_CALFACT - bit fields */
> +#define STM32H7_CALFACT_D_SHIFT		16
> +#define STM32H7_CALFACT_D_MASK		GENMASK(26, 16)
> +#define STM32H7_CALFACT_S_SHIFT		0
> +#define STM32H7_CALFACT_S_MASK		GENMASK(10, 0)
> +
> +/* STM32H7_ADC_CALFACT2 - bit fields */
> +#define STM32H7_LINCALFACT_SHIFT	0
> +#define STM32H7_LINCALFACT_MASK		GENMASK(29, 0)
> +
> +/* Number of linear calibration shadow registers / LINCALRDYW control bits */
> +#define STM32H7_LINCALFACT_NUM		6
> +
> +/* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */
> +#define STM32H7_BOOST_CLKRATE		20000000UL
> +
>   #define STM32_ADC_MAX_SQ		16	/* SQ1..SQ16 */
>   #define STM32_ADC_TIMEOUT_US		100000
>   #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
> @@ -122,6 +195,18 @@ struct stm32_adc_trig_info {
>   };
>   
>   /**
> + * struct stm32_adc_calib - optional adc calibration data
> + * @calfact_s: Calibration offset for single ended channels
> + * @calfact_d: Calibration offset in differential
> + * @lincalfact: Linearity calibration factor
> + */
> +struct stm32_adc_calib {
> +	u32			calfact_s;
> +	u32			calfact_d;
> +	u32			lincalfact[STM32H7_LINCALFACT_NUM];
> +};
> +
> +/**
>    * stm32_adc_regs - stm32 ADC misc registers & bitfield desc
>    * @reg:		register offset
>    * @mask:		bitfield mask
> @@ -161,16 +246,22 @@ struct stm32_adc_regspec {
>    * @adc_info:		per instance input channels definitions
>    * @trigs:		external trigger sources
>    * @clk_required:	clock is required
> + * @selfcalib:		optional routine for self-calibration
> + * @prepare:		optional prepare routine (power-up, enable)
>    * @start_conv:		routine to start conversions
>    * @stop_conv:		routine to stop conversions
> + * @unprepare:		optional unprepare routine (disable, power-down)
>    */
>   struct stm32_adc_cfg {
>   	const struct stm32_adc_regspec	*regs;
>   	const struct stm32_adc_info	*adc_info;
>   	struct stm32_adc_trig_info	*trigs;
>   	bool clk_required;
> +	int (*selfcalib)(struct stm32_adc *);
> +	int (*prepare)(struct stm32_adc *);
>   	void (*start_conv)(struct stm32_adc *, bool dma);
>   	void (*stop_conv)(struct stm32_adc *);
> +	void (*unprepare)(struct stm32_adc *);
>   };
>   
>   /**
> @@ -191,6 +282,8 @@ struct stm32_adc_cfg {
>    * @rx_buf:		dma rx buffer cpu address
>    * @rx_dma_buf:		dma rx buffer bus address
>    * @rx_buf_sz:		dma rx buffer size
> + * @pcsel		bitmask to preselect channels on some devices
> + * @cal:		optional calibration data on some devices
>    */
>   struct stm32_adc {
>   	struct stm32_adc_common	*common;
> @@ -209,6 +302,8 @@ struct stm32_adc {
>   	u8			*rx_buf;
>   	dma_addr_t		rx_dma_buf;
>   	unsigned int		rx_buf_sz;
> +	u32			pcsel;
> +	struct stm32_adc_calib	cal;
>   };
>   
>   /**
> @@ -269,6 +364,42 @@ struct stm32_adc_info {
>   	.num_res = ARRAY_SIZE(stm32f4_adc_resolutions),
>   };
>   
> +/* Input definitions for stm32h7 */
> +static const struct stm32_adc_chan_spec stm32h7_adc_channels[] = {
> +	{ IIO_VOLTAGE, 0, "in0" },
> +	{ IIO_VOLTAGE, 1, "in1" },
> +	{ IIO_VOLTAGE, 2, "in2" },
> +	{ IIO_VOLTAGE, 3, "in3" },
> +	{ IIO_VOLTAGE, 4, "in4" },
> +	{ IIO_VOLTAGE, 5, "in5" },
> +	{ IIO_VOLTAGE, 6, "in6" },
> +	{ IIO_VOLTAGE, 7, "in7" },
> +	{ IIO_VOLTAGE, 8, "in8" },
> +	{ IIO_VOLTAGE, 9, "in9" },
> +	{ IIO_VOLTAGE, 10, "in10" },
> +	{ IIO_VOLTAGE, 11, "in11" },
> +	{ IIO_VOLTAGE, 12, "in12" },
> +	{ IIO_VOLTAGE, 13, "in13" },
> +	{ IIO_VOLTAGE, 14, "in14" },
> +	{ IIO_VOLTAGE, 15, "in15" },
> +	{ IIO_VOLTAGE, 16, "in16" },
> +	{ IIO_VOLTAGE, 17, "in17" },
> +	{ IIO_VOLTAGE, 18, "in18" },
> +	{ IIO_VOLTAGE, 19, "in19" },
> +};
It does rather feel like a bit of macro magic would have remove
the need for the third parameter ;)  The first is kind of pointless
currently as well.

It doesn't really matter though, but if you want to send a patch
tidying that up it would be welcome.
> +
> +static const unsigned int stm32h7_adc_resolutions[] = {
> +	/* sorted values so the index matches RES[2:0] in STM32H7_ADC_CFGR */
> +	16, 14, 12, 10, 8,
> +};
> +
> +static const struct stm32_adc_info stm32h7_adc_info = {
> +	.channels = stm32h7_adc_channels,
> +	.max_channels = ARRAY_SIZE(stm32h7_adc_channels),
> +	.resolutions = stm32h7_adc_resolutions,
> +	.num_res = ARRAY_SIZE(stm32h7_adc_resolutions),
> +};
> +
>   /**
>    * stm32f4_sq - describe regular sequence registers
>    * - L: sequence len (register & bit field)
> @@ -327,6 +458,58 @@ struct stm32_adc_info {
>   	.res = { STM32F4_ADC_CR1, STM32F4_RES_MASK, STM32F4_RES_SHIFT },
>   };
>   
> +static const struct stm32_adc_regs stm32h7_sq[STM32_ADC_MAX_SQ + 1] = {
> +	/* L: len bit field description to be kept as first element */
> +	{ STM32H7_ADC_SQR1, GENMASK(3, 0), 0 },
> +	/* SQ1..SQ16 registers & bit fields (reg, mask, shift) */
> +	{ STM32H7_ADC_SQR1, GENMASK(10, 6), 6 },
> +	{ STM32H7_ADC_SQR1, GENMASK(16, 12), 12 },
> +	{ STM32H7_ADC_SQR1, GENMASK(22, 18), 18 },
> +	{ STM32H7_ADC_SQR1, GENMASK(28, 24), 24 },
> +	{ STM32H7_ADC_SQR2, GENMASK(4, 0), 0 },
> +	{ STM32H7_ADC_SQR2, GENMASK(10, 6), 6 },
> +	{ STM32H7_ADC_SQR2, GENMASK(16, 12), 12 },
> +	{ STM32H7_ADC_SQR2, GENMASK(22, 18), 18 },
> +	{ STM32H7_ADC_SQR2, GENMASK(28, 24), 24 },
> +	{ STM32H7_ADC_SQR3, GENMASK(4, 0), 0 },
> +	{ STM32H7_ADC_SQR3, GENMASK(10, 6), 6 },
> +	{ STM32H7_ADC_SQR3, GENMASK(16, 12), 12 },
> +	{ STM32H7_ADC_SQR3, GENMASK(22, 18), 18 },
> +	{ STM32H7_ADC_SQR3, GENMASK(28, 24), 24 },
> +	{ STM32H7_ADC_SQR4, GENMASK(4, 0), 0 },
> +	{ STM32H7_ADC_SQR4, GENMASK(10, 6), 6 },
> +};
> +
> +/* STM32H7 external trigger sources for all instances */
> +static struct stm32_adc_trig_info stm32h7_adc_trigs[] = {
> +	{ TIM1_CH1, STM32_EXT0 },
> +	{ TIM1_CH2, STM32_EXT1 },
> +	{ TIM1_CH3, STM32_EXT2 },
> +	{ TIM2_CH2, STM32_EXT3 },
> +	{ TIM3_TRGO, STM32_EXT4 },
> +	{ TIM4_CH4, STM32_EXT5 },
> +	{ TIM8_TRGO, STM32_EXT7 },
> +	{ TIM8_TRGO2, STM32_EXT8 },
> +	{ TIM1_TRGO, STM32_EXT9 },
> +	{ TIM1_TRGO2, STM32_EXT10 },
> +	{ TIM2_TRGO, STM32_EXT11 },
> +	{ TIM4_TRGO, STM32_EXT12 },
> +	{ TIM6_TRGO, STM32_EXT13 },
> +	{ TIM3_CH4, STM32_EXT15 },
> +	{},
> +};
> +
> +static const struct stm32_adc_regspec stm32h7_adc_regspec = {
> +	.dr = STM32H7_ADC_DR,
> +	.ier_eoc = { STM32H7_ADC_IER, STM32H7_EOCIE },
> +	.isr_eoc = { STM32H7_ADC_ISR, STM32H7_EOC },
> +	.sqr = stm32h7_sq,
> +	.exten = { STM32H7_ADC_CFGR, STM32H7_EXTEN_MASK, STM32H7_EXTEN_SHIFT },
> +	.extsel = { STM32H7_ADC_CFGR, STM32H7_EXTSEL_MASK,
> +		    STM32H7_EXTSEL_SHIFT },
> +	.res = { STM32H7_ADC_CFGR, STM32H7_RES_MASK, STM32H7_RES_SHIFT },
> +};
> +
>   /**
>    * STM32 ADC registers access routines
>    * @adc: stm32 adc instance
> @@ -340,6 +523,12 @@ static u32 stm32_adc_readl(struct stm32_adc *adc, u32 reg)
>   	return readl_relaxed(adc->common->base + adc->offset + reg);
>   }
>   
> +#define stm32_adc_readl_addr(addr)	stm32_adc_readl(adc, addr)
> +
> +#define stm32_adc_readl_poll_timeout(reg, val, cond, sleep_us, timeout_us) \
> +	readx_poll_timeout(stm32_adc_readl_addr, reg, val, \
> +			   cond, sleep_us, timeout_us)
> +
>   static u16 stm32_adc_readw(struct stm32_adc *adc, u32 reg)
>   {
>   	return readw_relaxed(adc->common->base + adc->offset + reg);
> @@ -436,6 +625,324 @@ static void stm32f4_adc_stop_conv(struct stm32_adc *adc)
>   			   STM32F4_ADON | STM32F4_DMA | STM32F4_DDS);
>   }
>   
> +static void stm32h7_adc_start_conv(struct stm32_adc *adc, bool dma)
> +{
> +	enum stm32h7_adc_dmngt dmngt;
> +	unsigned long flags;
> +	u32 val;
> +
> +	if (dma)
> +		dmngt = STM32H7_DMNGT_DMA_CIRC;
> +	else
> +		dmngt = STM32H7_DMNGT_DR_ONLY;
> +
> +	spin_lock_irqsave(&adc->lock, flags);
> +	val = stm32_adc_readl(adc, STM32H7_ADC_CFGR);
> +	val = (val & ~STM32H7_DMNGT_MASK) | (dmngt << STM32H7_DMNGT_SHIFT);
> +	stm32_adc_writel(adc, STM32H7_ADC_CFGR, val);
> +	spin_unlock_irqrestore(&adc->lock, flags);
> +
> +	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTART);
> +}
> +
> +static void stm32h7_adc_stop_conv(struct stm32_adc *adc)
> +{
> +	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +	int ret;
> +	u32 val;
> +
> +	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADSTP);
> +
> +	ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
> +					   !(val & (STM32H7_ADSTART)),
> +					   100, STM32_ADC_TIMEOUT_US);
> +	if (ret)
> +		dev_warn(&indio_dev->dev, "stop failed\n");
> +
> +	stm32_adc_clr_bits(adc, STM32H7_ADC_CFGR, STM32H7_DMNGT_MASK);
> +}
> +
> +static void stm32h7_adc_exit_pwr_down(struct stm32_adc *adc)
> +{
> +	/* Exit deep power down, then enable ADC voltage regulator */
> +	stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
> +	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADVREGEN);
> +
> +	if (adc->common->rate > STM32H7_BOOST_CLKRATE)
> +		stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
> +
> +	/* Wait for startup time */
> +	usleep_range(10, 20);
> +}
> +
> +static void stm32h7_adc_enter_pwr_down(struct stm32_adc *adc)
> +{
> +	stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_BOOST);
> +
> +	/* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */
> +	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_DEEPPWD);
> +}
> +
> +static int stm32h7_adc_enable(struct stm32_adc *adc)
> +{
> +	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +	int ret;
> +	u32 val;
> +
> +	/* Clear ADRDY by writing one, then enable ADC */
> +	stm32_adc_set_bits(adc, STM32H7_ADC_ISR, STM32H7_ADRDY);
> +	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADEN);
> +
> +	/* Poll for ADRDY to be set (after adc startup time) */
> +	ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_ISR, val,
> +					   val & STM32H7_ADRDY,
> +					   100, STM32_ADC_TIMEOUT_US);
> +	if (ret) {
> +		stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADEN);
> +		dev_err(&indio_dev->dev, "Failed to enable ADC\n");
> +	}
> +
> +	return ret;
> +}
> +
> +static void stm32h7_adc_disable(struct stm32_adc *adc)
> +{
> +	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +	int ret;
> +	u32 val;
> +
> +	/* Disable ADC and wait until it's effectively disabled */
> +	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADDIS);
> +	ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
> +					   !(val & STM32H7_ADEN), 100,
> +					   STM32_ADC_TIMEOUT_US);
> +	if (ret)
> +		dev_warn(&indio_dev->dev, "Failed to disable\n");
> +}
> +
> +/**
> + * stm32h7_adc_read_selfcalib() - read calibration shadow regs, save result
> + * @adc: stm32 adc instance
> + */
> +static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc)
> +{
> +	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +	int i, ret;
> +	u32 lincalrdyw_mask, val;
> +
> +	/* Enable adc so LINCALRDYW1..6 bits are writable */
> +	ret = stm32h7_adc_enable(adc);
> +	if (ret)
> +		return ret;
> +
> +	/* Read linearity calibration */
> +	lincalrdyw_mask = STM32H7_LINCALRDYW6;
> +	for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
> +		/* Clear STM32H7_LINCALRDYW[6..1]: transfer calib to CALFACT2 */
> +		stm32_adc_clr_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask);
> +
> +		/* Poll: wait calib data to be ready in CALFACT2 register */
> +		ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
> +						   !(val & lincalrdyw_mask),
> +						   100, STM32_ADC_TIMEOUT_US);
> +		if (ret) {
> +			dev_err(&indio_dev->dev, "Failed to read calfact\n");
> +			goto disable;
> +		}
> +
> +		val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2);
> +		adc->cal.lincalfact[i] = (val & STM32H7_LINCALFACT_MASK);
> +		adc->cal.lincalfact[i] >>= STM32H7_LINCALFACT_SHIFT;
> +
> +		lincalrdyw_mask >>= 1;
> +	}
> +
> +	/* Read offset calibration */
> +	val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT);
> +	adc->cal.calfact_s = (val & STM32H7_CALFACT_S_MASK);
> +	adc->cal.calfact_s >>= STM32H7_CALFACT_S_SHIFT;
> +	adc->cal.calfact_d = (val & STM32H7_CALFACT_D_MASK);
> +	adc->cal.calfact_d >>= STM32H7_CALFACT_D_SHIFT;
> +
> +disable:
> +	stm32h7_adc_disable(adc);
> +
> +	return ret;
> +}
> +
> +/**
> + * stm32h7_adc_restore_selfcalib() - Restore saved self-calibration result
> + * @adc: stm32 adc instance
> + * Note: ADC must be enabled, with no on-going conversions.
> + */
> +static int stm32h7_adc_restore_selfcalib(struct stm32_adc *adc)
> +{
> +	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +	int i, ret;
> +	u32 lincalrdyw_mask, val;
> +
> +	val = (adc->cal.calfact_s << STM32H7_CALFACT_S_SHIFT) |
> +		(adc->cal.calfact_d << STM32H7_CALFACT_D_SHIFT);
> +	stm32_adc_writel(adc, STM32H7_ADC_CALFACT, val);
> +
> +	lincalrdyw_mask = STM32H7_LINCALRDYW6;
> +	for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
> +		/*
> +		 * Write saved calibration data to shadow registers:
> +		 * Write CALFACT2, and set LINCALRDYW[6..1] bit to trigger
> +		 * data write. Then poll to wait for complete transfer.
> +		 */
> +		val = adc->cal.lincalfact[i] << STM32H7_LINCALFACT_SHIFT;
> +		stm32_adc_writel(adc, STM32H7_ADC_CALFACT2, val);
> +		stm32_adc_set_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask);
> +		ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
> +						   val & lincalrdyw_mask,
> +						   100, STM32_ADC_TIMEOUT_US);
> +		if (ret) {
> +			dev_err(&indio_dev->dev, "Failed to write calfact\n");
> +			return ret;
> +		}
> +
> +		/*
> +		 * Read back calibration data, has two effects:
> +		 * - It ensures bits LINCALRDYW[6..1] are kept cleared
> +		 *   for next time calibration needs to be restored.
> +		 * - BTW, bit clear triggers a read, then check data has been
> +		 *   correctly written.
> +		 */
> +		stm32_adc_clr_bits(adc, STM32H7_ADC_CR, lincalrdyw_mask);
> +		ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
> +						   !(val & lincalrdyw_mask),
> +						   100, STM32_ADC_TIMEOUT_US);
> +		if (ret) {
> +			dev_err(&indio_dev->dev, "Failed to read calfact\n");
> +			return ret;
> +		}
> +		val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2);
> +		if (val != adc->cal.lincalfact[i] << STM32H7_LINCALFACT_SHIFT) {
> +			dev_err(&indio_dev->dev, "calfact not consistent\n");
> +			return -EIO;
> +		}
> +
> +		lincalrdyw_mask >>= 1;
> +	}
> +
> +	return 0;
> +}
> +
> +/**
> + * Fixed timeout value for ADC calibration.
> + * worst cases:
> + * - low clock frequency
> + * - maximum prescalers
> + * Calibration requires:
> + * - 131,072 ADC clock cycle for the linear calibration
> + * - 20 ADC clock cycle for the offset calibration
> + *
> + * Set to 100ms for now
> + */
> +#define STM32H7_ADC_CALIB_TIMEOUT_US		100000
> +
> +/**
> + * stm32h7_adc_selfcalib() - Procedure to calibrate ADC (from power down)
> + * @adc: stm32 adc instance
> + * Exit from power down, calibrate ADC, then return to power down.
> + */
> +static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
> +{
> +	struct iio_dev *indio_dev = iio_priv_to_dev(adc);
> +	int ret;
> +	u32 val;
> +
> +	stm32h7_adc_exit_pwr_down(adc);
> +
> +	/*
> +	 * Select calibration mode:
> +	 * - Offset calibration for single ended inputs
> +	 * - No linearity calibration (do it later, before reading it)
> +	 */
> +	stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALDIF);
> +	stm32_adc_clr_bits(adc, STM32H7_ADC_CR, STM32H7_ADCALLIN);
> +
> +	/* Start calibration, then wait for completion */
> +	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL);
> +	ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
> +					   !(val & STM32H7_ADCAL), 100,
> +					   STM32H7_ADC_CALIB_TIMEOUT_US);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "calibration failed\n");
> +		goto pwr_dwn;
> +	}
> +
> +	/*
> +	 * Select calibration mode, then start calibration:
> +	 * - Offset calibration for differential input
> +	 * - Linearity calibration (needs to be done only once for single/diff)
> +	 *   will run simultaneously with offset calibration.
> +	 */
> +	stm32_adc_set_bits(adc, STM32H7_ADC_CR,
> +			   STM32H7_ADCALDIF | STM32H7_ADCALLIN);
> +	stm32_adc_set_bits(adc, STM32H7_ADC_CR, STM32H7_ADCAL);
> +	ret = stm32_adc_readl_poll_timeout(STM32H7_ADC_CR, val,
> +					   !(val & STM32H7_ADCAL), 100,
> +					   STM32H7_ADC_CALIB_TIMEOUT_US);
> +	if (ret) {
> +		dev_err(&indio_dev->dev, "calibration failed\n");
> +		goto pwr_dwn;
> +	}
> +
> +	stm32_adc_clr_bits(adc, STM32H7_ADC_CR,
> +			   STM32H7_ADCALDIF | STM32H7_ADCALLIN);
> +
> +	/* Read calibration result for future reference */
> +	ret = stm32h7_adc_read_selfcalib(adc);
> +
> +pwr_dwn:
> +	stm32h7_adc_enter_pwr_down(adc);
> +
> +	return ret;
> +}
> +
> +/**
> + * stm32h7_adc_prepare() - Leave power down mode to enable ADC.
> + * @adc: stm32 adc instance
> + * Leave power down mode.
> + * Enable ADC.
> + * Restore calibration data.
> + * Pre-select channels that may be used in PCSEL (required by input MUX / IO).
> + */
> +static int stm32h7_adc_prepare(struct stm32_adc *adc)
> +{
> +	int ret;
> +
> +	stm32h7_adc_exit_pwr_down(adc);
> +
> +	ret = stm32h7_adc_enable(adc);
> +	if (ret)
> +		goto pwr_dwn;
> +
> +	ret = stm32h7_adc_restore_selfcalib(adc);
> +	if (ret)
> +		goto disable;
> +
> +	stm32_adc_writel(adc, STM32H7_ADC_PCSEL, adc->pcsel);
> +
> +	return 0;
> +
> +disable:
> +	stm32h7_adc_disable(adc);
> +pwr_dwn:
> +	stm32h7_adc_enter_pwr_down(adc);
> +
> +	return ret;
> +}
> +
> +static void stm32h7_adc_unprepare(struct stm32_adc *adc)
> +{
> +	stm32h7_adc_disable(adc);
> +	stm32h7_adc_enter_pwr_down(adc);
> +}
> +
>   /**
>    * stm32_adc_conf_scan_seq() - Build regular channels scan sequence
>    * @indio_dev: IIO device
> @@ -606,6 +1113,12 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
>   
>   	adc->bufi = 0;
>   
> +	if (adc->cfg->prepare) {
> +		ret = adc->cfg->prepare(adc);
> +		if (ret)
> +			return ret;
> +	}
> +
>   	/* Program chan number in regular sequence (SQ1) */
>   	val = stm32_adc_readl(adc, regs->sqr[1].reg);
>   	val &= ~regs->sqr[1].mask;
> @@ -637,6 +1150,9 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
>   
>   	stm32_adc_conv_irq_disable(adc);
>   
> +	if (adc->cfg->unprepare)
> +		adc->cfg->unprepare(adc);
> +
>   	return ret;
>   }
>   
> @@ -861,10 +1377,16 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
>   	struct stm32_adc *adc = iio_priv(indio_dev);
>   	int ret;
>   
> +	if (adc->cfg->prepare) {
> +		ret = adc->cfg->prepare(adc);
> +		if (ret)
> +			return ret;
> +	}
> +
>   	ret = stm32_adc_set_trig(indio_dev, indio_dev->trig);
>   	if (ret) {
>   		dev_err(&indio_dev->dev, "Can't set trigger\n");
> -		return ret;
> +		goto err_unprepare;
>   	}
>   
>   	ret = stm32_adc_dma_start(indio_dev);
> @@ -892,6 +1414,9 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
>   		dmaengine_terminate_all(adc->dma_chan);
>   err_clr_trig:
>   	stm32_adc_set_trig(indio_dev, NULL);
> +err_unprepare:
> +	if (adc->cfg->unprepare)
> +		adc->cfg->unprepare(adc);
>   
>   	return ret;
>   }
> @@ -915,6 +1440,9 @@ static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
>   	if (stm32_adc_set_trig(indio_dev, NULL))
>   		dev_err(&indio_dev->dev, "Can't clear trigger\n");
>   
> +	if (adc->cfg->unprepare)
> +		adc->cfg->unprepare(adc);
> +
>   	return ret;
>   }
>   
> @@ -1002,6 +1530,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
>   {
>   	struct stm32_adc *adc = iio_priv(indio_dev);
>   
> +	/* Initialize IIO channel */
If I was being really really fussy, this comment shouldn't be in this patch.
Nothing wrong with it, but it isn't what this patch is about.
>   	chan->type = channel->type;
>   	chan->channel = channel->channel;
>   	chan->datasheet_name = channel->name;
> @@ -1013,6 +1542,9 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
>   	chan->scan_type.realbits = adc->cfg->adc_info->resolutions[adc->res];
>   	chan->scan_type.storagebits = 16;
>   	chan->ext_info = stm32_adc_ext_info;
> +
> +	/* pre-build selected channels mask */
> +	adc->pcsel |= BIT(chan->channel);
>   }
>   
>   static int stm32_adc_chan_of_init(struct iio_dev *indio_dev)
> @@ -1166,6 +1698,12 @@ static int stm32_adc_probe(struct platform_device *pdev)
>   		goto err_clk_disable;
>   	stm32_adc_set_res(adc);
>   
> +	if (adc->cfg->selfcalib) {
> +		ret = adc->cfg->selfcalib(adc);
> +		if (ret)
> +			goto err_clk_disable;
> +	}
> +
>   	ret = stm32_adc_chan_of_init(indio_dev);
>   	if (ret < 0)
>   		goto err_clk_disable;
> @@ -1236,8 +1774,20 @@ static int stm32_adc_remove(struct platform_device *pdev)
>   	.stop_conv = stm32f4_adc_stop_conv,
>   };
>   
> +static const struct stm32_adc_cfg stm32h7_adc_cfg = {
> +	.regs = &stm32h7_adc_regspec,
> +	.adc_info = &stm32h7_adc_info,
> +	.trigs = stm32h7_adc_trigs,
> +	.selfcalib = stm32h7_adc_selfcalib,
> +	.start_conv = stm32h7_adc_start_conv,
> +	.stop_conv = stm32h7_adc_stop_conv,
> +	.prepare = stm32h7_adc_prepare,
> +	.unprepare = stm32h7_adc_unprepare,
> +};
> +
>   static const struct of_device_id stm32_adc_of_match[] = {
>   	{ .compatible = "st,stm32f4-adc", .data = (void *)&stm32f4_adc_cfg },
> +	{ .compatible = "st,stm32h7-adc", .data = (void *)&stm32h7_adc_cfg },
>   	{},
>   };
>   MODULE_DEVICE_TABLE(of, stm32_adc_of_match);
> 

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7
  2017-05-19 12:45 ` [PATCH 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7 Fabrice Gasnier
@ 2017-05-23 15:07   ` Rob Herring
  2017-05-23 15:54     ` Fabrice Gasnier
  0 siblings, 1 reply; 9+ messages in thread
From: Rob Herring @ 2017-05-23 15:07 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, May 19, 2017 at 02:45:24PM +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>
> ---
>  .../devicetree/bindings/iio/adc/st,stm32-adc.txt     | 20 ++++++++++++++------
>  1 file changed, 14 insertions(+), 6 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..9519d2e 100644
> --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
> @@ -21,11 +21,17 @@ 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" or "st,stm32h7-adc-core".

Reformat this to 1 per line.

>  - 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 will be used by default if not set.

The clocks listed should be all connections present, not policy as to 
what clock you want to use for conversions.

> +  - "bus" clock: for registers access, common to all ADCs.
> +    It's unused on stm32f4.

s/unused/not present/

> +    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 +48,16 @@ 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" or "st,stm32h7-adc".

One per line.

>  - 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.

Does the h7 simply have the same parent connected to all ADC instances 
or really doesn't have a per instance clock? For the former case, you 
should still have a clock, but just all be the same parent.

>  - interrupt-parent: Phandle to the parent interrupt controller.
>  - interrupts: IRQ Line for the ADC (e.g. may be 0 for adc at 0, 1 for adc at 100 or
>    2 for adc at 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
>  
> -- 
> 1.9.1
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7
  2017-05-23 15:07   ` Rob Herring
@ 2017-05-23 15:54     ` Fabrice Gasnier
  0 siblings, 0 replies; 9+ messages in thread
From: Fabrice Gasnier @ 2017-05-23 15:54 UTC (permalink / raw)
  To: linux-arm-kernel

On 05/23/2017 05:07 PM, Rob Herring wrote:
> On Fri, May 19, 2017 at 02:45:24PM +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>
>> ---
>>  .../devicetree/bindings/iio/adc/st,stm32-adc.txt     | 20 ++++++++++++++------
>>  1 file changed, 14 insertions(+), 6 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..9519d2e 100644
>> --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
>> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
>> @@ -21,11 +21,17 @@ 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" or "st,stm32h7-adc-core".
> Reformat this to 1 per line.
Hi Rob,

I'll fix it in v2
>
>>  - 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 will be used by default if not set.
> The clocks listed should be all connections present, not policy as to 
> what clock you want to use for conversions.
Shall I only mention it's optional on stm32h7 ?
(e.g. remove "bus clock will...")

>
>> +  - "bus" clock: for registers access, common to all ADCs.
>> +    It's unused on stm32f4.
> s/unused/not present/
I'll fix it in v2

>
>> +    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 +48,16 @@ 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" or "st,stm32h7-adc".
> One per line.
I'll fix it in v2

>
>>  - 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.
> Does the h7 simply have the same parent connected to all ADC instances 
> or really doesn't have a per instance clock? For the former case, you 
> should still have a clock, but just all be the same parent.
It's the second case. There's no per instance clock.
Do you want me to rephrase a little ?
e.g. like it's required on stm32f4 and not present on stm32h7 ?

Please advise,
Thanks for reviewing,
Fabrice

>
>>  - interrupt-parent: Phandle to the parent interrupt controller.
>>  - interrupts: IRQ Line for the ADC (e.g. may be 0 for adc at 0, 1 for adc at 100 or
>>    2 for adc at 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
>>  
>> -- 
>> 1.9.1
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2017-05-23 15:54 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-05-19 12:45 [PATCH 0/5] iio: add support for STM32H7 ADC Fabrice Gasnier
2017-05-19 12:45 ` [PATCH 1/5] dt-bindings: iio: stm32-adc: add support for STM32H7 Fabrice Gasnier
2017-05-23 15:07   ` Rob Herring
2017-05-23 15:54     ` Fabrice Gasnier
2017-05-19 12:45 ` [PATCH 2/5] iio: adc: stm32: make core adc clock optional by default Fabrice Gasnier
2017-05-19 12:45 ` [PATCH 3/5] iio: adc: stm32: introduce compatible data cfg Fabrice Gasnier
2017-05-19 12:45 ` [PATCH 4/5] iio: adc: stm32: make per instance bus clock optional Fabrice Gasnier
2017-05-19 12:45 ` [PATCH 5/5] iio: adc: stm32: add support for STM32H7 Fabrice Gasnier
2017-05-20 17:42   ` Jonathan Cameron

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).