From 72179a889501ef339df094abe108846b19e7a06c Mon Sep 17 00:00:00 2001
From: Christophe Priouzeau <christophe.priouzeau@st.com>
Date: Mon, 26 Nov 2018 14:46:26 +0100
Subject: [PATCH 29/52] ARM-stm32mp1-r0-rc2-HWSPINLOCK-IIO-I2C

---
 .../devicetree/bindings/iio/adc/st,stm32-adc.txt   |  34 +++
 arch/arm/Kconfig.debug                             |  27 ++
 drivers/hwspinlock/Kconfig                         |   9 +
 drivers/hwspinlock/Makefile                        |   1 +
 drivers/hwspinlock/stm32_hwspinlock.c              | 157 +++++++++++
 drivers/i2c/busses/i2c-stm32f7.c                   |   6 +-
 drivers/iio/adc/stm32-adc-core.c                   | 296 ++++++++++++++++++++-
 drivers/iio/adc/stm32-adc.c                        |   4 +-
 8 files changed, 529 insertions(+), 5 deletions(-)
 create mode 100644 drivers/hwspinlock/stm32_hwspinlock.c

diff --git a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
index c46598c..a6aa796 100644
--- a/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
+++ b/Documentation/devicetree/bindings/iio/adc/st,stm32-adc.txt
@@ -49,6 +49,40 @@ Optional properties:
 - st,max-clk-rate-hz: Allow to specify desired max clock rate used by analog
   circuitry.
 
+- vdda-supply: Phandle to the vdda input voltage. It can be used to supply ADC
+  analog inputs switches on stm32mp1 and stm32h7.
+
+- vdd-supply: Phandle to the vdd input voltage. It can be used to supply ADC
+  analog inputs switches on stm32mp1.
+
+- st,syscfg-vbooster: Voltage booster control for analog switches supply.
+  This is available on stm32mp1 and stm32h7 (requires vdda-supply property).
+  It must be composed of 3 cells:
+	1st cell: phandle to syscfg
+	2nd cell: register offset within SYSCFG
+	3rd cell: bitmask for BOOSTE on stm32h7, EN_BOOSTER set bit on stm32mp1
+
+- st,syscfg-vbooster-clr: Voltage booster clear for analog switches supply.
+  This is available on stm32mp1 (requires st,syscfg-vbooster and vdda-supply).
+	1st cell: phandle to syscfg
+	2nd cell: clear register offset within SYSCFG
+	3rd cell: bitmask for EN_BOOSTER clear bit on stm32mp1
+
+- st,syscfg-anaswvdd: VDDA / VDD selection for analog switches supply.
+  This is available on stm32mp1 (requires vdda-supply and vdd-supply).
+  It must be composed of 3 cells:
+	1st cell: phandle to syscfg
+	2nd cell: register offset within SYSCFG
+	3rd cell: bitmask for ANASWVDD set bit
+
+- st,syscfg-anaswvdd-clr: VDDA / VDD selection clear for analog switches supply.
+  This is available on stm32mp1 (requires st,syscfg-anaswvdd, vdda-supply and
+  vdd-supply).
+  It must be composed of 3 cells:
+	1st cell: phandle to syscfg
+	2nd cell: clear register offset within SYSCFG
+	3rd cell: bitmask for ANASWVDD clear bit
+
 Contents of a stm32 adc child node:
 -----------------------------------
 An ADC block node should contain at least one subnode, representing an
diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug
index f6fcb8a..d5ec6c3 100644
--- a/arch/arm/Kconfig.debug
+++ b/arch/arm/Kconfig.debug
@@ -1184,6 +1184,28 @@ choice
 
 		  If unsure, say N.
 
+	config STM32F4_DEBUG_UART
+		bool "Use STM32F4 UART for low-level debug"
+		depends on ARCH_STM32
+		select DEBUG_STM32_UART
+		help
+		  Say Y here if you want kernel low-level debugging support
+		  on STM32F4 based platforms, which default UART is wired on
+		  USART1.
+
+		  If unsure, say N.
+
+	config STM32F7_DEBUG_UART
+		bool "Use STM32F7 UART for low-level debug"
+		depends on ARCH_STM32
+		select DEBUG_STM32_UART
+		help
+		  Say Y here if you want kernel low-level debugging support
+		  on STM32F7 based platforms, which default UART is wired on
+		  USART1.
+
+		  If unsure, say N.
+
 	config TEGRA_DEBUG_UART_AUTO_ODMDATA
 		bool "Kernel low-level debugging messages via Tegra UART via ODMDATA"
 		depends on ARCH_TEGRA
@@ -1468,6 +1490,10 @@ config DEBUG_STI_UART
 	bool
 	depends on ARCH_STI
 
+config DEBUG_STM32_UART
+	bool
+	depends on ARCH_STM32
+
 config DEBUG_SIRFSOC_UART
 	bool
 	depends on ARCH_SIRF
@@ -1517,6 +1543,7 @@ config DEBUG_LL_INCLUDE
 	default "debug/s5pv210.S" if DEBUG_S5PV210_UART
 	default "debug/sirf.S" if DEBUG_SIRFSOC_UART
 	default "debug/sti.S" if DEBUG_STI_UART
+	default "debug/stm32.S" if DEBUG_STM32_UART
 	default "debug/tegra.S" if DEBUG_TEGRA_UART
 	default "debug/ux500.S" if DEBUG_UX500_UART
 	default "debug/vexpress.S" if DEBUG_VEXPRESS_UART0_DETECT
diff --git a/drivers/hwspinlock/Kconfig b/drivers/hwspinlock/Kconfig
index e895d29..7869c67 100644
--- a/drivers/hwspinlock/Kconfig
+++ b/drivers/hwspinlock/Kconfig
@@ -49,6 +49,15 @@ config HWSPINLOCK_SPRD
 
 	  If unsure, say N.
 
+config HWSPINLOCK_STM32
+	tristate "STM32 Hardware Spinlock device"
+	depends on MACH_STM32MP157
+	depends on HWSPINLOCK
+	help
+	  Say y here to support the STM32 Hardware Spinlock device.
+
+	  If unsure, say N.
+
 config HSEM_U8500
 	tristate "STE Hardware Semaphore functionality"
 	depends on HWSPINLOCK
diff --git a/drivers/hwspinlock/Makefile b/drivers/hwspinlock/Makefile
index b87c01a..ed053e3 100644
--- a/drivers/hwspinlock/Makefile
+++ b/drivers/hwspinlock/Makefile
@@ -8,4 +8,5 @@ obj-$(CONFIG_HWSPINLOCK_OMAP)		+= omap_hwspinlock.o
 obj-$(CONFIG_HWSPINLOCK_QCOM)		+= qcom_hwspinlock.o
 obj-$(CONFIG_HWSPINLOCK_SIRF)		+= sirf_hwspinlock.o
 obj-$(CONFIG_HWSPINLOCK_SPRD)		+= sprd_hwspinlock.o
+obj-$(CONFIG_HWSPINLOCK_STM32)		+= stm32_hwspinlock.o
 obj-$(CONFIG_HSEM_U8500)		+= u8500_hsem.o
diff --git a/drivers/hwspinlock/stm32_hwspinlock.c b/drivers/hwspinlock/stm32_hwspinlock.c
new file mode 100644
index 0000000..3242b72
--- /dev/null
+++ b/drivers/hwspinlock/stm32_hwspinlock.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics SA 2018
+ * Author: Benjamin Gaignard <benjamin.gaignard@st.com> for STMicroelectronics.
+ */
+
+#include <linux/clk.h>
+#include <linux/hwspinlock.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+
+#include "hwspinlock_internal.h"
+
+#define STM32_MUTEX_COREID	BIT(8)
+#define STM32_MUTEX_LOCK_BIT	BIT(31)
+#define STM32_MUTEX_NUM_LOCKS	32
+
+struct stm32_hwspinlock {
+	struct clk *clk;
+	struct hwspinlock_device bank;
+};
+
+static int stm32_hwspinlock_trylock(struct hwspinlock *lock)
+{
+	void __iomem *lock_addr = lock->priv;
+	u32 status;
+
+	writel(STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID, lock_addr);
+	status = readl(lock_addr);
+
+	return status == (STM32_MUTEX_LOCK_BIT | STM32_MUTEX_COREID);
+}
+
+static void stm32_hwspinlock_unlock(struct hwspinlock *lock)
+{
+	void __iomem *lock_addr = lock->priv;
+
+	writel(STM32_MUTEX_COREID, lock_addr);
+}
+
+static const struct hwspinlock_ops stm32_hwspinlock_ops = {
+	.trylock	= stm32_hwspinlock_trylock,
+	.unlock		= stm32_hwspinlock_unlock,
+};
+
+static int stm32_hwspinlock_probe(struct platform_device *pdev)
+{
+	struct stm32_hwspinlock *hw;
+	void __iomem *io_base;
+	struct resource *res;
+	size_t array_size;
+	int i, ret;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	io_base = devm_ioremap_resource(&pdev->dev, res);
+	if (!io_base)
+		return -ENOMEM;
+
+	array_size = STM32_MUTEX_NUM_LOCKS * sizeof(struct hwspinlock);
+	hw = devm_kzalloc(&pdev->dev, sizeof(*hw) + array_size, GFP_KERNEL);
+	if (!hw)
+		return -ENOMEM;
+
+	hw->clk = devm_clk_get(&pdev->dev, "hsem");
+	if (IS_ERR(hw->clk))
+		return PTR_ERR(hw->clk);
+
+	for (i = 0; i < STM32_MUTEX_NUM_LOCKS; i++)
+		hw->bank.lock[i].priv = io_base + i * sizeof(u32);
+
+	platform_set_drvdata(pdev, hw);
+	pm_runtime_enable(&pdev->dev);
+
+	ret = hwspin_lock_register(&hw->bank, &pdev->dev, &stm32_hwspinlock_ops,
+				   0, STM32_MUTEX_NUM_LOCKS);
+
+	if (ret)
+		pm_runtime_disable(&pdev->dev);
+
+	return ret;
+}
+
+static int stm32_hwspinlock_remove(struct platform_device *pdev)
+{
+	struct stm32_hwspinlock *hw = platform_get_drvdata(pdev);
+	int ret;
+
+	ret = hwspin_lock_unregister(&hw->bank);
+	if (ret)
+		dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret);
+
+	pm_runtime_disable(&pdev->dev);
+
+	return 0;
+}
+
+static int __maybe_unused stm32_hwspinlock_runtime_suspend(struct device *dev)
+{
+	struct stm32_hwspinlock *hw = dev_get_drvdata(dev);
+
+	clk_disable_unprepare(hw->clk);
+
+	return 0;
+}
+
+static int __maybe_unused stm32_hwspinlock_runtime_resume(struct device *dev)
+{
+	struct stm32_hwspinlock *hw = dev_get_drvdata(dev);
+
+	clk_prepare_enable(hw->clk);
+
+	return 0;
+}
+
+static const struct dev_pm_ops stm32_hwspinlock_pm_ops = {
+	SET_RUNTIME_PM_OPS(stm32_hwspinlock_runtime_suspend,
+			   stm32_hwspinlock_runtime_resume,
+			   NULL)
+};
+
+static const struct of_device_id stm32_hwpinlock_ids[] = {
+	{ .compatible = "st,stm32-hwspinlock", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, stm32_hwpinlock_ids);
+
+static struct platform_driver stm32_hwspinlock_driver = {
+	.probe		= stm32_hwspinlock_probe,
+	.remove		= stm32_hwspinlock_remove,
+	.driver		= {
+		.name	= "stm32_hwspinlock",
+		.of_match_table = stm32_hwpinlock_ids,
+		.pm	= &stm32_hwspinlock_pm_ops,
+	},
+};
+
+static int __init stm32_hwspinlock_init(void)
+{
+	return platform_driver_register(&stm32_hwspinlock_driver);
+}
+
+/* board init code might need to reserve hwspinlocks for predefined purposes */
+postcore_initcall(stm32_hwspinlock_init);
+
+static void __exit stm32_hwspinlock_exit(void)
+{
+	platform_driver_unregister(&stm32_hwspinlock_driver);
+}
+module_exit(stm32_hwspinlock_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Hardware spinlock driver for STM32 SoCs");
+MODULE_AUTHOR("Benjamin Gaignard <benjamin.gaignard@st.com>");
diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c
index c1cbf93..ac30aea 100644
--- a/drivers/i2c/busses/i2c-stm32f7.c
+++ b/drivers/i2c/busses/i2c-stm32f7.c
@@ -457,7 +457,7 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
 		 STM32F7_I2C_ANALOG_FILTER_DELAY_MAX : 0);
 	dnf_delay = setup->dnf * i2cclk;
 
-	sdadel_min = setup->fall_time - i2c_specs[setup->speed].hddat_min -
+	sdadel_min = i2c_specs[setup->speed].hddat_min + setup->fall_time -
 		af_delay_min - (setup->dnf + 3) * i2cclk;
 
 	sdadel_max = i2c_specs[setup->speed].vddat_max - setup->rise_time -
@@ -501,8 +501,12 @@ static int stm32f7_i2c_compute_timing(struct stm32f7_i2c_dev *i2c_dev,
 
 					list_add_tail(&v->node,
 						      &solutions);
+					break;
 				}
 			}
+
+			if (p_prev == p)
+				break;
 		}
 	}
 
diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index a32e826..6234456 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -16,10 +16,12 @@
 #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/of_irq.h>
 #include <linux/pm_runtime.h>
+#include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 
@@ -88,6 +90,8 @@
 #define STM32H7_CKMODE_SHIFT		16
 #define STM32H7_CKMODE_MASK		GENMASK(17, 16)
 
+#define STM32_ADC_CORE_SLEEP_DELAY_MS	2000
+
 /**
  * stm32_adc_common_regs - stm32 common registers, compatible dependent data
  * @csr:	common status register offset
@@ -117,16 +121,30 @@ 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_clr: analog switch control use set and clear registers
  * @exti_trigs	EXTI triggers info
  */
 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;
+	int has_syscfg_clr;
 	struct stm32_adc_trig_info *exti_trigs;
 };
 
 /**
+ * stm32_adc_syscfg - stm32 ADC SYSCFG data
+ * @regmap:	reference to syscon
+ * @reg:	register offset within SYSCFG
+ * @mask:	bitmask within SYSCFG register
+ */
+struct stm32_adc_syscfg {
+	struct regmap *regmap;
+	u32 reg;
+	u32 mask;
+};
+
+/**
  * struct stm32_adc_priv - stm32 ADC core private data
  * @irq:		irq(s) for ADC block
  * @domain:		irq domain reference
@@ -137,6 +155,10 @@ struct stm32_adc_priv_cfg {
  * @cfg:		compatible configuration data
  * @common:		common data for all ADC instances
  * @ccr_bak:		backup'ed CCR in low power mode
+ * @vbooster:		BOOSTE syscfg / EN_BOOSTER syscfg set
+ * @vbooster_clr:	EN_BOOSTER syscfg clear
+ * @anaswvdd:		ANASWVDD syscfg set
+ * @anaswvdd_clr:	ANASWVDD syscfg clear
  */
 struct stm32_adc_priv {
 	int				irq[STM32_ADC_MAX_ADCS];
@@ -144,10 +166,16 @@ struct stm32_adc_priv {
 	struct clk			*aclk;
 	struct clk			*bclk;
 	u32				max_clk_rate;
+	struct regulator		*vdd;
+	struct regulator		*vdda;
 	struct regulator		*vref;
 	const struct stm32_adc_priv_cfg	*cfg;
 	struct stm32_adc_common		common;
 	u32				ccr_bak;
+	struct stm32_adc_syscfg		vbooster;
+	struct stm32_adc_syscfg		vbooster_clr;
+	struct stm32_adc_syscfg		anaswvdd;
+	struct stm32_adc_syscfg		anaswvdd_clr;
 };
 
 static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
@@ -567,16 +595,187 @@ static int stm32_adc_triggers_probe(struct platform_device *pdev,
 	return 0;
 }
 
+static int stm32_adc_switches_supply_en(struct device *dev)
+{
+	struct stm32_adc_common *common = dev_get_drvdata(dev);
+	struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+	int ret, vdda, vdd = 0;
+	u32 anaswvdd, en_booster;
+
+	/*
+	 * On STM32H7 and STM32MP1, the ADC inputs are multiplexed with analog
+	 * switches (e.g. 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 if above 2.7V (STM32MP1 only).
+	 *
+	 * Make all this optional, since this is a trade-off between analog
+	 * performance and power consumption.
+	 */
+	if (IS_ERR(priv->vdda) || IS_ERR(priv->vbooster.regmap)) {
+		dev_dbg(dev, "%s: nothing to do\n", __func__);
+		return 0;
+	}
+
+	ret = regulator_enable(priv->vdda);
+	if (ret < 0) {
+		dev_err(dev, "vdda enable failed %d\n", ret);
+		return ret;
+	}
+
+	ret = regulator_get_voltage(priv->vdda);
+	if (ret < 0) {
+		dev_err(dev, "vdda get voltage failed %d\n", ret);
+		goto vdda_dis;
+	}
+	vdda = ret;
+
+	if (!IS_ERR(priv->vdd) && !IS_ERR(priv->anaswvdd.regmap)) {
+		ret = regulator_enable(priv->vdd);
+		if (ret < 0) {
+			dev_err(dev, "vdd enable failed %d\n", ret);
+			goto vdda_dis;
+		}
+
+		ret = regulator_get_voltage(priv->vdd);
+		if (ret < 0) {
+			dev_err(dev, "vdd get voltage failed %d\n", ret);
+			goto vdd_dis;
+		}
+		vdd = ret;
+	}
+
+	/*
+	 * Recommended settings for ANASWVDD and EN_BOOSTER:
+	 * - vdda > 2.7V:                ANASWVDD = 0, EN_BOOSTER = 0
+	 * - vdda < 2.7V and vdd < 2.7V: ANASWVDD = 0, EN_BOOSTER = 1
+	 * - vdda < 2.7V but vdd > 2.7V: ANASWVDD = 1, EN_BOOSTER = 0 (stm32mp1)
+	 */
+	if (vdda > 2700000) {
+		/* analog switches supplied by vdda (default) */
+		anaswvdd = 0;
+		en_booster = 0;
+	} else {
+		if (vdd < 2700000) {
+			/* Voltage booster enabled */
+			anaswvdd = 0;
+			en_booster = priv->vbooster.mask;
+		} else {
+			/* analog switches supplied by vdd */
+			anaswvdd = priv->anaswvdd.mask;
+			en_booster = 0;
+		}
+	}
+
+	dev_dbg(dev, "vdda=%d, vdd=%d, setting: en_booster=%x, anaswvdd=%x\n",
+		vdda, vdd, en_booster, anaswvdd);
+
+	/* direct write en_booster value (or use clear register) */
+	if (en_booster || IS_ERR(priv->vbooster_clr.regmap))
+		ret = regmap_update_bits(priv->vbooster.regmap,
+					 priv->vbooster.reg,
+					 priv->vbooster.mask, en_booster);
+	else
+		ret = regmap_update_bits(priv->vbooster_clr.regmap,
+					 priv->vbooster_clr.reg,
+					 priv->vbooster_clr.mask,
+					 priv->vbooster_clr.mask);
+	if (ret) {
+		dev_err(dev, "can't access voltage booster, %d\n", ret);
+		goto vdd_dis;
+	}
+
+	/* Booster voltage can take up to 50 μs to stabilize */
+	if (en_booster)
+		usleep_range(50, 100);
+
+	if (!IS_ERR(priv->anaswvdd.regmap)) {
+		/* direct write anaswvdd value (or use clear register) */
+		if (anaswvdd || IS_ERR(priv->anaswvdd_clr.regmap))
+			ret = regmap_update_bits(priv->anaswvdd.regmap,
+						 priv->anaswvdd.reg,
+						 priv->anaswvdd.mask, anaswvdd);
+		else
+			ret = regmap_update_bits(priv->anaswvdd_clr.regmap,
+						 priv->anaswvdd_clr.reg,
+						 priv->anaswvdd_clr.mask,
+						 priv->anaswvdd_clr.mask);
+		if (ret) {
+			dev_err(dev, "can't access anaswvdd, %d\n", ret);
+			goto booster_dis;
+		}
+	}
+
+	return ret;
+
+booster_dis:
+	if (IS_ERR(priv->vbooster_clr.regmap))
+		regmap_update_bits(priv->vbooster.regmap, priv->vbooster.reg,
+				   priv->vbooster.mask, 0);
+	else
+		regmap_update_bits(priv->vbooster_clr.regmap,
+				   priv->vbooster_clr.reg,
+				   priv->vbooster_clr.mask,
+				   priv->vbooster_clr.mask);
+vdd_dis:
+	if (!IS_ERR(priv->vdd) && !IS_ERR(priv->anaswvdd.regmap))
+		regulator_disable(priv->vdd);
+vdda_dis:
+	regulator_disable(priv->vdda);
+
+	return ret;
+}
+
+static void stm32_adc_switches_supply_dis(struct device *dev)
+{
+	struct stm32_adc_common *common = dev_get_drvdata(dev);
+	struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+
+	if (IS_ERR(priv->vdda) || IS_ERR(priv->vbooster.regmap))
+		return;
+
+	if (!IS_ERR(priv->anaswvdd.regmap)) {
+		if (IS_ERR(priv->anaswvdd_clr.regmap))
+			regmap_update_bits(priv->anaswvdd.regmap,
+					   priv->anaswvdd.reg,
+					   priv->anaswvdd.mask, 0);
+		else
+			regmap_update_bits(priv->anaswvdd_clr.regmap,
+					   priv->anaswvdd_clr.reg,
+					   priv->anaswvdd_clr.mask,
+					   priv->anaswvdd_clr.mask);
+	}
+
+	if (IS_ERR(priv->vbooster_clr.regmap))
+		regmap_update_bits(priv->vbooster.regmap, priv->vbooster.reg,
+				   priv->vbooster.mask, 0);
+	else
+		regmap_update_bits(priv->vbooster_clr.regmap,
+				   priv->vbooster_clr.reg,
+				   priv->vbooster_clr.mask,
+				   priv->vbooster_clr.mask);
+
+	if (!IS_ERR(priv->vdd) && !IS_ERR(priv->anaswvdd.regmap))
+		regulator_disable(priv->vdd);
+
+	regulator_disable(priv->vdda);
+}
+
 static int stm32_adc_core_hw_start(struct device *dev)
 {
 	struct stm32_adc_common *common = dev_get_drvdata(dev);
 	struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
 	int ret;
 
+	ret = stm32_adc_switches_supply_en(dev);
+	if (ret < 0)
+		return ret;
+
 	ret = regulator_enable(priv->vref);
 	if (ret < 0) {
 		dev_err(dev, "vref enable failed\n");
-		return ret;
+		goto err_switches_disable;
 	}
 
 	if (priv->bclk) {
@@ -604,6 +803,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_disable:
+	stm32_adc_switches_supply_dis(dev);
 
 	return ret;
 }
@@ -620,6 +821,68 @@ static void stm32_adc_core_hw_stop(struct device *dev)
 	if (priv->bclk)
 		clk_disable_unprepare(priv->bclk);
 	regulator_disable(priv->vref);
+	stm32_adc_switches_supply_dis(dev);
+}
+
+static int stm32_adc_get_syscfg_cell(struct device_node *np,
+				     struct stm32_adc_syscfg *syscfg,
+				     const char * prop)
+{
+	int ret;
+
+	syscfg->regmap = syscon_regmap_lookup_by_phandle(np, prop);
+	if (IS_ERR(syscfg->regmap)) {
+		pr_debug("FGA: %s %ld\n", prop, PTR_ERR(syscfg->regmap));
+		/* Optional */
+		if (PTR_ERR(syscfg->regmap) == -ENODEV)
+			return 0;
+		else
+			return PTR_ERR(syscfg->regmap);
+	}
+
+	ret = of_property_read_u32_index(np, prop, 1, &syscfg->reg);
+	if (ret)
+		return ret;
+
+	return of_property_read_u32_index(np, prop, 2, &syscfg->mask);
+}
+
+static int stm32_adc_syscfg_probe(struct platform_device *pdev,
+				  struct stm32_adc_priv *priv)
+{
+	struct device_node *np = pdev->dev.of_node;
+	int ret;
+
+	/* Start to lookup BOOSTE/EN_BOOSTER first, for stm32h7/stm32mp1 */
+	ret = stm32_adc_get_syscfg_cell(np, &priv->vbooster,
+					"st,syscfg-vbooster");
+	if (ret)
+		return ret;
+
+	/* Continue with stm32mp1 EN_BOOSTER/ANASWVDD set and clear bits*/
+	ret = stm32_adc_get_syscfg_cell(np, &priv->vbooster_clr,
+					"st,syscfg-vbooster-clr");
+	if (ret)
+		return ret;
+
+	ret = stm32_adc_get_syscfg_cell(np, &priv->anaswvdd,
+					"st,syscfg-anaswvdd");
+	if (ret)
+		return ret;
+
+	ret = stm32_adc_get_syscfg_cell(np, &priv->anaswvdd_clr,
+					 "st,syscfg-anaswvdd-clr");
+	if (ret)
+		return ret;
+
+	/* Sanity, check syscfg set/clear pairs are filled in */
+	if (priv->cfg->has_syscfg_clr && ((!IS_ERR(priv->vbooster.regmap) &&
+					  IS_ERR(priv->vbooster_clr.regmap)) ||
+					  (!IS_ERR(priv->anaswvdd.regmap) &&
+					  IS_ERR(priv->anaswvdd_clr.regmap))))
+		return -EINVAL;
+
+	return ret;
 }
 
 static int stm32_adc_probe(struct platform_device *pdev)
@@ -657,6 +920,24 @@ static int stm32_adc_probe(struct platform_device *pdev)
 		return ret;
 	}
 
+	priv->vdda = devm_regulator_get_optional(&pdev->dev, "vdda");
+	if (IS_ERR(priv->vdda)) {
+		ret = PTR_ERR(priv->vdda);
+		if (ret != -ENODEV) {
+			dev_err(&pdev->dev, "vdda get failed, %d\n", ret);
+			return ret;
+		}
+	}
+
+	priv->vdd = devm_regulator_get_optional(&pdev->dev, "vdd");
+	if (IS_ERR(priv->vdd)) {
+		ret = PTR_ERR(priv->vdd);
+		if (ret != -ENODEV) {
+			dev_err(&pdev->dev, "vdd get failed, %d\n", ret);
+			return ret;
+		}
+	}
+
 	priv->aclk = devm_clk_get(&pdev->dev, "adc");
 	if (IS_ERR(priv->aclk)) {
 		ret = PTR_ERR(priv->aclk);
@@ -677,8 +958,17 @@ static int stm32_adc_probe(struct platform_device *pdev)
 		priv->bclk = NULL;
 	}
 
+	ret = stm32_adc_syscfg_probe(pdev, priv);
+	if (ret) {
+		if (ret != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "Can't probe syscfg: %d\n", 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);
+	pm_runtime_use_autosuspend(dev);
 	pm_runtime_enable(dev);
 
 	ret = stm32_adc_core_hw_start(dev);
@@ -718,7 +1008,8 @@ static int stm32_adc_probe(struct platform_device *pdev)
 		goto err_irq_remove;
 	}
 
-	pm_runtime_put(dev);
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_put_autosuspend(dev);
 
 	return 0;
 
@@ -789,6 +1080,7 @@ static const struct stm32_adc_priv_cfg stm32h7_adc_priv_cfg = {
 static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
 	.regs = &stm32h7_adc_common_regs,
 	.clk_sel = stm32h7_adc_clk_sel,
+	.has_syscfg_clr = true,
 	.max_clk_rate_hz = 40000000,
 	.exti_trigs = stm32h7_adc_exti_trigs,
 };
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 980355e..13d2e3c 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -201,7 +201,7 @@ enum stm32h7_adc_dmngt {
 #define STM32_ADC_MAX_SMP		7	/* SMPx range is [0..7] */
 #define STM32_ADC_TIMEOUT_US		100000
 #define STM32_ADC_TIMEOUT	(msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
-#define STM32_ADC_AUTO_SUSPEND_DELAY_MS	2000
+#define STM32_ADC_HW_STOP_DELAY_MS	100
 
 #define STM32_DMA_BUFFER_SIZE		PAGE_SIZE
 
@@ -2783,7 +2783,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
 	/* Get stm32-adc-core PM online */
 	pm_runtime_get_noresume(dev);
 	pm_runtime_set_active(dev);
-	pm_runtime_set_autosuspend_delay(dev, STM32_ADC_AUTO_SUSPEND_DELAY_MS);
+	pm_runtime_set_autosuspend_delay(dev, STM32_ADC_HW_STOP_DELAY_MS);
 	pm_runtime_use_autosuspend(dev);
 	pm_runtime_enable(dev);
 
-- 
2.7.4

