linux-iio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 0/3] STM32 ADC analog switches supply control
@ 2019-07-03 10:08 Fabrice Gasnier
  2019-07-03 10:08 ` [PATCH v2 1/3] dt-bindings: iio: adc: stm32: add " Fabrice Gasnier
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Fabrice Gasnier @ 2019-07-03 10:08 UTC (permalink / raw)
  To: jic23, robh+dt, alexandre.torgue
  Cc: mark.rutland, mcoquelin.stm32, lars, knaack.h, pmeerw,
	fabrice.gasnier, linux-iio, devicetree, linux-stm32,
	linux-arm-kernel, linux-kernel

This series adds support for SYSCFG bits that control ADC analog switches
supply on STM32MP1 and STM32H7.

The ADC inputs are multiplexed with analog switches which have reduced
performances when their supply is below 2.7V. Analog switches supply
can be controlled using SYSCFG bits, to reach full ADC performance.

---
Changes in v2:
- Rework since: "Add missing vdda-supply to STM32 ADC" series [1].
- Booster is handled by a regulator driver, see
  "regulator: add support for the STM32 ADC booster" series [2].
- Only enable vdd regulator when needed
- Fix typos

[1] https://lkml.org/lkml/2019/6/19/411
[2] https://lkml.org/lkml/2019/6/28/188

Fabrice Gasnier (3):
  dt-bindings: iio: adc: stm32: add analog switches supply control
  iio: adc: stm32-adc: add analog switches supply control
  ARM: dts: stm32: add syscfg to ADC on stm32mp157c

 .../devicetree/bindings/iio/adc/st,stm32-adc.txt   |   6 +
 arch/arm/boot/dts/stm32mp157c.dtsi                 |   1 +
 drivers/iio/adc/stm32-adc-core.c                   | 193 ++++++++++++++++++++-
 3 files changed, 199 insertions(+), 1 deletion(-)

-- 
2.7.4


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

* [PATCH v2 1/3] dt-bindings: iio: adc: stm32: add analog switches supply control
  2019-07-03 10:08 [PATCH v2 0/3] STM32 ADC analog switches supply control Fabrice Gasnier
@ 2019-07-03 10:08 ` Fabrice Gasnier
  2019-07-14 16:11   ` Jonathan Cameron
  2019-07-03 10:08 ` [PATCH v2 2/3] iio: adc: stm32-adc: " Fabrice Gasnier
  2019-07-03 10:08 ` [PATCH v2 3/3] ARM: dts: stm32: add syscfg to ADC on stm32mp157c Fabrice Gasnier
  2 siblings, 1 reply; 9+ messages in thread
From: Fabrice Gasnier @ 2019-07-03 10:08 UTC (permalink / raw)
  To: jic23, robh+dt, alexandre.torgue
  Cc: mark.rutland, mcoquelin.stm32, lars, knaack.h, pmeerw,
	fabrice.gasnier, linux-iio, devicetree, linux-stm32,
	linux-arm-kernel, linux-kernel

On stm32h7 and stm32mp1, the ADC inputs are multiplexed with analog
switches which have reduced performances when their supply is below 2.7V
(vdda by default). Booster or vdd can be used, to get full ADC
performances.

Add documentation for:
- optional booster-supply that can be used on stm32h7 and stm32mp1.
- optional vdd-supply that can be used on stm32mp1 to supply analog
  circuitry, selected by syscfg.
Mark these as optional, since this is a trade-off between analog
performance and power consumption.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
Changes in v2:
- Rework since: "Add missing vdda-supply to STM32 ADC" series.
- Add booster-supply regulator description
- typo: input switches
---
 Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
index 93a0bd2..4c0da8c 100644
--- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
@@ -47,6 +47,12 @@ Required properties:
 Optional properties:
 - A pinctrl state named "default" for each ADC channel may be defined to set
   inX ADC pins in mode of operation for analog input on external pin.
+- booster-supply: Phandle to the embedded booster regulator that can be used
+  to supply ADC analog input switches on stm32h7 and stm32mp1.
+- vdd-supply: Phandle to the vdd input voltage. It can be used to supply ADC
+  analog input switches on stm32mp1.
+- st,syscfg: Phandle to system configuration controller. It can be used to
+  control the analog circuitry on stm32mp1.
 
 Contents of a stm32 adc child node:
 -----------------------------------
-- 
2.7.4


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

* [PATCH v2 2/3] iio: adc: stm32-adc: add analog switches supply control
  2019-07-03 10:08 [PATCH v2 0/3] STM32 ADC analog switches supply control Fabrice Gasnier
  2019-07-03 10:08 ` [PATCH v2 1/3] dt-bindings: iio: adc: stm32: add " Fabrice Gasnier
