mail archive of the barebox mailing list
 help / color / mirror / Atom feed
* [PATCH 1/4] regulator: add regulator_get_voltage() to API
@ 2021-02-08 19:18 Ahmad Fatoum
  2021-02-08 19:18 ` [PATCH 2/4] regulator: add support for struct regulator_desc::off_on_delay Ahmad Fatoum
                   ` (3 more replies)
  0 siblings, 4 replies; 6+ messages in thread
From: Ahmad Fatoum @ 2021-02-08 19:18 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum, has

ADC drivers need to query their reference regulator's voltage to
format their raw readings. Provide regulator_get_voltage() so ADC
drivers need not hardcode a reference voltage. Regulator drivers
that don't support this (i.e. nearly everything in-tree) will
have the function return with -EINVAL.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/regulator/core.c | 27 +++++++++++++++++++++++++++
 include/regulator.h      | 21 +++++++++++++++++++++
 2 files changed, 48 insertions(+)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 6ea21a4609d5..7ced283c116c 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -176,6 +176,9 @@ int of_regulator_register(struct regulator_dev *rd, struct device_node *node)
 
 	ri->node = node;
 
+	if (rd->desc->fixed_uV && rd->desc->n_voltages == 1)
+		ri->min_uv = ri->max_uv = rd->desc->fixed_uV;
+
 	of_property_read_u32(node, "regulator-enable-ramp-delay",
 			&ri->enable_time_us);
 	of_property_read_u32(node, "regulator-min-microvolt",
@@ -539,6 +542,30 @@ void regulator_bulk_free(int num_consumers,
 }
 EXPORT_SYMBOL_GPL(regulator_bulk_free);
 
+int regulator_get_voltage(struct regulator *regulator)
+{
+	struct regulator_dev *rdev = regulator->ri->rdev;
+	int sel, ret;
+
+	if (rdev->desc->ops->get_voltage_sel) {
+		sel = rdev->desc->ops->get_voltage_sel(rdev);
+		if (sel < 0)
+			return sel;
+		ret = rdev->desc->ops->list_voltage(rdev, sel);
+	} else if (rdev->desc->ops->get_voltage) {
+		ret = rdev->desc->ops->get_voltage(rdev);
+	} else if (rdev->desc->ops->list_voltage) {
+		ret = rdev->desc->ops->list_voltage(rdev, 0);
+	} else if (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1)) {
+		ret = rdev->desc->fixed_uV;
+	} else {
+		return -EINVAL;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(regulator_get_voltage_rdev);
+
 static void regulator_print_one(struct regulator_internal *ri)
 {
 	struct regulator *r;
diff --git a/include/regulator.h b/include/regulator.h
index 7c2a01b6875c..524e53042c92 100644
--- a/include/regulator.h
+++ b/include/regulator.h
@@ -51,6 +51,7 @@ struct regulator_bulk_data {
  * @disable_val: Disabling value for control when using regmap enable/disable ops
  * @enable_is_inverted: A flag to indicate set enable_mask bits to disable
  *                      when using regulator_enable_regmap and friends APIs.
+ * @fixed_uV: Fixed voltage of rails.
  */
 
 struct regulator_desc {
@@ -73,6 +74,7 @@ struct regulator_desc {
 
 	const struct regulator_linear_range *linear_ranges;
 	int n_linear_ranges;
+	int fixed_uV;
 };
 
 struct regulator_dev {
@@ -92,6 +94,9 @@ struct regulator_ops {
 	int (*list_voltage) (struct regulator_dev *, unsigned int);
 	int (*set_voltage_sel) (struct regulator_dev *, unsigned int);
 	int (*map_voltage)(struct regulator_dev *, int min_uV, int max_uV);
+
+	int (*get_voltage) (struct regulator_dev *);
+	int (*get_voltage_sel) (struct regulator_dev *);
 };
 
 /*
@@ -166,6 +171,17 @@ int regulator_bulk_disable(int num_consumers,
 void regulator_bulk_free(int num_consumers,
 			 struct regulator_bulk_data *consumers);
 
+/**
+ * regulator_get_voltage - get regulator output voltage
+ * @regulator: regulator source
+ *
+ * This returns the current regulator voltage in uV.
+ *
+ * NOTE: If the regulator is disabled it will return the voltage value. This
+ * function should not be used to determine regulator state.
+ */
+int regulator_get_voltage(struct regulator *regulator);
+
 /*
  * Helper functions intended to be used by regulator drivers prior registering
  * their regulators.
@@ -223,6 +239,11 @@ static inline void regulator_bulk_free(int num_consumers,
 {
 }
 
+static inline int regulator_get_voltage(struct regulator *regulator)
+{
+	return -EINVAL;
+}
+
 #endif
 
 #endif /* __REGULATOR_H */
-- 
2.30.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 2/4] regulator: add support for struct regulator_desc::off_on_delay
  2021-02-08 19:18 [PATCH 1/4] regulator: add regulator_get_voltage() to API Ahmad Fatoum
@ 2021-02-08 19:18 ` Ahmad Fatoum
  2021-02-08 19:18 ` [PATCH 3/4] regulator: add driver for stm32-vrefbuf Ahmad Fatoum
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 6+ messages in thread
From: Ahmad Fatoum @ 2021-02-08 19:18 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum, has

We already honour the enable time in the device tree, read it out
of a new regulator_desc::off_on_delay as well, same as Linux does.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/regulator/core.c | 3 +++
 include/regulator.h      | 2 ++
 2 files changed, 5 insertions(+)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index 7ced283c116c..ac3a9b048ea8 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -176,6 +176,9 @@ int of_regulator_register(struct regulator_dev *rd, struct device_node *node)
 
 	ri->node = node;
 
+	if (rd->desc->off_on_delay)
+		ri->enable_time_us = rd->desc->off_on_delay;
+
 	if (rd->desc->fixed_uV && rd->desc->n_voltages == 1)
 		ri->min_uv = ri->max_uv = rd->desc->fixed_uV;
 
diff --git a/include/regulator.h b/include/regulator.h
index 524e53042c92..83a8813265ff 100644
--- a/include/regulator.h
+++ b/include/regulator.h
@@ -52,6 +52,7 @@ struct regulator_bulk_data {
  * @enable_is_inverted: A flag to indicate set enable_mask bits to disable
  *                      when using regulator_enable_regmap and friends APIs.
  * @fixed_uV: Fixed voltage of rails.
+ * @off_on_delay: guard time (in uS), before re-enabling a regulator
  */
 
 struct regulator_desc {
@@ -75,6 +76,7 @@ struct regulator_desc {
 	const struct regulator_linear_range *linear_ranges;
 	int n_linear_ranges;
 	int fixed_uV;
+	unsigned int off_on_delay;
 };
 
 struct regulator_dev {
-- 
2.30.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 3/4] regulator: add driver for stm32-vrefbuf
  2021-02-08 19:18 [PATCH 1/4] regulator: add regulator_get_voltage() to API Ahmad Fatoum
  2021-02-08 19:18 ` [PATCH 2/4] regulator: add support for struct regulator_desc::off_on_delay Ahmad Fatoum
@ 2021-02-08 19:18 ` Ahmad Fatoum
  2021-02-08 19:18 ` [PATCH 4/4] aiodev: add support for STM32 ADC Ahmad Fatoum
  2021-02-10  9:15 ` [PATCH 1/4] regulator: add regulator_get_voltage() to API Sascha Hauer
  3 siblings, 0 replies; 6+ messages in thread
From: Ahmad Fatoum @ 2021-02-08 19:18 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum, has

This driver supports STMicroelectronics STM32 VREFBUF (voltage
reference buffer) which can be used as voltage reference for
internal ADCs, DACs and also for external components through
dedicated Vref+ pin. Ported from Linux v5.11-rc1.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 arch/arm/dts/stm32mp151.dtsi      |   4 +
 drivers/regulator/Kconfig         |   9 ++
 drivers/regulator/Makefile        |   1 +
 drivers/regulator/helpers.c       |  25 ++++
 drivers/regulator/stm32-vrefbuf.c | 220 ++++++++++++++++++++++++++++++
 include/regulator.h               |   5 +
 6 files changed, 264 insertions(+)
 create mode 100644 drivers/regulator/stm32-vrefbuf.c

diff --git a/arch/arm/dts/stm32mp151.dtsi b/arch/arm/dts/stm32mp151.dtsi
index ca11492de564..b82227fa206e 100644
--- a/arch/arm/dts/stm32mp151.dtsi
+++ b/arch/arm/dts/stm32mp151.dtsi
@@ -62,3 +62,7 @@
 &bsec {
 	barebox,provide-mac-address = <&ethernet0 0x39>;
 };
+
+&vrefbuf {
+	regulator-name = "vref";
+};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 1ce057180a01..9be81832f23d 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -28,6 +28,15 @@ config REGULATOR_STM32_PWR
 	  This driver supports internal regulators (1V1, 1V8, 3V3) in the
 	  STMicroelectronics STM32 chips.
 
+config REGULATOR_STM32_VREFBUF
+	tristate "STMicroelectronics STM32 VREFBUF"
+	depends on ARCH_STM32MP || COMPILE_TEST
+	help
+	  This driver supports STMicroelectronics STM32 VREFBUF (voltage
+	  reference buffer) which can be used as voltage reference for
+	  internal ADCs, DACs and also for external components through
+	  dedicated Vref+ pin.
+
 config REGULATOR_STPMIC1
 	tristate "STMicroelectronics STPMIC1 PMIC Regulators"
 	depends on MFD_STPMIC1
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 4d0bba6c52dd..67859bb79ee4 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -6,3 +6,4 @@ obj-$(CONFIG_REGULATOR_PFUZE) += pfuze.o
 obj-$(CONFIG_REGULATOR_STPMIC1) += stpmic1_regulator.o
 obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o
 obj-$(CONFIG_REGULATOR_STM32_PWR) += stm32-pwr.o
+obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
diff --git a/drivers/regulator/helpers.c b/drivers/regulator/helpers.c
index c4877cecf7e9..e741944ce7cf 100644
--- a/drivers/regulator/helpers.c
+++ b/drivers/regulator/helpers.c
@@ -369,4 +369,29 @@ int regulator_map_voltage_iterate(struct regulator_dev *rdev,
 }
 EXPORT_SYMBOL_GPL(regulator_map_voltage_iterate);
 
+/**
+ * regulator_list_voltage_table - List voltages with table based mapping
+ *
+ * @rdev: Regulator device
+ * @selector: Selector to convert into a voltage
+ *
+ * Regulators with table based mapping between voltages and
+ * selectors can set volt_table in the regulator descriptor
+ * and then use this function as their list_voltage() operation.
+ */
+int regulator_list_voltage_table(struct regulator_dev *rdev,
+				 unsigned int selector)
+{
+	if (!rdev->desc->volt_table) {
+		BUG_ON(!rdev->desc->volt_table);
+		return -EINVAL;
+	}
 
+	if (selector >= rdev->desc->n_voltages)
+		return -EINVAL;
+	if (selector < rdev->desc->linear_min_sel)
+		return 0;
+
+	return rdev->desc->volt_table[selector];
+}
+EXPORT_SYMBOL_GPL(regulator_list_voltage_table);
diff --git a/drivers/regulator/stm32-vrefbuf.c b/drivers/regulator/stm32-vrefbuf.c
new file mode 100644
index 000000000000..3956b1f64f6e
--- /dev/null
+++ b/drivers/regulator/stm32-vrefbuf.c
@@ -0,0 +1,220 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
+ */
+
+#include <common.h>
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <of.h>
+#include <regulator.h>
+
+/* STM32 VREFBUF registers */
+#define STM32_VREFBUF_CSR		0x00
+
+/* STM32 VREFBUF CSR bitfields */
+#define STM32_VRS			GENMASK(6, 4)
+#define STM32_VRR			BIT(3)
+#define STM32_HIZ			BIT(1)
+#define STM32_ENVR			BIT(0)
+
+#define STM32_VREFBUF_AUTO_SUSPEND_DELAY_MS	10
+
+#define readl_relaxed readl
+#define writel_relaxed writel
+
+struct stm32_vrefbuf {
+	void __iomem *base;
+	struct clk *clk;
+	struct device_d *dev;
+	struct regulator_dev rdev;
+};
+
+struct stm32_vrefbuf_desc {
+	struct regulator_desc desc;
+	const char *supply_name;
+};
+
+static inline struct stm32_vrefbuf *to_stm32_vrefbuf(struct regulator_dev *rdev)
+{
+	return container_of(rdev, struct stm32_vrefbuf, rdev);
+}
+
+static const unsigned int stm32_vrefbuf_voltages[] = {
+	/* Matches resp. VRS = 000b, 001b, 010b, 011b */
+	2500000, 2048000, 1800000, 1500000,
+};
+
+static int stm32_vrefbuf_enable(struct regulator_dev *rdev)
+{
+	struct stm32_vrefbuf *priv = to_stm32_vrefbuf(rdev);
+	u32 val;
+	int ret;
+
+	val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+	val = (val & ~STM32_HIZ) | STM32_ENVR;
+	writel_relaxed(val, priv->base + STM32_VREFBUF_CSR);
+
+	/*
+	 * Vrefbuf startup time depends on external capacitor: wait here for
+	 * VRR to be set. That means output has reached expected value.
+	 * ~650us sleep should be enough for caps up to 1.5uF. Use 10ms as
+	 * arbitrary timeout.
+	 */
+	ret = readl_poll_timeout(priv->base + STM32_VREFBUF_CSR, val,
+				 val & STM32_VRR, 10000);
+	if (ret) {
+		dev_err(priv->dev, "stm32 vrefbuf timed out!\n");
+		val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+		val = (val & ~STM32_ENVR) | STM32_HIZ;
+		writel_relaxed(val, priv->base + STM32_VREFBUF_CSR);
+	}
+
+	return ret;
+}
+
+static int stm32_vrefbuf_disable(struct regulator_dev *rdev)
+{
+	struct stm32_vrefbuf *priv = to_stm32_vrefbuf(rdev);
+	u32 val;
+
+	val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+	val &= ~STM32_ENVR;
+	writel_relaxed(val, priv->base + STM32_VREFBUF_CSR);
+
+	return 0;
+}
+
+static int stm32_vrefbuf_is_enabled(struct regulator_dev *rdev)
+{
+	struct stm32_vrefbuf *priv = to_stm32_vrefbuf(rdev);
+	int ret;
+
+	ret = readl_relaxed(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR;
+
+	return ret;
+}
+
+static int stm32_vrefbuf_set_voltage_sel(struct regulator_dev *rdev,
+					 unsigned sel)
+{
+	struct stm32_vrefbuf *priv = to_stm32_vrefbuf(rdev);
+	u32 val;
+
+	val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+	val = (val & ~STM32_VRS) | FIELD_PREP(STM32_VRS, sel);
+	writel_relaxed(val, priv->base + STM32_VREFBUF_CSR);
+
+	return 0;
+}
+
+static int stm32_vrefbuf_get_voltage_sel(struct regulator_dev *rdev)
+{
+	struct stm32_vrefbuf *priv = to_stm32_vrefbuf(rdev);
+	u32 val;
+	int ret;
+
+	val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+	ret = FIELD_GET(STM32_VRS, val);
+
+	return ret;
+}
+
+static const struct regulator_ops stm32_vrefbuf_volt_ops = {
+	.enable		= stm32_vrefbuf_enable,
+	.disable	= stm32_vrefbuf_disable,
+	.is_enabled	= stm32_vrefbuf_is_enabled,
+	.get_voltage_sel = stm32_vrefbuf_get_voltage_sel,
+	.set_voltage_sel = stm32_vrefbuf_set_voltage_sel,
+	.list_voltage	= regulator_list_voltage_table,
+};
+
+static const struct stm32_vrefbuf_desc stm32_vrefbuf_regu = {
+	.desc = {
+		.volt_table = stm32_vrefbuf_voltages,
+		.n_voltages = ARRAY_SIZE(stm32_vrefbuf_voltages),
+		.ops = &stm32_vrefbuf_volt_ops,
+		.off_on_delay = 1000,
+	},
+	.supply_name = "vdda",
+};
+
+static int stm32_vrefbuf_probe(struct device_d *dev)
+{
+	struct stm32_vrefbuf *priv;
+	struct regulator_dev *rdev;
+	struct regulator *supply;
+	int ret;
+
+	supply = regulator_get(dev, stm32_vrefbuf_regu.supply_name);
+	if (IS_ERR(supply))
+		return PTR_ERR(supply);
+
+	priv = xzalloc(sizeof(*priv));
+	priv->dev = dev;
+
+	priv->base = dev_request_mem_region(dev, 0);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	priv->clk = clk_get(dev, NULL);
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
+	ret = clk_enable(priv->clk);
+	if (ret) {
+		dev_err(dev, "clk enable failed with error %d\n", ret);
+		return ret;
+	}
+
+	rdev = &priv->rdev;
+
+	rdev->dev = dev;
+	rdev->desc = &stm32_vrefbuf_regu.desc;
+
+	ret = of_regulator_register(rdev, dev->device_node);
+	if (ret) {
+		ret = PTR_ERR(rdev);
+		dev_err(dev, "register failed with error %d\n", ret);
+		goto err_clk_dis;
+	}
+
+	regulator_enable(supply);
+
+	dev->priv = priv;
+
+	return 0;
+
+err_clk_dis:
+	clk_disable(priv->clk);
+
+	return ret;
+}
+
+static void stm32_vrefbuf_remove(struct device_d *dev)
+{
+	struct stm32_vrefbuf *priv = dev->priv;
+
+	clk_disable(priv->clk);
+};
+
+static const struct of_device_id __maybe_unused stm32_vrefbuf_of_match[] = {
+	{ .compatible = "st,stm32-vrefbuf", },
+	{},
+};
+
+static struct driver_d stm32_vrefbuf_driver = {
+	.probe = stm32_vrefbuf_probe,
+	.name  = "stm32-vrefbuf",
+	.remove = stm32_vrefbuf_remove,
+	.of_compatible = stm32_vrefbuf_of_match,
+};
+device_platform_driver(stm32_vrefbuf_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 VREFBUF driver");
+MODULE_ALIAS("platform:stm32-vrefbuf");
diff --git a/include/regulator.h b/include/regulator.h
index 83a8813265ff..bbe8dd91d84b 100644
--- a/include/regulator.h
+++ b/include/regulator.h
@@ -51,6 +51,7 @@ struct regulator_bulk_data {
  * @disable_val: Disabling value for control when using regmap enable/disable ops
  * @enable_is_inverted: A flag to indicate set enable_mask bits to disable
  *                      when using regulator_enable_regmap and friends APIs.
+ * @volt_table: Voltage mapping table (if table based mapping)
  * @fixed_uV: Fixed voltage of rails.
  * @off_on_delay: guard time (in uS), before re-enabling a regulator
  */
@@ -75,6 +76,7 @@ struct regulator_desc {
 
 	const struct regulator_linear_range *linear_ranges;
 	int n_linear_ranges;
+	const unsigned int *volt_table;
 	int fixed_uV;
 	unsigned int off_on_delay;
 };
@@ -190,6 +192,9 @@ int regulator_get_voltage(struct regulator *regulator);
  */
 int regulator_desc_list_voltage_linear_range(const struct regulator_desc *desc,
 					     unsigned int selector);
+
+int regulator_list_voltage_table(struct regulator_dev *rdev,
+				  unsigned int selector);
 #else
 
 static inline struct regulator *regulator_get(struct device_d *dev, const char *id)
-- 
2.30.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH 4/4] aiodev: add support for STM32 ADC
  2021-02-08 19:18 [PATCH 1/4] regulator: add regulator_get_voltage() to API Ahmad Fatoum
  2021-02-08 19:18 ` [PATCH 2/4] regulator: add support for struct regulator_desc::off_on_delay Ahmad Fatoum
  2021-02-08 19:18 ` [PATCH 3/4] regulator: add driver for stm32-vrefbuf Ahmad Fatoum
@ 2021-02-08 19:18 ` Ahmad Fatoum
  2021-02-09  8:00   ` [PATCH] fixup! " Ahmad Fatoum
  2021-02-10  9:15 ` [PATCH 1/4] regulator: add regulator_get_voltage() to API Sascha Hauer
  3 siblings, 1 reply; 6+ messages in thread
From: Ahmad Fatoum @ 2021-02-08 19:18 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum, has

This driver supports simple one-shot readings rather than continuous
sampling with DMA, etc. ADC channels should be configured via
device tree, using the kernel bindings.

Code is based on the stm32-adc drivers of Linux v5.11-rc1 and
U-Boot v2021.01-rc4.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 arch/arm/configs/stm32mp_defconfig |   3 +
 drivers/aiodev/Kconfig             |   7 +
 drivers/aiodev/Makefile            |   1 +
 drivers/aiodev/stm32-adc-core.c    | 211 ++++++++++++++++
 drivers/aiodev/stm32-adc-core.h    |  52 ++++
 drivers/aiodev/stm32-adc.c         | 374 +++++++++++++++++++++++++++++
 6 files changed, 648 insertions(+)
 create mode 100644 drivers/aiodev/stm32-adc-core.c
 create mode 100644 drivers/aiodev/stm32-adc-core.h
 create mode 100644 drivers/aiodev/stm32-adc.c

diff --git a/arch/arm/configs/stm32mp_defconfig b/arch/arm/configs/stm32mp_defconfig
index e9f89e69d969..e1ee4ec08205 100644
--- a/arch/arm/configs/stm32mp_defconfig
+++ b/arch/arm/configs/stm32mp_defconfig
@@ -94,6 +94,8 @@ CONFIG_NET_NETCONSOLE=y
 CONFIG_NET_FASTBOOT=y
 CONFIG_OFDEVICE=y
 CONFIG_OF_BAREBOX_DRIVERS=y
+CONFIG_AIODEV=y
+CONFIG_STM32_ADC=y
 CONFIG_DRIVER_SERIAL_STM32=y
 CONFIG_DRIVER_NET_DESIGNWARE_EQOS=y
 CONFIG_DRIVER_NET_DESIGNWARE_STM32=y
@@ -132,6 +134,7 @@ CONFIG_STM32_BSEC=y
 CONFIG_REGULATOR=y
 CONFIG_REGULATOR_FIXED=y
 CONFIG_REGULATOR_STM32_PWR=y
+CONFIG_REGULATOR_STM32_VREFBUF=y
 CONFIG_REGULATOR_STPMIC1=y
 CONFIG_REMOTEPROC=y
 CONFIG_STM32_REMOTEPROC=y
diff --git a/drivers/aiodev/Kconfig b/drivers/aiodev/Kconfig
index 5fb445c096e6..88d013aad0b9 100644
--- a/drivers/aiodev/Kconfig
+++ b/drivers/aiodev/Kconfig
@@ -43,4 +43,11 @@ config AM335X_ADC
 	  rather than continuous sampling with DMA, etc.  ADC channels should be
 	  configured via device tree, using the kernel bindings.
 
+config STM32_ADC
+	tristate "STM32 ADC driver"
+	depends on ARCH_STM32MP || COMPILE_TEST
+	help
+	  Support for ADC on STM32.  Supports simple one-shot readings
+	  rather than continuous sampling with DMA, etc.  ADC channels should be
+	  configured via device tree, using the kernel bindings.
 endif
diff --git a/drivers/aiodev/Makefile b/drivers/aiodev/Makefile
index 5f48b2022a81..52652f67b756 100644
--- a/drivers/aiodev/Makefile
+++ b/drivers/aiodev/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_LM75) += lm75.o
 obj-$(CONFIG_MC13XXX_ADC) += mc13xxx_adc.o
 obj-$(CONFIG_QORIQ_THERMAL) += qoriq_thermal.o
 obj-$(CONFIG_AM335X_ADC) += am335x_adc.o
+obj-$(CONFIG_STM32_ADC) += stm32-adc.o stm32-adc-core.o
diff --git a/drivers/aiodev/stm32-adc-core.c b/drivers/aiodev/stm32-adc-core.c
new file mode 100644
index 000000000000..410e2a894e12
--- /dev/null
+++ b/drivers/aiodev/stm32-adc-core.c
@@ -0,0 +1,211 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
+ *
+ * Originally based on the Linux kernel v4.18 drivers/iio/adc/stm32-adc-core.c.
+ */
+
+#include <common.h>
+#include <linux/clk.h>
+#include <regulator.h>
+#include <linux/bitops.h>
+#include "stm32-adc-core.h"
+
+/* STM32H7 - common registers for all ADC instances */
+#define STM32H7_ADC_CCR			(STM32_ADCX_COMN_OFFSET + 0x08)
+
+/* STM32H7_ADC_CCR - bit fields */
+#define STM32H7_PRESC_SHIFT		18
+#define STM32H7_PRESC_MASK		GENMASK(21, 18)
+#define STM32H7_CKMODE_SHIFT		16
+#define STM32H7_CKMODE_MASK		GENMASK(17, 16)
+
+/* STM32 H7 maximum analog clock rate (from datasheet) */
+#define STM32H7_ADC_MAX_CLK_RATE	36000000
+
+/**
+ * struct stm32h7_adc_ck_spec - specification for stm32h7 adc clock
+ * @ckmode: ADC clock mode, Async or sync with prescaler.
+ * @presc: prescaler bitfield for async clock mode
+ * @div: prescaler division ratio
+ */
+struct stm32h7_adc_ck_spec {
+	u32 ckmode;
+	u32 presc;
+	int div;
+};
+
+static const struct stm32h7_adc_ck_spec stm32h7_adc_ckmodes_spec[] = {
+	/* 00: CK_ADC[1..3]: Asynchronous clock modes */
+	{ 0, 0, 1 },
+	{ 0, 1, 2 },
+	{ 0, 2, 4 },
+	{ 0, 3, 6 },
+	{ 0, 4, 8 },
+	{ 0, 5, 10 },
+	{ 0, 6, 12 },
+	{ 0, 7, 16 },
+	{ 0, 8, 32 },
+	{ 0, 9, 64 },
+	{ 0, 10, 128 },
+	{ 0, 11, 256 },
+	/* HCLK used: Synchronous clock modes (1, 2 or 4 prescaler) */
+	{ 1, 0, 1 },
+	{ 2, 0, 2 },
+	{ 3, 0, 4 },
+};
+
+static int stm32h7_adc_clk_sel(struct device_d *dev,
+			       struct stm32_adc_common *common)
+{
+	u32 ckmode, presc;
+	unsigned long rate;
+	unsigned int i;
+	int div;
+
+	/* stm32h7 bus clock is common for all ADC instances (mandatory) */
+	if (!common->bclk) {
+		dev_err(dev, "No bclk clock found\n");
+		return -ENOENT;
+	}
+
+	/*
+	 * stm32h7 can use either 'bus' or 'adc' clock for analog circuitry.
+	 * So, choice is to have bus clock mandatory and adc clock optional.
+	 * If optional 'adc' clock has been found, then try to use it first.
+	 */
+	if (common->aclk) {
+		/*
+		 * Asynchronous clock modes (e.g. ckmode == 0)
+		 * From spec: PLL output musn't exceed max rate
+		 */
+		rate = clk_get_rate(common->aclk);
+		if (!rate) {
+			dev_err(dev, "Invalid aclk rate: 0\n");
+			return -EINVAL;
+		}
+
+		for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
+			ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
+			presc = stm32h7_adc_ckmodes_spec[i].presc;
+			div = stm32h7_adc_ckmodes_spec[i].div;
+
+			if (ckmode)
+				continue;
+
+			if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
+				goto out;
+		}
+	}
+
+	/* Synchronous clock modes (e.g. ckmode is 1, 2 or 3) */
+	rate = clk_get_rate(common->bclk);
+	if (!rate) {
+		dev_err(dev, "Invalid bus clock rate: 0\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(stm32h7_adc_ckmodes_spec); i++) {
+		ckmode = stm32h7_adc_ckmodes_spec[i].ckmode;
+		presc = stm32h7_adc_ckmodes_spec[i].presc;
+		div = stm32h7_adc_ckmodes_spec[i].div;
+
+		if (!ckmode)
+			continue;
+
+		if ((rate / div) <= STM32H7_ADC_MAX_CLK_RATE)
+			goto out;
+	}
+
+	dev_err(dev, "clk selection failed\n");
+	return -EINVAL;
+
+out:
+	/* rate used later by each ADC instance to control BOOST mode */
+	common->rate = rate / div;
+
+	/* Set common clock mode and prescaler */
+	clrsetbits_le32(common->base + STM32H7_ADC_CCR,
+			STM32H7_CKMODE_MASK | STM32H7_PRESC_MASK,
+			ckmode << STM32H7_CKMODE_SHIFT |
+			presc << STM32H7_PRESC_SHIFT);
+
+	dev_dbg(dev, "Using %s clock/%d source at %ld kHz\n",
+		ckmode ? "bus" : "adc", div, common->rate / 1000);
+
+	return 0;
+}
+
+static int stm32_adc_core_probe(struct device_d *dev)
+{
+	struct stm32_adc_common *common;
+	int ret;
+
+	common = xzalloc(sizeof(*common));
+
+	common->vref = regulator_get(dev, "vref");
+	if (IS_ERR(common->vref)) {
+		dev_err(dev, "can't get vref-supply: %pe\n", common->vref);
+		return PTR_ERR(common->vref);
+	}
+
+	ret = regulator_get_voltage(common->vref);
+	if (ret < 0) {
+		dev_err(dev, "can't get vref-supply value: %d\n", ret);
+		return ret;
+	}
+	common->vref_uv = ret;
+
+	common->aclk = clk_get(dev, "adc");
+	if (!IS_ERR(common->aclk)) {
+		ret = clk_enable(common->aclk);
+		if (ret) {
+			dev_err(dev, "Can't enable aclk: %d\n", ret);
+			return ret;
+		}
+	}
+
+	common->bclk = clk_get(dev, "bus");
+	if (!IS_ERR(common->bclk)) {
+		ret = clk_enable(common->bclk);
+		if (ret) {
+			dev_err(dev, "Can't enable bclk: %d\n", ret);
+			goto err_aclk_disable;
+		}
+	}
+
+	common->base = dev_request_mem_region(dev, 0);
+	if (IS_ERR(common->base)) {
+		dev_err(dev, "can't get address\n");
+		return -ENOENT;
+	}
+
+	ret = stm32h7_adc_clk_sel(dev, common);
+	if (ret)
+		goto err_bclk_disable;
+
+	dev->priv = common;
+	return of_platform_populate(dev->device_node, NULL, dev);
+
+err_bclk_disable:
+	clk_disable(common->bclk);
+
+err_aclk_disable:
+	clk_disable(common->aclk);
+
+	return ret;
+}
+
+static const struct of_device_id stm32_adc_core_ids[] = {
+	{ .compatible = "st,stm32h7-adc-core" },
+	{ .compatible = "st,stm32mp1-adc-core" },
+	{}
+};
+
+static struct driver_d stm32_adc_core_driver = {
+	.name		= "stm32-adc-core",
+	.probe		= stm32_adc_core_probe,
+	.of_compatible	= DRV_OF_COMPAT(stm32_adc_core_ids),
+};
+device_platform_driver(stm32_adc_core_driver);
diff --git a/drivers/aiodev/stm32-adc-core.h b/drivers/aiodev/stm32-adc-core.h
new file mode 100644
index 000000000000..de6c0b9495f3
--- /dev/null
+++ b/drivers/aiodev/stm32-adc-core.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>.
+ *
+ * Originally based on the Linux kernel v4.18 drivers/iio/adc/stm32-adc-core.h.
+ */
+
+#ifndef __STM32_ADC_H
+#define __STM32_ADC_H
+
+/*
+ * STM32 - ADC global register map
+ * ________________________________________________________
+ * | Offset |                 Register                    |
+ * --------------------------------------------------------
+ * | 0x000  |                Master ADC1                  |
+ * --------------------------------------------------------
+ * | 0x100  |                Slave ADC2                   |
+ * --------------------------------------------------------
+ * | 0x200  |                Slave ADC3                   |
+ * --------------------------------------------------------
+ * | 0x300  |         Master & Slave common regs          |
+ * --------------------------------------------------------
+ */
+#define STM32_ADC_MAX_ADCS		3
+#define STM32_ADCX_COMN_OFFSET		0x300
+
+#include <linux/types.h>
+
+struct regulator;
+struct clk;
+
+/**
+ * struct stm32_adc_common - stm32 ADC driver common data (for all instances)
+ * @base:		control registers base cpu addr
+ * @rate:		clock rate used for analog circuitry
+ * @aclk:		clock for the analog circuitry
+ * @bclk:		bus clock common for all ADCs
+ * @vref:		regulator reference
+ * @vref_uv:		reference supply voltage (uV)
+ */
+struct stm32_adc_common {
+	void __iomem *base;
+	unsigned long rate;
+	struct clk *aclk;
+	struct clk *bclk;
+	struct regulator *vref;
+	int vref_uv;
+};
+
+#endif
diff --git a/drivers/aiodev/stm32-adc.c b/drivers/aiodev/stm32-adc.c
new file mode 100644
index 000000000000..eb9548adef70
--- /dev/null
+++ b/drivers/aiodev/stm32-adc.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2018, STMicroelectronics - All Rights Reserved
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
+ *
+ * Originally based on the Linux kernel v4.18 drivers/iio/adc/stm32-adc.c.
+ */
+
+#include <common.h>
+#include <linux/bitops.h>
+#include <linux/iopoll.h>
+#include <aiodev.h>
+#include <regulator.h>
+#include <linux/math64.h>
+#include "stm32-adc-core.h"
+
+/* STM32H7 - Registers for each ADC instance */
+#define STM32H7_ADC_ISR			0x00
+#define STM32H7_ADC_CR			0x08
+#define STM32H7_ADC_CFGR		0x0C
+#define STM32H7_ADC_SMPR1		0x14
+#define STM32H7_ADC_SMPR2		0x18
+#define STM32H7_ADC_PCSEL		0x1C
+#define STM32H7_ADC_SQR1		0x30
+#define STM32H7_ADC_DR			0x40
+#define STM32H7_ADC_DIFSEL		0xC0
+
+/* STM32H7_ADC_ISR - bit fields */
+#define STM32MP1_VREGREADY		BIT(12)
+#define STM32H7_EOC			BIT(2)
+#define STM32H7_ADRDY			BIT(0)
+
+/* STM32H7_ADC_CR - bit fields */
+#define STM32H7_DEEPPWD			BIT(29)
+#define STM32H7_ADVREGEN		BIT(28)
+#define STM32H7_BOOST			BIT(8)
+#define STM32H7_ADSTART			BIT(2)
+#define STM32H7_ADDIS			BIT(1)
+#define STM32H7_ADEN			BIT(0)
+
+/* STM32H7_ADC_CFGR bit fields */
+#define STM32H7_EXTEN			GENMASK(11, 10)
+#define STM32H7_DMNGT			GENMASK(1, 0)
+
+/* STM32H7_ADC_SQR1 - bit fields */
+#define STM32H7_SQ1_SHIFT		6
+
+/* BOOST bit must be set on STM32H7 when ADC clock is above 20MHz */
+#define STM32H7_BOOST_CLKRATE		20000000UL
+
+#define STM32_ADC_CH_MAX		20	/* max number of channels */
+#define STM32_ADC_MAX_SMP		7	/* SMPx range is [0..7] */
+#define STM32_ADC_TIMEOUT_US		100000
+
+struct stm32_adc_regs {
+	int reg;
+	int mask;
+	int shift;
+};
+
+struct stm32_adc_cfg {
+	unsigned int max_channels;
+	unsigned int num_bits;
+	bool has_vregready;
+	const struct stm32_adc_regs *smp_bits;
+	const unsigned int *smp_cycles;
+};
+
+struct stm32_adc {
+	struct stm32_adc_common *common;
+	void __iomem *regs;
+	const struct stm32_adc_cfg *cfg;
+	u32 channel_mask;
+	u32 data_mask;
+	struct aiodevice aiodev;
+	void __iomem *base;
+	struct aiochannel *channels;
+	u32 *channel_map;
+	u32 smpr_val[2];
+};
+
+static void stm32_adc_stop(struct stm32_adc *adc)
+{
+	setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADDIS);
+	clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST);
+	/* Setting DEEPPWD disables ADC vreg and clears ADVREGEN */
+	setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_DEEPPWD);
+
+	regulator_disable(adc->common->vref);
+}
+
+static int stm32_adc_start_channel(struct stm32_adc *adc, int channel)
+{
+	struct device_d *dev = adc->aiodev.hwdev;
+	struct stm32_adc_common *common = adc->common;
+	int ret;
+	u32 val;
+
+	ret = regulator_enable(common->vref);
+	if (ret)
+		return ret;
+
+	/* Exit deep power down, then enable ADC voltage regulator */
+	clrbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_DEEPPWD);
+	setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADVREGEN);
+	if (common->rate > STM32H7_BOOST_CLKRATE)
+		setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_BOOST);
+
+	/* Wait for startup time */
+	if (!adc->cfg->has_vregready) {
+		udelay(20);
+	} else {
+		ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val,
+					 val & STM32MP1_VREGREADY,
+					 STM32_ADC_TIMEOUT_US);
+		if (ret < 0) {
+			stm32_adc_stop(adc);
+			dev_err(dev, "Failed to enable vreg: %d\n", ret);
+			return ret;
+		}
+	}
+
+	/* Only use single ended channels */
+	writel(0, adc->regs + STM32H7_ADC_DIFSEL);
+
+	/* Enable ADC, Poll for ADRDY to be set (after adc startup time) */
+	setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADEN);
+	ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val,
+				 val & STM32H7_ADRDY, STM32_ADC_TIMEOUT_US);
+	if (ret < 0) {
+		stm32_adc_stop(adc);
+		dev_err(dev, "Failed to enable ADC: %d\n", ret);
+		return ret;
+	}
+
+	/* Preselect channels */
+	writel(adc->channel_mask, adc->regs + STM32H7_ADC_PCSEL);
+
+	/* Apply sampling time settings */
+	writel(adc->smpr_val[0], adc->regs + STM32H7_ADC_SMPR1);
+	writel(adc->smpr_val[1], adc->regs + STM32H7_ADC_SMPR2);
+
+	/* Program regular sequence: chan in SQ1 & len = 0 for one channel */
+	writel(channel << STM32H7_SQ1_SHIFT, adc->regs + STM32H7_ADC_SQR1);
+
+	/* Trigger detection disabled (conversion can be launched in SW) */
+	clrbits_le32(adc->regs + STM32H7_ADC_CFGR, STM32H7_EXTEN |
+		     STM32H7_DMNGT);
+
+	return 0;
+}
+
+static int stm32_adc_channel_data(struct stm32_adc *adc, int channel,
+				  int *data)
+{
+	struct device_d *dev = &adc->aiodev.dev;
+	int ret;
+	u32 val;
+
+	setbits_le32(adc->regs + STM32H7_ADC_CR, STM32H7_ADSTART);
+	ret = readl_poll_timeout(adc->regs + STM32H7_ADC_ISR, val,
+				 val & STM32H7_EOC, STM32_ADC_TIMEOUT_US);
+	if (ret < 0) {
+		dev_err(dev, "conversion timed out: %d\n", ret);
+		return ret;
+	}
+
+	*data = readl(adc->regs + STM32H7_ADC_DR);
+
+	return 0;
+}
+
+static int stm32_adc_channel_single_shot(struct aiochannel *chan, int *data)
+{
+	struct stm32_adc *adc = container_of(chan->aiodev, struct stm32_adc, aiodev);
+	int ret, index;
+	s64 raw64;
+
+	index = adc->channel_map[chan->index];
+
+	ret = stm32_adc_start_channel(adc, index);
+	if (ret)
+		return ret;
+
+	ret = stm32_adc_channel_data(adc, index, data);
+	if (ret)
+		return ret;
+
+	raw64 = *data;
+	raw64 *= adc->common->vref_uv;
+	*data = div_s64(raw64, adc->data_mask);
+
+	return 0;
+}
+
+static void stm32_adc_smpr_init(struct stm32_adc *adc, int channel, u32 smp_ns)
+{
+	const struct stm32_adc_regs *smpr = &adc->cfg->smp_bits[channel];
+	u32 period_ns, shift = smpr->shift, mask = smpr->mask;
+	unsigned int smp, r = smpr->reg;
+
+	/* Determine sampling time (ADC clock cycles) */
+	period_ns = NSEC_PER_SEC / adc->common->rate;
+	for (smp = 0; smp <= STM32_ADC_MAX_SMP; smp++)
+		if ((period_ns * adc->cfg->smp_cycles[smp]) >= smp_ns)
+			break;
+	if (smp > STM32_ADC_MAX_SMP)
+		smp = STM32_ADC_MAX_SMP;
+
+	/* pre-build sampling time registers (e.g. smpr1, smpr2) */
+	adc->smpr_val[r] = (adc->smpr_val[r] & ~mask) | (smp << shift);
+}
+
+static int stm32_adc_chan_of_init(struct device_d *dev, struct stm32_adc *adc)
+{
+	unsigned int i;
+	int num_channels = 0, num_times = 0;
+	u32 smp = 0xffffffff; /* Set sampling time to max value by default */
+	int ret;
+
+	/* Retrieve single ended channels listed in device tree */
+	of_get_property(dev->device_node, "st,adc-channels", &num_channels);
+	num_channels /= sizeof(__be32);
+
+	if (num_channels > adc->cfg->max_channels) {
+		dev_err(dev, "too many st,adc-channels: %d\n", num_channels);
+		return -EINVAL;
+	}
+
+	/* Optional sample time is provided either for each, or all channels */
+	of_get_property(dev->device_node, "st,min-sample-time-nsecs", &num_times);
+	num_times /= sizeof(__be32);
+	if (num_times > 1 && num_times != num_channels) {
+		dev_err(dev, "Invalid st,min-sample-time-nsecs\n");
+		return -EINVAL;
+	}
+
+	adc->channels = calloc(sizeof(*adc->channels), num_channels);
+	if (!adc->channels)
+		return -ENOMEM;
+
+	adc->aiodev.channels = calloc(sizeof(*adc->aiodev.channels), num_channels);
+	if (!adc->aiodev.channels)
+		return -ENOMEM;
+
+	adc->channel_map = calloc(sizeof(u32), num_channels);
+
+	adc->aiodev.num_channels = num_channels;
+	adc->aiodev.hwdev = dev;
+	adc->aiodev.read = stm32_adc_channel_single_shot;
+
+	for (i = 0; i < num_channels; i++) {
+		u32 chan;
+
+		ret = of_property_read_u32_index(dev->device_node, "st,adc-channels", i, &chan);
+		if (ret)
+			return ret;
+
+		if (chan >= adc->cfg->max_channels) {
+			dev_err(dev, "bad channel %u\n", chan);
+			return -EINVAL;
+		}
+
+		adc->channel_mask |= 1 << chan;
+
+		adc->aiodev.channels[i] = &adc->channels[i];
+		adc->channels[i].unit = "uV";
+		adc->channel_map[i] = chan;
+
+		/*
+		 * Using of_property_read_u32_index(), smp value will only be
+		 * modified if valid u32 value can be decoded. This allows to
+		 * get either no value, 1 shared value for all indexes, or one
+		 * value per channel.
+		 */
+		of_property_read_u32_index(dev->device_node, "st,min-sample-time-nsecs",
+					   i, &smp);
+		/* Prepare sampling time settings */
+		stm32_adc_smpr_init(adc, chan, smp);
+	}
+
+	adc->data_mask = (1 << adc->cfg->num_bits) - 1;
+
+	ret = aiodevice_register(&adc->aiodev);
+	if (ret < 0)
+		dev_err(dev, "Failed to register aiodev\n");
+
+	return ret;
+}
+
+static int stm32_adc_probe(struct device_d *dev)
+{
+	struct stm32_adc_common *common = dev->parent->priv;
+	struct stm32_adc *adc;
+	u32 offset;
+	int ret;
+
+	ret = of_property_read_u32(dev->device_node, "reg", &offset);
+	if (ret) {
+		dev_err(dev, "Can't read reg property\n");
+		return ret;
+	}
+
+	adc = xzalloc(sizeof(*adc));
+
+	adc->regs = common->base + offset;
+	adc->cfg = device_get_match_data(dev);
+	adc->common = common;
+
+	return stm32_adc_chan_of_init(dev, adc);
+}
+
+/*
+ * stm32h7_smp_bits - describe sampling time register index & bit fields
+ * Sorted so it can be indexed by channel number.
+ */
+static const struct stm32_adc_regs stm32h7_smp_bits[] = {
+	/* STM32H7_ADC_SMPR1, smpr[] index, mask, shift for SMP0 to SMP9 */
+	{ 0, GENMASK(2, 0), 0 },
+	{ 0, GENMASK(5, 3), 3 },
+	{ 0, GENMASK(8, 6), 6 },
+	{ 0, GENMASK(11, 9), 9 },
+	{ 0, GENMASK(14, 12), 12 },
+	{ 0, GENMASK(17, 15), 15 },
+	{ 0, GENMASK(20, 18), 18 },
+	{ 0, GENMASK(23, 21), 21 },
+	{ 0, GENMASK(26, 24), 24 },
+	{ 0, GENMASK(29, 27), 27 },
+	/* STM32H7_ADC_SMPR2, smpr[] index, mask, shift for SMP10 to SMP19 */
+	{ 1, GENMASK(2, 0), 0 },
+	{ 1, GENMASK(5, 3), 3 },
+	{ 1, GENMASK(8, 6), 6 },
+	{ 1, GENMASK(11, 9), 9 },
+	{ 1, GENMASK(14, 12), 12 },
+	{ 1, GENMASK(17, 15), 15 },
+	{ 1, GENMASK(20, 18), 18 },
+	{ 1, GENMASK(23, 21), 21 },
+	{ 1, GENMASK(26, 24), 24 },
+	{ 1, GENMASK(29, 27), 27 },
+};
+
+/* STM32H7 programmable sampling time (ADC clock cycles, rounded down) */
+static const unsigned int stm32h7_adc_smp_cycles[STM32_ADC_MAX_SMP + 1] = {
+	1, 2, 8, 16, 32, 64, 387, 810,
+};
+
+static const struct stm32_adc_cfg stm32h7_adc_cfg = {
+	.num_bits = 16,
+	.max_channels = STM32_ADC_CH_MAX,
+	.smp_bits = stm32h7_smp_bits,
+	.smp_cycles = stm32h7_adc_smp_cycles,
+};
+
+
+static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
+	.num_bits = 16,
+	.max_channels = STM32_ADC_CH_MAX,
+	.smp_bits = stm32h7_smp_bits,
+	.smp_cycles = stm32h7_adc_smp_cycles,
+	.has_vregready = true,
+};
+
+static const struct of_device_id qoriq_tmu_match[] = {
+	{ .compatible = "st,stm32h7-adc", .data = &stm32h7_adc_cfg },
+	{ .compatible = "st,stm32mp1-adc", .data = &stm32mp1_adc_cfg },
+	{}
+};
+
+static struct driver_d imx_thermal_driver = {
+	.name		= "stm32-adc",
+	.probe		= stm32_adc_probe,
+	.of_compatible	= DRV_OF_COMPAT(qoriq_tmu_match),
+};
+device_platform_driver(imx_thermal_driver);
-- 
2.30.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* [PATCH] fixup! aiodev: add support for STM32 ADC
  2021-02-08 19:18 ` [PATCH 4/4] aiodev: add support for STM32 ADC Ahmad Fatoum
@ 2021-02-09  8:00   ` Ahmad Fatoum
  0 siblings, 0 replies; 6+ messages in thread
From: Ahmad Fatoum @ 2021-02-09  8:00 UTC (permalink / raw)
  To: barebox; +Cc: Ahmad Fatoum

The identifiers hint at where the driver boilerplate was taken from.
Replace them with correctly named ones.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/aiodev/stm32-adc.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/aiodev/stm32-adc.c b/drivers/aiodev/stm32-adc.c
index eb9548adef70..c99b995eafe5 100644
--- a/drivers/aiodev/stm32-adc.c
+++ b/drivers/aiodev/stm32-adc.c
@@ -360,15 +360,15 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
 	.has_vregready = true,
 };
 
-static const struct of_device_id qoriq_tmu_match[] = {
+static const struct of_device_id stm32_adc_match[] = {
 	{ .compatible = "st,stm32h7-adc", .data = &stm32h7_adc_cfg },
 	{ .compatible = "st,stm32mp1-adc", .data = &stm32mp1_adc_cfg },
 	{}
 };
 
-static struct driver_d imx_thermal_driver = {
+static struct driver_d stm32_adc_driver = {
 	.name		= "stm32-adc",
 	.probe		= stm32_adc_probe,
-	.of_compatible	= DRV_OF_COMPAT(qoriq_tmu_match),
+	.of_compatible	= DRV_OF_COMPAT(stm32_adc_match),
 };
-device_platform_driver(imx_thermal_driver);
+device_platform_driver(stm32_adc_driver);
-- 
2.30.0


_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

* Re: [PATCH 1/4] regulator: add regulator_get_voltage() to API
  2021-02-08 19:18 [PATCH 1/4] regulator: add regulator_get_voltage() to API Ahmad Fatoum
                   ` (2 preceding siblings ...)
  2021-02-08 19:18 ` [PATCH 4/4] aiodev: add support for STM32 ADC Ahmad Fatoum
@ 2021-02-10  9:15 ` Sascha Hauer
  3 siblings, 0 replies; 6+ messages in thread
From: Sascha Hauer @ 2021-02-10  9:15 UTC (permalink / raw)
  To: Ahmad Fatoum; +Cc: barebox, has

On Mon, Feb 08, 2021 at 08:18:24PM +0100, Ahmad Fatoum wrote:
> ADC drivers need to query their reference regulator's voltage to
> format their raw readings. Provide regulator_get_voltage() so ADC
> drivers need not hardcode a reference voltage. Regulator drivers
> that don't support this (i.e. nearly everything in-tree) will
> have the function return with -EINVAL.
> 
> Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
> ---
>  drivers/regulator/core.c | 27 +++++++++++++++++++++++++++
>  include/regulator.h      | 21 +++++++++++++++++++++
>  2 files changed, 48 insertions(+)

Applied, thanks

Sascha

> 
> diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
> index 6ea21a4609d5..7ced283c116c 100644
> --- a/drivers/regulator/core.c
> +++ b/drivers/regulator/core.c
> @@ -176,6 +176,9 @@ int of_regulator_register(struct regulator_dev *rd, struct device_node *node)
>  
>  	ri->node = node;
>  
> +	if (rd->desc->fixed_uV && rd->desc->n_voltages == 1)
> +		ri->min_uv = ri->max_uv = rd->desc->fixed_uV;
> +
>  	of_property_read_u32(node, "regulator-enable-ramp-delay",
>  			&ri->enable_time_us);
>  	of_property_read_u32(node, "regulator-min-microvolt",
> @@ -539,6 +542,30 @@ void regulator_bulk_free(int num_consumers,
>  }
>  EXPORT_SYMBOL_GPL(regulator_bulk_free);
>  
> +int regulator_get_voltage(struct regulator *regulator)
> +{
> +	struct regulator_dev *rdev = regulator->ri->rdev;
> +	int sel, ret;
> +
> +	if (rdev->desc->ops->get_voltage_sel) {
> +		sel = rdev->desc->ops->get_voltage_sel(rdev);
> +		if (sel < 0)
> +			return sel;
> +		ret = rdev->desc->ops->list_voltage(rdev, sel);
> +	} else if (rdev->desc->ops->get_voltage) {
> +		ret = rdev->desc->ops->get_voltage(rdev);
> +	} else if (rdev->desc->ops->list_voltage) {
> +		ret = rdev->desc->ops->list_voltage(rdev, 0);
> +	} else if (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1)) {
> +		ret = rdev->desc->fixed_uV;
> +	} else {
> +		return -EINVAL;
> +	}
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(regulator_get_voltage_rdev);
> +
>  static void regulator_print_one(struct regulator_internal *ri)
>  {
>  	struct regulator *r;
> diff --git a/include/regulator.h b/include/regulator.h
> index 7c2a01b6875c..524e53042c92 100644
> --- a/include/regulator.h
> +++ b/include/regulator.h
> @@ -51,6 +51,7 @@ struct regulator_bulk_data {
>   * @disable_val: Disabling value for control when using regmap enable/disable ops
>   * @enable_is_inverted: A flag to indicate set enable_mask bits to disable
>   *                      when using regulator_enable_regmap and friends APIs.
> + * @fixed_uV: Fixed voltage of rails.
>   */
>  
>  struct regulator_desc {
> @@ -73,6 +74,7 @@ struct regulator_desc {
>  
>  	const struct regulator_linear_range *linear_ranges;
>  	int n_linear_ranges;
> +	int fixed_uV;
>  };
>  
>  struct regulator_dev {
> @@ -92,6 +94,9 @@ struct regulator_ops {
>  	int (*list_voltage) (struct regulator_dev *, unsigned int);
>  	int (*set_voltage_sel) (struct regulator_dev *, unsigned int);
>  	int (*map_voltage)(struct regulator_dev *, int min_uV, int max_uV);
> +
> +	int (*get_voltage) (struct regulator_dev *);
> +	int (*get_voltage_sel) (struct regulator_dev *);
>  };
>  
>  /*
> @@ -166,6 +171,17 @@ int regulator_bulk_disable(int num_consumers,
>  void regulator_bulk_free(int num_consumers,
>  			 struct regulator_bulk_data *consumers);
>  
> +/**
> + * regulator_get_voltage - get regulator output voltage
> + * @regulator: regulator source
> + *
> + * This returns the current regulator voltage in uV.
> + *
> + * NOTE: If the regulator is disabled it will return the voltage value. This
> + * function should not be used to determine regulator state.
> + */
> +int regulator_get_voltage(struct regulator *regulator);
> +
>  /*
>   * Helper functions intended to be used by regulator drivers prior registering
>   * their regulators.
> @@ -223,6 +239,11 @@ static inline void regulator_bulk_free(int num_consumers,
>  {
>  }
>  
> +static inline int regulator_get_voltage(struct regulator *regulator)
> +{
> +	return -EINVAL;
> +}
> +
>  #endif
>  
>  #endif /* __REGULATOR_H */
> -- 
> 2.30.0
> 
> 
> _______________________________________________
> barebox mailing list
> barebox@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/barebox
> 

-- 
Pengutronix e.K.                           |                             |
Steuerwalder Str. 21                       | http://www.pengutronix.de/  |
31137 Hildesheim, Germany                  | Phone: +49-5121-206917-0    |
Amtsgericht Hildesheim, HRA 2686           | Fax:   +49-5121-206917-5555 |

_______________________________________________
barebox mailing list
barebox@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/barebox

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

end of thread, other threads:[~2021-02-10  9:15 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-02-08 19:18 [PATCH 1/4] regulator: add regulator_get_voltage() to API Ahmad Fatoum
2021-02-08 19:18 ` [PATCH 2/4] regulator: add support for struct regulator_desc::off_on_delay Ahmad Fatoum
2021-02-08 19:18 ` [PATCH 3/4] regulator: add driver for stm32-vrefbuf Ahmad Fatoum
2021-02-08 19:18 ` [PATCH 4/4] aiodev: add support for STM32 ADC Ahmad Fatoum
2021-02-09  8:00   ` [PATCH] fixup! " Ahmad Fatoum
2021-02-10  9:15 ` [PATCH 1/4] regulator: add regulator_get_voltage() to API Sascha Hauer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox