All of lore.kernel.org
 help / color / mirror / Atom feed
From: Jonathan Cameron <jic23@kernel.org>
To: Fabrice Gasnier <fabrice.gasnier@st.com>
Cc: <robh+dt@kernel.org>, <alexandre.torgue@st.com>,
	<mark.rutland@arm.com>, <mcoquelin.stm32@gmail.com>,
	<lars@metafoo.de>, <knaack.h@gmx.de>, <pmeerw@pmeerw.net>,
	<linux-iio@vger.kernel.org>, <devicetree@vger.kernel.org>,
	<linux-stm32@st-md-mailman.stormreply.com>,
	<linux-arm-kernel@lists.infradead.org>,
	<linux-kernel@vger.kernel.org>
Subject: Re: [PATCH v2 2/3] iio: adc: stm32-adc: add analog switches supply control
Date: Sun, 14 Jul 2019 17:12:05 +0100	[thread overview]
Message-ID: <20190714171205.32facd54@archlinux> (raw)
In-Reply-To: <1562148496-26789-3-git-send-email-fabrice.gasnier@st.com>

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[] = {


WARNING: multiple messages have this Message-ID (diff)
From: Jonathan Cameron <jic23@kernel.org>
To: Fabrice Gasnier <fabrice.gasnier@st.com>
Cc: mark.rutland@arm.com, devicetree@vger.kernel.org,
	lars@metafoo.de, alexandre.torgue@st.com,
	linux-iio@vger.kernel.org, pmeerw@pmeerw.net,
	linux-kernel@vger.kernel.org, robh+dt@kernel.org,
	mcoquelin.stm32@gmail.com, knaack.h@gmx.de,
	linux-stm32@st-md-mailman.stormreply.com,
	linux-arm-kernel@lists.infradead.org
Subject: Re: [PATCH v2 2/3] iio: adc: stm32-adc: add analog switches supply control
Date: Sun, 14 Jul 2019 17:12:05 +0100	[thread overview]
Message-ID: <20190714171205.32facd54@archlinux> (raw)
In-Reply-To: <1562148496-26789-3-git-send-email-fabrice.gasnier@st.com>

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[] = {


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

WARNING: multiple messages have this Message-ID (diff)
From: Jonathan Cameron <jic23@kernel.org>
To: Fabrice Gasnier <fabrice.gasnier@st.com>
Cc: robh+dt@kernel.org, alexandre.torgue@st.com,
	mark.rutland@arm.com, mcoquelin.stm32@gmail.com, lars@metafoo.de,
	knaack.h@gmx.de, pmeerw@pmeerw.net, linux-iio@vger.kernel.org,
	devicetree@vger.kernel.org,
	linux-stm32@st-md-mailman.stormreply.com,
	linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 2/3] iio: adc: stm32-adc: add analog switches supply control
Date: Sun, 14 Jul 2019 17:12:05 +0100	[thread overview]
Message-ID: <20190714171205.32facd54@archlinux> (raw)
In-Reply-To: <1562148496-26789-3-git-send-email-fabrice.gasnier@st.com>

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[] = {

  reply	other threads:[~2019-07-14 16:12 UTC|newest]

Thread overview: 27+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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-03 10:08 ` 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-03 10:08   ` Fabrice Gasnier
2019-07-14 16:11   ` Jonathan Cameron
2019-07-14 16:11     ` Jonathan Cameron
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   ` Fabrice Gasnier
2019-07-03 10:08   ` Fabrice Gasnier
2019-07-14 16:12   ` Jonathan Cameron [this message]
2019-07-14 16:12     ` Jonathan Cameron
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-03 10:08   ` Fabrice Gasnier
2019-07-03 10:08   ` Fabrice Gasnier
2019-07-14 16:13   ` Jonathan Cameron
2019-07-14 16:13     ` Jonathan Cameron
2019-07-14 16:13     ` Jonathan Cameron
2019-07-15  7:01     ` Fabrice Gasnier
2019-07-15  7:01       ` Fabrice Gasnier
2019-07-15  7:01       ` Fabrice Gasnier
2019-07-29  7:23   ` Alexandre Torgue
2019-07-29  7:23     ` Alexandre Torgue
2019-07-29  7:23     ` Alexandre Torgue

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20190714171205.32facd54@archlinux \
    --to=jic23@kernel.org \
    --cc=alexandre.torgue@st.com \
    --cc=devicetree@vger.kernel.org \
    --cc=fabrice.gasnier@st.com \
    --cc=knaack.h@gmx.de \
    --cc=lars@metafoo.de \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-stm32@st-md-mailman.stormreply.com \
    --cc=mark.rutland@arm.com \
    --cc=mcoquelin.stm32@gmail.com \
    --cc=pmeerw@pmeerw.net \
    --cc=robh+dt@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.