@ 2019-07-03 10:08 ` Fabrice Gasnier
  2019-07-14 16:12   ` Jonathan Cameron
  2019-07-03 10:08 ` [PATCH v2 3/3] ARM: dts: stm32: add syscfg to ADC on stm32mp157c Fabrice Gasnier
  2 siblings, 1 reply; 9+ messages in thread
From: Fabrice Gasnier @ 2019-07-03 10:08 UTC (permalink / raw)
  To: jic23, robh+dt, alexandre.torgue
  Cc: mark.rutland, mcoquelin.stm32, lars, knaack.h, pmeerw,
	fabrice.gasnier, linux-iio, devicetree, linux-stm32,
	linux-arm-kernel, linux-kernel

On stm32h7 and stm32mp1, the ADC inputs are multiplexed with analog
switches which have reduced performances when their supply is below 2.7V
(vdda by default):
- 3.3V embedded booster can be used, to get full ADC performances
  (increases power consumption).
- vdd supply can be selected if above 2.7V by setting ANASWVDD syscfg bit,
  on STM32MP1 only.

Make this optional, since this is a trade-off between analog performance
and power consumption.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
Changes in v2:
- Only enable vdd regulator when needed.
- Rework vdda enabling since: "Add missing vdda-supply to STM32 ADC".
- Booster has been added to the regulator framework. This helps also when
  there are several ADC instances like on stm32h7 (e.g. ADC12 and ADC3), to
  benefit from the use count.
---
 drivers/iio/adc/stm32-adc-core.c | 193 ++++++++++++++++++++++++++++++++++++++-
 1 file changed, 192 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index 1f7ce51..4299cef 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -14,9 +14,11 @@
 #include <linux/irqchip/chained_irq.h>
 #include <linux/irqdesc.h>
 #include <linux/irqdomain.h>
+#include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/pm_runtime.h>
+#include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 
@@ -51,6 +53,17 @@
 
 #define STM32_ADC_CORE_SLEEP_DELAY_MS	2000
 
+/* SYSCFG registers */
+#define STM32MP1_SYSCFG_PMCSETR		0x04
+#define STM32MP1_SYSCFG_PMCCLRR		0x44
+
+/* SYSCFG bit fields */
+#define STM32MP1_SYSCFG_ANASWVDD_MASK	BIT(9)
+
+/* SYSCFG capability flags */
+#define HAS_VBOOSTER		BIT(0)
+#define HAS_ANASWVDD		BIT(1)
+
 /**
  * stm32_adc_common_regs - stm32 common registers, compatible dependent data
  * @csr:	common status register offset
@@ -74,11 +87,13 @@ struct stm32_adc_priv;
  * @regs:	common registers for all instances
  * @clk_sel:	clock selection routine
  * @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
+ * @has_syscfg: SYSCFG capability flags
  */
 struct stm32_adc_priv_cfg {
 	const struct stm32_adc_common_regs *regs;
 	int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
 	u32 max_clk_rate_hz;
+	unsigned int has_syscfg;
 };
 
 /**
@@ -87,22 +102,32 @@ struct stm32_adc_priv_cfg {
  * @domain:		irq domain reference
  * @aclk:		clock reference for the analog circuitry
  * @bclk:		bus clock common for all ADCs, depends on part used
+ * @booster:		booster supply reference
+ * @vdd:		vdd supply reference
  * @vdda:		vdda analog supply reference
  * @vref:		regulator reference
+ * @vdd_uv:		vdd supply voltage (microvolts)
+ * @vdda_uv:		vdda supply voltage (microvolts)
  * @cfg:		compatible configuration data
  * @common:		common data for all ADC instances
  * @ccr_bak:		backup CCR in low power mode
+ * @syscfg:		reference to syscon, system control registers
  */
 struct stm32_adc_priv {
 	int				irq[STM32_ADC_MAX_ADCS];
 	struct irq_domain		*domain;
 	struct clk			*aclk;
 	struct clk			*bclk;
+	struct regulator		*booster;
+	struct regulator		*vdd;
 	struct regulator		*vdda;
 	struct regulator		*vref;
+	int				vdd_uv;
+	int				vdda_uv;
 	const struct stm32_adc_priv_cfg	*cfg;
 	struct stm32_adc_common		common;
 	u32				ccr_bak;
+	struct regmap			*syscfg;
 };
 
 static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
@@ -390,6 +415,82 @@ static void stm32_adc_irq_remove(struct platform_device *pdev,
 	}
 }
 
+static int stm32_adc_core_switches_supply_en(struct stm32_adc_priv *priv,
+					     struct device *dev)
+{
+	int ret;
+
+	/*
+	 * On STM32H7 and STM32MP1, the ADC inputs are multiplexed with analog
+	 * switches (via PCSEL) which have reduced performances when their
+	 * supply is below 2.7V (vdda by default):
+	 * - Voltage booster can be used, to get full ADC performances
+	 *   (increases power consumption).
+	 * - Vdd can be used to supply them, if above 2.7V (STM32MP1 only).
+	 *
+	 * Recommended settings for ANASWVDD and EN_BOOSTER:
+	 * - vdda < 2.7V but vdd > 2.7V: ANASWVDD = 1, EN_BOOSTER = 0 (stm32mp1)
+	 * - vdda < 2.7V and vdd < 2.7V: ANASWVDD = 0, EN_BOOSTER = 1
+	 * - vdda >= 2.7V:               ANASWVDD = 0, EN_BOOSTER = 0 (default)
+	 */
+	if (priv->vdda_uv < 2700000) {
+		if (priv->syscfg && priv->vdd_uv > 2700000) {
+			ret = regulator_enable(priv->vdd);
+			if (ret < 0) {
+				dev_err(dev, "vdd enable failed %d\n", ret);
+				return ret;
+			}
+
+			ret = regmap_write(priv->syscfg,
+					   STM32MP1_SYSCFG_PMCSETR,
+					   STM32MP1_SYSCFG_ANASWVDD_MASK);
+			if (ret < 0) {
+				regulator_disable(priv->vdd);
+				dev_err(dev, "vdd select failed, %d\n", ret);
+				return ret;
+			}
+			dev_dbg(dev, "analog switches supplied by vdd\n");
+
+			return 0;
+		}
+
+		if (priv->booster) {
+			/*
+			 * This is optional, as this is a trade-off between
+			 * analog performance and power consumption.
+			 */
+			ret = regulator_enable(priv->booster);
+			if (ret < 0) {
+				dev_err(dev, "booster enable failed %d\n", ret);
+				return ret;
+			}
+			dev_dbg(dev, "analog switches supplied by booster\n");
+
+			return 0;
+		}
+	}
+
+	/* Fallback using vdda (default), nothing to do */
+	dev_dbg(dev, "analog switches supplied by vdda (%d uV)\n",
+		priv->vdda_uv);
+
+	return 0;
+}
+
+static void stm32_adc_core_switches_supply_dis(struct stm32_adc_priv *priv)
+{
+	if (priv->vdda_uv < 2700000) {
+		if (priv->syscfg && priv->vdd_uv > 2700000) {
+			regmap_write(priv->syscfg, STM32MP1_SYSCFG_PMCCLRR,
+				     STM32MP1_SYSCFG_ANASWVDD_MASK);
+			regulator_disable(priv->vdd);
+			return;
+		}
+		if (priv->booster)
+			regulator_disable(priv->booster);
+	}
+}
+
 static int stm32_adc_core_hw_start(struct device *dev)
 {
 	struct stm32_adc_common *common = dev_get_drvdata(dev);
@@ -402,10 +503,21 @@ static int stm32_adc_core_hw_start(struct device *dev)
 		return ret;
 	}
 
+	ret = regulator_get_voltage(priv->vdda);
+	if (ret < 0) {
+		dev_err(dev, "vdda get voltage failed, %d\n", ret);
+		goto err_vdda_disable;
+	}
+	priv->vdda_uv = ret;
+
+	ret = stm32_adc_core_switches_supply_en(priv, dev);
+	if (ret < 0)
+		goto err_vdda_disable;
+
 	ret = regulator_enable(priv->vref);
 	if (ret < 0) {
 		dev_err(dev, "vref enable failed\n");
-		goto err_vdda_disable;
+		goto err_switches_dis;
 	}
 
 	if (priv->bclk) {
@@ -433,6 +545,8 @@ static int stm32_adc_core_hw_start(struct device *dev)
 		clk_disable_unprepare(priv->bclk);
 err_regulator_disable:
 	regulator_disable(priv->vref);
+err_switches_dis:
+	stm32_adc_core_switches_supply_dis(priv);
 err_vdda_disable:
 	regulator_disable(priv->vdda);
 
@@ -451,9 +565,80 @@ static void stm32_adc_core_hw_stop(struct device *dev)
 	if (priv->bclk)
 		clk_disable_unprepare(priv->bclk);
 	regulator_disable(priv->vref);
+	stm32_adc_core_switches_supply_dis(priv);
 	regulator_disable(priv->vdda);
 }
 
+static int stm32_adc_core_switches_probe(struct device *dev,
+					 struct stm32_adc_priv *priv)
+{
+	struct device_node *np = dev->of_node;
+	int ret;
+
+	/* Analog switches supply can be controlled by syscfg (optional) */
+	priv->syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+	if (IS_ERR(priv->syscfg)) {
+		ret = PTR_ERR(priv->syscfg);
+		if (ret != -ENODEV) {
+			if (ret != -EPROBE_DEFER)
+				dev_err(dev, "Can't probe syscfg: %d\n", ret);
+			return ret;
+		}
+		priv->syscfg = NULL;
+	}
+
+	/* Booster can be used to supply analog switches (optional) */
+	if (priv->cfg->has_syscfg & HAS_VBOOSTER &&
+	    of_property_read_bool(np, "booster-supply")) {
+		priv->booster = devm_regulator_get_optional(dev, "booster");
+		if (IS_ERR(priv->booster)) {
+			ret = PTR_ERR(priv->booster);
+			if (ret != -ENODEV) {
+				if (ret != -EPROBE_DEFER)
+					dev_err(dev, "can't get booster %d\n",
+						ret);
+				return ret;
+			}
+			priv->booster = NULL;
+		}
+	}
+
+	/* Vdd can be used to supply analog switches (optional) */
+	if (priv->cfg->has_syscfg & HAS_ANASWVDD &&
+	    of_property_read_bool(np, "vdd-supply")) {
+		priv->vdd = devm_regulator_get_optional(dev, "vdd");
+		if (IS_ERR(priv->vdd)) {
+			ret = PTR_ERR(priv->vdd);
+			if (ret != -ENODEV) {
+				if (ret != -EPROBE_DEFER)
+					dev_err(dev, "can't get vdd %d\n", ret);
+				return ret;
+			}
+			priv->vdd = NULL;
+		}
+	}
+
+	if (priv->vdd) {
+		ret = regulator_enable(priv->vdd);
+		if (ret < 0) {
+			dev_err(dev, "vdd enable failed %d\n", ret);
+			return ret;
+		}
+
+		ret = regulator_get_voltage(priv->vdd);
+		if (ret < 0) {
+			dev_err(dev, "vdd get voltage failed %d\n", ret);
+			regulator_disable(priv->vdd);
+			return ret;
+		}
+		priv->vdd_uv = ret;
+
+		regulator_disable(priv->vdd);
+	}
+
+	return 0;
+}
+
 static int stm32_adc_probe(struct platform_device *pdev)
 {
 	struct stm32_adc_priv *priv;
@@ -514,6 +699,10 @@ static int stm32_adc_probe(struct platform_device *pdev)
 		priv->bclk = NULL;
 	}
 
+	ret = stm32_adc_core_switches_probe(dev, priv);
+	if (ret)
+		return ret;
+
 	pm_runtime_get_noresume(dev);
 	pm_runtime_set_active(dev);
 	pm_runtime_set_autosuspend_delay(dev, STM32_ADC_CORE_SLEEP_DELAY_MS);
@@ -611,12 +800,14 @@ static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
 	.regs = &stm32h7_adc_common_regs,
 	.clk_sel = stm32h7_adc_clk_sel,
 	.max_clk_rate_hz = 36000000,
+	.has_syscfg = HAS_VBOOSTER,
 };
 
 static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
 	.regs = &stm32h7_adc_common_regs,
 	.clk_sel = stm32h7_adc_clk_sel,
 	.max_clk_rate_hz = 40000000,
+	.has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD,
 };
 
 static const struct of_device_id stm32_adc_of_match[] = {
-- 
2.7.4


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

* [PATCH v2 3/3] ARM: dts: stm32: add syscfg to ADC on stm32mp157c
  2019-07-03 10:08 [PATCH v2 0/3] STM32 ADC analog switches supply control Fabrice Gasnier
  2019-07-03 10:08 ` [PATCH v2 1/3] dt-bindings: iio: adc: stm32: add " Fabrice Gasnier
  2019-07-03 10:08 ` [PATCH v2 2/3] iio: adc: stm32-adc: " Fabrice Gasnier
@ 2019-07-03 10:08 ` Fabrice Gasnier
  2019-07-14 16:13   ` Jonathan Cameron
  2019-07-29  7:23   ` Alexandre Torgue
  2 siblings, 2 replies; 9+ messages in thread
From: Fabrice Gasnier @ 2019-07-03 10:08 UTC (permalink / raw)
  To: jic23, robh+dt, alexandre.torgue
  Cc: mark.rutland, mcoquelin.stm32, lars, knaack.h, pmeerw,
	fabrice.gasnier, linux-iio, devicetree, linux-stm32,
	linux-arm-kernel, linux-kernel

On stm32mp157c, the ADC inputs are multiplexed with analog switches which
have reduced performances when their supply is below 2.7V (vdda by
default).
Add syscfg registers that can be used on stm32mp157c, to get full ADC
analog performances.

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

diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi
index 2dd5162..b9a5b58 100644
--- a/arch/arm/boot/dts/stm32mp157c.dtsi
+++ b/arch/arm/boot/dts/stm32mp157c.dtsi
@@ -862,6 +862,7 @@
 			clocks = <&rcc ADC12>, <&rcc ADC12_K>;
 			clock-names = "bus", "adc";
 			interrupt-controller;
+			st,syscfg = <&syscfg>;
 			#interrupt-cells = <1>;
 			#address-cells = <1>;
 			#size-cells = <0>;
-- 
2.7.4


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

* Re: [PATCH v2 1/3] dt-bindings: iio: adc: stm32: add analog switches supply control
  2019-07-03 10:08 ` [PATCH v2 1/3] dt-bindings: iio: adc: stm32: add " Fabrice Gasnier
@ 2019-07-14 16:11   ` Jonathan Cameron
  0 siblings, 0 replies; 9+ messages in thread
From: Jonathan Cameron @ 2019-07-14 16:11 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: robh+dt, alexandre.torgue, mark.rutland, mcoquelin.stm32, lars,
	knaack.h, pmeerw, linux-iio, devicetree, linux-stm32,
	linux-arm-kernel, linux-kernel

On Wed, 3 Jul 2019 12:08:14 +0200
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:

> On stm32h7 and stm32mp1, the ADC inputs are multiplexed with analog
> switches which have reduced performances when their supply is below 2.7V
> (vdda by default). Booster or vdd can be used, to get full ADC
> performances.
> 
> Add documentation for:
> - optional booster-supply that can be used on stm32h7 and stm32mp1.
> - optional vdd-supply that can be used on stm32mp1 to supply analog
>   circuitry, selected by syscfg.
> Mark these as optional, since this is a trade-off between analog
> performance and power consumption.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Seems fine to me. Applied to the togreg branch of iio.git and pushed out
as testing for the autobuilders to play with it.

Thanks,

Jonathan

> ---
> Changes in v2:
> - Rework since: "Add missing vdda-supply to STM32 ADC" series.
> - Add booster-supply regulator description
> - typo: input switches
> ---
>  Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
> index 93a0bd2..4c0da8c 100644
> --- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
> +++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
> @@ -47,6 +47,12 @@ Required properties:
>  Optional properties:
>  - A pinctrl state named "default" for each ADC channel may be defined to set
>    inX ADC pins in mode of operation for analog input on external pin.
> +- booster-supply: Phandle to the embedded booster regulator that can be used
> +  to supply ADC analog input switches on stm32h7 and stm32mp1.
> +- vdd-supply: Phandle to the vdd input voltage. It can be used to supply ADC
> +  analog input switches on stm32mp1.
> +- st,syscfg: Phandle to system configuration controller. It can be used to
> +  control the analog circuitry on stm32mp1.
>  
>  Contents of a stm32 adc child node:
>  -----------------------------------


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

* Re: [PATCH v2 2/3] iio: adc: stm32-adc: add analog switches supply control
  2019-07-03 10:08 ` [PATCH v2 2/3] iio: adc: stm32-adc: " Fabrice Gasnier
@ 2019-07-14 16:12   ` Jonathan Cameron
  0 siblings, 0 replies; 9+ messages in thread
From: Jonathan Cameron @ 2019-07-14 16:12 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: robh+dt, alexandre.torgue, mark.rutland, mcoquelin.stm32, lars,
	knaack.h, pmeerw, linux-iio, devicetree, linux-stm32,
	linux-arm-kernel, linux-kernel

On Wed, 3 Jul 2019 12:08:15 +0200
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:

> On stm32h7 and stm32mp1, the ADC inputs are multiplexed with analog
> switches which have reduced performances when their supply is below 2.7V
> (vdda by default):
> - 3.3V embedded booster can be used, to get full ADC performances
>   (increases power consumption).
> - vdd supply can be selected if above 2.7V by setting ANASWVDD syscfg bit,
>   on STM32MP1 only.
> 
> Make this optional, since this is a trade-off between analog performance
> and power consumption.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
Looks good to me.

Applied to the togreg branch of iio.git and pushed out as testing.

Thanks,

Jonathan

> ---
> Changes in v2:
> - Only enable vdd regulator when needed.
> - Rework vdda enabling since: "Add missing vdda-supply to STM32 ADC".
> - Booster has been added to the regulator framework. This helps also when
>   there are several ADC instances like on stm32h7 (e.g. ADC12 and ADC3), to
>   benefit from the use count.
> ---
>  drivers/iio/adc/stm32-adc-core.c | 193 ++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 192 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
> index 1f7ce51..4299cef 100644
> --- a/drivers/iio/adc/stm32-adc-core.c
> +++ b/drivers/iio/adc/stm32-adc-core.c
> @@ -14,9 +14,11 @@
>  #include <linux/irqchip/chained_irq.h>
>  #include <linux/irqdesc.h>
>  #include <linux/irqdomain.h>
> +#include <linux/mfd/syscon.h>
>  #include <linux/module.h>
>  #include <linux/of_device.h>
>  #include <linux/pm_runtime.h>
> +#include <linux/regmap.h>
>  #include <linux/regulator/consumer.h>
>  #include <linux/slab.h>
>  
> @@ -51,6 +53,17 @@
>  
>  #define STM32_ADC_CORE_SLEEP_DELAY_MS	2000
>  
> +/* SYSCFG registers */
> +#define STM32MP1_SYSCFG_PMCSETR		0x04
> +#define STM32MP1_SYSCFG_PMCCLRR		0x44
> +
> +/* SYSCFG bit fields */
> +#define STM32MP1_SYSCFG_ANASWVDD_MASK	BIT(9)
> +
> +/* SYSCFG capability flags */
> +#define HAS_VBOOSTER		BIT(0)
> +#define HAS_ANASWVDD		BIT(1)
> +
>  /**
>   * stm32_adc_common_regs - stm32 common registers, compatible dependent data
>   * @csr:	common status register offset
> @@ -74,11 +87,13 @@ struct stm32_adc_priv;
>   * @regs:	common registers for all instances
>   * @clk_sel:	clock selection routine
>   * @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
> + * @has_syscfg: SYSCFG capability flags
>   */
>  struct stm32_adc_priv_cfg {
>  	const struct stm32_adc_common_regs *regs;
>  	int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
>  	u32 max_clk_rate_hz;
> +	unsigned int has_syscfg;
>  };
>  
>  /**
> @@ -87,22 +102,32 @@ struct stm32_adc_priv_cfg {
>   * @domain:		irq domain reference
>   * @aclk:		clock reference for the analog circuitry
>   * @bclk:		bus clock common for all ADCs, depends on part used
> + * @booster:		booster supply reference
> + * @vdd:		vdd supply reference
>   * @vdda:		vdda analog supply reference
>   * @vref:		regulator reference
> + * @vdd_uv:		vdd supply voltage (microvolts)
> + * @vdda_uv:		vdda supply voltage (microvolts)
>   * @cfg:		compatible configuration data
>   * @common:		common data for all ADC instances
>   * @ccr_bak:		backup CCR in low power mode
> + * @syscfg:		reference to syscon, system control registers
>   */
>  struct stm32_adc_priv {
>  	int				irq[STM32_ADC_MAX_ADCS];
>  	struct irq_domain		*domain;
>  	struct clk			*aclk;
>  	struct clk			*bclk;
> +	struct regulator		*booster;
> +	struct regulator		*vdd;
>  	struct regulator		*vdda;
>  	struct regulator		*vref;
> +	int				vdd_uv;
> +	int				vdda_uv;
>  	const struct stm32_adc_priv_cfg	*cfg;
>  	struct stm32_adc_common		common;
>  	u32				ccr_bak;
> +	struct regmap			*syscfg;
>  };
>  
>  static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
> @@ -390,6 +415,82 @@ static void stm32_adc_irq_remove(struct platform_device *pdev,
>  	}
>  }
>  
> +static int stm32_adc_core_switches_supply_en(struct stm32_adc_priv *priv,
> +					     struct device *dev)
> +{
> +	int ret;
> +
> +	/*
> +	 * On STM32H7 and STM32MP1, the ADC inputs are multiplexed with analog
> +	 * switches (via PCSEL) which have reduced performances when their
> +	 * supply is below 2.7V (vdda by default):
> +	 * - Voltage booster can be used, to get full ADC performances
> +	 *   (increases power consumption).
> +	 * - Vdd can be used to supply them, if above 2.7V (STM32MP1 only).
> +	 *
> +	 * Recommended settings for ANASWVDD and EN_BOOSTER:
> +	 * - vdda < 2.7V but vdd > 2.7V: ANASWVDD = 1, EN_BOOSTER = 0 (stm32mp1)
> +	 * - vdda < 2.7V and vdd < 2.7V: ANASWVDD = 0, EN_BOOSTER = 1
> +	 * - vdda >= 2.7V:               ANASWVDD = 0, EN_BOOSTER = 0 (default)
> +	 */
> +	if (priv->vdda_uv < 2700000) {
> +		if (priv->syscfg && priv->vdd_uv > 2700000) {
> +			ret = regulator_enable(priv->vdd);
> +			if (ret < 0) {
> +				dev_err(dev, "vdd enable failed %d\n", ret);
> +				return ret;
> +			}
> +
> +			ret = regmap_write(priv->syscfg,
> +					   STM32MP1_SYSCFG_PMCSETR,
> +					   STM32MP1_SYSCFG_ANASWVDD_MASK);
> +			if (ret < 0) {
> +				regulator_disable(priv->vdd);
> +				dev_err(dev, "vdd select failed, %d\n", ret);
> +				return ret;
> +			}
> +			dev_dbg(dev, "analog switches supplied by vdd\n");
> +
> +			return 0;
> +		}
> +
> +		if (priv->booster) {
> +			/*
> +			 * This is optional, as this is a trade-off between
> +			 * analog performance and power consumption.
> +			 */
> +			ret = regulator_enable(priv->booster);
> +			if (ret < 0) {
> +				dev_err(dev, "booster enable failed %d\n", ret);
> +				return ret;
> +			}
> +			dev_dbg(dev, "analog switches supplied by booster\n");
> +
> +			return 0;
> +		}
> +	}
> +
> +	/* Fallback using vdda (default), nothing to do */
> +	dev_dbg(dev, "analog switches supplied by vdda (%d uV)\n",
> +		priv->vdda_uv);
> +
> +	return 0;
> +}
> +
> +static void stm32_adc_core_switches_supply_dis(struct stm32_adc_priv *priv)
> +{
> +	if (priv->vdda_uv < 2700000) {
> +		if (priv->syscfg && priv->vdd_uv > 2700000) {
> +			regmap_write(priv->syscfg, STM32MP1_SYSCFG_PMCCLRR,
> +				     STM32MP1_SYSCFG_ANASWVDD_MASK);
> +			regulator_disable(priv->vdd);
> +			return;
> +		}
> +		if (priv->booster)
> +			regulator_disable(priv->booster);
> +	}
> +}
> +
>  static int stm32_adc_core_hw_start(struct device *dev)
>  {
>  	struct stm32_adc_common *common = dev_get_drvdata(dev);
> @@ -402,10 +503,21 @@ static int stm32_adc_core_hw_start(struct device *dev)
>  		return ret;
>  	}
>  
> +	ret = regulator_get_voltage(priv->vdda);
> +	if (ret < 0) {
> +		dev_err(dev, "vdda get voltage failed, %d\n", ret);
> +		goto err_vdda_disable;
> +	}
> +	priv->vdda_uv = ret;
> +
> +	ret = stm32_adc_core_switches_supply_en(priv, dev);
> +	if (ret < 0)
> +		goto err_vdda_disable;
> +
>  	ret = regulator_enable(priv->vref);
>  	if (ret < 0) {
>  		dev_err(dev, "vref enable failed\n");
> -		goto err_vdda_disable;
> +		goto err_switches_dis;
>  	}
>  
>  	if (priv->bclk) {
> @@ -433,6 +545,8 @@ static int stm32_adc_core_hw_start(struct device *dev)
>  		clk_disable_unprepare(priv->bclk);
>  err_regulator_disable:
>  	regulator_disable(priv->vref);
> +err_switches_dis:
> +	stm32_adc_core_switches_supply_dis(priv);
>  err_vdda_disable:
>  	regulator_disable(priv->vdda);
>  
> @@ -451,9 +565,80 @@ static void stm32_adc_core_hw_stop(struct device *dev)
>  	if (priv->bclk)
>  		clk_disable_unprepare(priv->bclk);
>  	regulator_disable(priv->vref);
> +	stm32_adc_core_switches_supply_dis(priv);
>  	regulator_disable(priv->vdda);
>  }
>  
> +static int stm32_adc_core_switches_probe(struct device *dev,
> +					 struct stm32_adc_priv *priv)
> +{
> +	struct device_node *np = dev->of_node;
> +	int ret;
> +
> +	/* Analog switches supply can be controlled by syscfg (optional) */
> +	priv->syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
> +	if (IS_ERR(priv->syscfg)) {
> +		ret = PTR_ERR(priv->syscfg);
> +		if (ret != -ENODEV) {
> +			if (ret != -EPROBE_DEFER)
> +				dev_err(dev, "Can't probe syscfg: %d\n", ret);
> +			return ret;
> +		}
> +		priv->syscfg = NULL;
> +	}
> +
> +	/* Booster can be used to supply analog switches (optional) */
> +	if (priv->cfg->has_syscfg & HAS_VBOOSTER &&
> +	    of_property_read_bool(np, "booster-supply")) {
> +		priv->booster = devm_regulator_get_optional(dev, "booster");
> +		if (IS_ERR(priv->booster)) {
> +			ret = PTR_ERR(priv->booster);
> +			if (ret != -ENODEV) {
> +				if (ret != -EPROBE_DEFER)
> +					dev_err(dev, "can't get booster %d\n",
> +						ret);
> +				return ret;
> +			}
> +			priv->booster = NULL;
> +		}
> +	}
> +
> +	/* Vdd can be used to supply analog switches (optional) */
> +	if (priv->cfg->has_syscfg & HAS_ANASWVDD &&
> +	    of_property_read_bool(np, "vdd-supply")) {
> +		priv->vdd = devm_regulator_get_optional(dev, "vdd");
> +		if (IS_ERR(priv->vdd)) {
> +			ret = PTR_ERR(priv->vdd);
> +			if (ret != -ENODEV) {
> +				if (ret != -EPROBE_DEFER)
> +					dev_err(dev, "can't get vdd %d\n", ret);
> +				return ret;
> +			}
> +			priv->vdd = NULL;
> +		}
> +	}
> +
> +	if (priv->vdd) {
> +		ret = regulator_enable(priv->vdd);
> +		if (ret < 0) {
> +			dev_err(dev, "vdd enable failed %d\n", ret);
> +			return ret;
> +		}
> +
> +		ret = regulator_get_voltage(priv->vdd);
> +		if (ret < 0) {
> +			dev_err(dev, "vdd get voltage failed %d\n", ret);
> +			regulator_disable(priv->vdd);
> +			return ret;
> +		}
> +		priv->vdd_uv = ret;
> +
> +		regulator_disable(priv->vdd);
> +	}
> +
> +	return 0;
> +}
> +
>  static int stm32_adc_probe(struct platform_device *pdev)
>  {
>  	struct stm32_adc_priv *priv;
> @@ -514,6 +699,10 @@ static int stm32_adc_probe(struct platform_device *pdev)
>  		priv->bclk = NULL;
>  	}
>  
> +	ret = stm32_adc_core_switches_probe(dev, priv);
> +	if (ret)
> +		return ret;
> +
>  	pm_runtime_get_noresume(dev);
>  	pm_runtime_set_active(dev);
>  	pm_runtime_set_autosuspend_delay(dev, STM32_ADC_CORE_SLEEP_DELAY_MS);
> @@ -611,12 +800,14 @@ static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
>  	.regs = &stm32h7_adc_common_regs,
>  	.clk_sel = stm32h7_adc_clk_sel,
>  	.max_clk_rate_hz = 36000000,
> +	.has_syscfg = HAS_VBOOSTER,
>  };
>  
>  static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
>  	.regs = &stm32h7_adc_common_regs,
>  	.clk_sel = stm32h7_adc_clk_sel,
>  	.max_clk_rate_hz = 40000000,
> +	.has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD,
>  };
>  
>  static const struct of_device_id stm32_adc_of_match[] = {


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

* Re: [PATCH v2 3/3] ARM: dts: stm32: add syscfg to ADC on stm32mp157c
  2019-07-03 10:08 ` [PATCH v2 3/3] ARM: dts: stm32: add syscfg to ADC on stm32mp157c Fabrice Gasnier
@ 2019-07-14 16:13   ` Jonathan Cameron
  2019-07-15  7:01     ` Fabrice Gasnier
  2019-07-29  7:23   ` Alexandre Torgue
  1 sibling, 1 reply; 9+ messages in thread
From: Jonathan Cameron @ 2019-07-14 16:13 UTC (permalink / raw)
  To: Fabrice Gasnier
  Cc: robh+dt, alexandre.torgue, mark.rutland, mcoquelin.stm32, lars,
	knaack.h, pmeerw, linux-iio, devicetree, linux-stm32,
	linux-arm-kernel, linux-kernel

On Wed, 3 Jul 2019 12:08:16 +0200
Fabrice Gasnier <fabrice.gasnier@st.com> wrote:

> On stm32mp157c, the ADC inputs are multiplexed with analog switches which
> have reduced performances when their supply is below 2.7V (vdda by
> default).
> Add syscfg registers that can be used on stm32mp157c, to get full ADC
> analog performances.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
I've applied the patches using this on the assumption this will go via
the usual SoC route.

Thanks,

Jonathan

> ---
>  arch/arm/boot/dts/stm32mp157c.dtsi | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi
> index 2dd5162..b9a5b58 100644
> --- a/arch/arm/boot/dts/stm32mp157c.dtsi
> +++ b/arch/arm/boot/dts/stm32mp157c.dtsi
> @@ -862,6 +862,7 @@
>  			clocks = <&rcc ADC12>, <&rcc ADC12_K>;
>  			clock-names = "bus", "adc";
>  			interrupt-controller;
> +			st,syscfg = <&syscfg>;
>  			#interrupt-cells = <1>;
>  			#address-cells = <1>;
>  			#size-cells = <0>;


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

* Re: [PATCH v2 3/3] ARM: dts: stm32: add syscfg to ADC on stm32mp157c
  2019-07-14 16:13   ` Jonathan Cameron
@ 2019-07-15  7:01     ` Fabrice Gasnier
  0 siblings, 0 replies; 9+ messages in thread
From: Fabrice Gasnier @ 2019-07-15  7:01 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: robh+dt, alexandre.torgue, mark.rutland, mcoquelin.stm32, lars,
	knaack.h, pmeerw, linux-iio, devicetree, linux-stm32,
	linux-arm-kernel, linux-kernel

On 7/14/19 6:13 PM, Jonathan Cameron wrote:
> On Wed, 3 Jul 2019 12:08:16 +0200
> Fabrice Gasnier <fabrice.gasnier@st.com> wrote:
> 
>> On stm32mp157c, the ADC inputs are multiplexed with analog switches which
>> have reduced performances when their supply is below 2.7V (vdda by
>> default).
>> Add syscfg registers that can be used on stm32mp157c, to get full ADC
>> analog performances.
>>
>> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> I've applied the patches using this on the assumption this will go via
> the usual SoC route.
> 
> Thanks,

Hi Jonathan,

Many thanks,
Fabrice

> 
> Jonathan
> 
>> ---
>>  arch/arm/boot/dts/stm32mp157c.dtsi | 1 +
>>  1 file changed, 1 insertion(+)
>>
>> diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi
>> index 2dd5162..b9a5b58 100644
>> --- a/arch/arm/boot/dts/stm32mp157c.dtsi
>> +++ b/arch/arm/boot/dts/stm32mp157c.dtsi
>> @@ -862,6 +862,7 @@
>>  			clocks = <&rcc ADC12>, <&rcc ADC12_K>;
>>  			clock-names = "bus", "adc";
>>  			interrupt-controller;
>> +			st,syscfg = <&syscfg>;
>>  			#interrupt-cells = <1>;
>>  			#address-cells = <1>;
>>  			#size-cells = <0>;
> 

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

* Re: [PATCH v2 3/3] ARM: dts: stm32: add syscfg to ADC on stm32mp157c
  2019-07-03 10:08 ` [PATCH v2 3/3] ARM: dts: stm32: add syscfg to ADC on stm32mp157c Fabrice Gasnier
  2019-07-14 16:13   ` Jonathan Cameron
@ 2019-07-29  7:23   ` Alexandre Torgue
  1 sibling, 0 replies; 9+ messages in thread
From: Alexandre Torgue @ 2019-07-29  7:23 UTC (permalink / raw)
  To: Fabrice Gasnier, jic23, robh+dt
  Cc: mark.rutland, mcoquelin.stm32, lars, knaack.h, pmeerw, linux-iio,
	devicetree, linux-stm32, linux-arm-kernel, linux-kernel

Hi fabrice

On 7/3/19 12:08 PM, Fabrice Gasnier wrote:
> On stm32mp157c, the ADC inputs are multiplexed with analog switches which
> have reduced performances when their supply is below 2.7V (vdda by
> default).
> Add syscfg registers that can be used on stm32mp157c, to get full ADC
> analog performances.
> 
> Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
> ---
>   arch/arm/boot/dts/stm32mp157c.dtsi | 1 +
>   1 file changed, 1 insertion(+)
> 
> diff --git a/arch/arm/boot/dts/stm32mp157c.dtsi b/arch/arm/boot/dts/stm32mp157c.dtsi
> index 2dd5162..b9a5b58 100644
> --- a/arch/arm/boot/dts/stm32mp157c.dtsi
> +++ b/arch/arm/boot/dts/stm32mp157c.dtsi
> @@ -862,6 +862,7 @@
>   			clocks = <&rcc ADC12>, <&rcc ADC12_K>;
>   			clock-names = "bus", "adc";
>   			interrupt-controller;
> +			st,syscfg = <&syscfg>;
>   			#interrupt-cells = <1>;
>   			#address-cells = <1>;
>   			#size-cells = <0>;
> 


Applied on stm32-next.

Thanks.
Alex

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

end of thread, other threads:[~2019-07-29  7:23 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-03 10:08 [PATCH v2 0/3] STM32 ADC analog switches supply control Fabrice Gasnier
2019-07-03 10:08 ` [PATCH v2 1/3] dt-bindings: iio: adc: stm32: add " Fabrice Gasnier
2019-07-14 16:11   ` Jonathan Cameron
2019-07-03 10:08 ` [PATCH v2 2/3] iio: adc: stm32-adc: " Fabrice Gasnier
2019-07-14 16:12   ` Jonathan Cameron
2019-07-03 10:08 ` [PATCH v2 3/3] ARM: dts: stm32: add syscfg to ADC on stm32mp157c Fabrice Gasnier
2019-07-14 16:13   ` Jonathan Cameron
2019-07-15  7:01     ` Fabrice Gasnier
2019-07-29  7:23   ` Alexandre Torgue

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