All of lore.kernel.org
 help / color / mirror / Atom feed
From: Fabrice Gasnier <fabrice.gasnier@st.com>
To: <broonie@kernel.org>, <lgirdwood@gmail.com>, <robh+dt@kernel.org>,
	<alexandre.torgue@st.com>
Cc: <mark.rutland@arm.com>, <mcoquelin.stm32@gmail.com>,
	<fabrice.gasnier@st.com>, <linux@armlinux.org.uk>,
	<devicetree@vger.kernel.org>,
	<linux-arm-kernel@lists.infradead.org>,
	<linux-kernel@vger.kernel.org>
Subject: [RESEND PATCH 2/3] regulator: Add support for stm32-vrefbuf
Date: Mon, 28 Aug 2017 14:58:52 +0200	[thread overview]
Message-ID: <1503925133-30722-3-git-send-email-fabrice.gasnier@st.com> (raw)
In-Reply-To: <1503925133-30722-1-git-send-email-fabrice.gasnier@st.com>

Add regulator driver for STM32 voltage reference buffer which can be
used as voltage reference for ADCs, DACs and external components through
dedicated VREF+ pin.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 drivers/regulator/Kconfig         |  12 +++
 drivers/regulator/Makefile        |   1 +
 drivers/regulator/stm32-vrefbuf.c | 215 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 228 insertions(+)
 create mode 100644 drivers/regulator/stm32-vrefbuf.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 99b9362..2cb2c63 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -746,6 +746,18 @@ config REGULATOR_SKY81452
 	  This driver can also be built as a module. If so, the module
 	  will be called sky81452-regulator.
 
+config REGULATOR_STM32_VREFBUF
+	tristate "STMicroelectronics STM32 VREFBUF"
+	depends on ARCH_STM32 || 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.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called stm32-vrefbuf.
+
 config REGULATOR_TI_ABB
 	tristate "TI Adaptive Body Bias on-chip LDO"
 	depends on ARCH_OMAP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 95b1e86..5af3788 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -94,6 +94,7 @@ obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
 obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
 obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
 obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
+obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
 obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o
 obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
 obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o
diff --git a/drivers/regulator/stm32-vrefbuf.c b/drivers/regulator/stm32-vrefbuf.c
new file mode 100644
index 0000000..a4efa3b
--- /dev/null
+++ b/drivers/regulator/stm32-vrefbuf.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_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)
+
+struct stm32_vrefbuf {
+	void __iomem *base;
+	struct clk *clk;
+};
+
+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 = rdev_get_drvdata(rdev);
+	u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+	int ret;
+
+	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), 650, 10000);
+	if (ret) {
+		dev_err(&rdev->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 = rdev_get_drvdata(rdev);
+	u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+
+	val = (val & ~STM32_ENVR) | STM32_HIZ;
+	writel_relaxed(val, priv->base + STM32_VREFBUF_CSR);
+
+	return 0;
+}
+
+static int stm32_vrefbuf_is_enabled(struct regulator_dev *rdev)
+{
+	struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+
+	return readl_relaxed(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR;
+}
+
+static int stm32_vrefbuf_set_voltage_sel(struct regulator_dev *rdev,
+					 unsigned sel)
+{
+	struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+	u32 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 = rdev_get_drvdata(rdev);
+	u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+
+	return FIELD_GET(STM32_VRS, val);
+}
+
+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 regulator_desc stm32_vrefbuf_regu = {
+	.name = "vref",
+	.supply_name = "vdda",
+	.volt_table = stm32_vrefbuf_voltages,
+	.n_voltages = ARRAY_SIZE(stm32_vrefbuf_voltages),
+	.ops = &stm32_vrefbuf_volt_ops,
+	.type = REGULATOR_VOLTAGE,
+	.owner = THIS_MODULE,
+};
+
+static int stm32_vrefbuf_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct stm32_vrefbuf *priv;
+	struct regulator_config config = { };
+	struct regulator_dev *rdev;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	priv->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "clk prepare failed\n");
+		return ret;
+	}
+
+	config.dev = &pdev->dev;
+	config.driver_data = priv;
+	config.of_node = pdev->dev.of_node;
+	config.init_data = of_get_regulator_init_data(&pdev->dev,
+						      pdev->dev.of_node,
+						      &stm32_vrefbuf_regu);
+
+	rdev = regulator_register(&stm32_vrefbuf_regu, &config);
+	if (IS_ERR(rdev)) {
+		dev_err(&pdev->dev, "vrefbuf register failed\n");
+		ret = PTR_ERR(rdev);
+		goto err_clk_dis;
+	}
+	platform_set_drvdata(pdev, rdev);
+
+	dev_info(&pdev->dev, "STM32 VREFBUF initialized\n");
+
+	return 0;
+
+err_clk_dis:
+	clk_disable_unprepare(priv->clk);
+
+	return ret;
+}
+
+static int stm32_vrefbuf_remove(struct platform_device *pdev)
+{
+	struct regulator_dev *rdev = platform_get_drvdata(pdev);
+	struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+
+	regulator_unregister(rdev);
+	clk_disable_unprepare(priv->clk);
+
+	return 0;
+};
+
+static const struct of_device_id stm32_vrefbuf_of_match[] = {
+	{ .compatible = "st,stm32-vrefbuf", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, stm32_vrefbuf_of_match);
+
+static struct platform_driver stm32_vrefbuf_driver = {
+	.probe = stm32_vrefbuf_probe,
+	.remove = stm32_vrefbuf_remove,
+	.driver = {
+		.name  = "stm32-vrefbuf",
+		.of_match_table = of_match_ptr(stm32_vrefbuf_of_match),
+	},
+};
+
+static int __init stm32_vrefbuf_init(void)
+{
+	return platform_driver_register(&stm32_vrefbuf_driver);
+}
+subsys_initcall(stm32_vrefbuf_init);
+
+static void __exit stm32_vrefbuf_exit(void)
+{
+	platform_driver_unregister(&stm32_vrefbuf_driver);
+}
+module_exit(stm32_vrefbuf_exit)
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 VREFBUF driver");
+MODULE_ALIAS("platform:stm32-vrefbuf");
-- 
1.9.1

WARNING: multiple messages have this Message-ID (diff)
From: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
To: broonie-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
	lgirdwood-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org,
	alexandre.torgue-qxv4g6HH51o@public.gmane.org
Cc: mark.rutland-5wv7dgnIgG8@public.gmane.org,
	mcoquelin.stm32-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org,
	fabrice.gasnier-qxv4g6HH51o@public.gmane.org,
	linux-I+IVW8TIWO2tmTQ+vhA3Yw@public.gmane.org,
	devicetree-u79uwXL29TY76Z2rM5mHXA@public.gmane.org,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r@public.gmane.org,
	linux-kernel-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
Subject: [RESEND PATCH 2/3] regulator: Add support for stm32-vrefbuf
Date: Mon, 28 Aug 2017 14:58:52 +0200	[thread overview]
Message-ID: <1503925133-30722-3-git-send-email-fabrice.gasnier@st.com> (raw)
In-Reply-To: <1503925133-30722-1-git-send-email-fabrice.gasnier-qxv4g6HH51o@public.gmane.org>

Add regulator driver for STM32 voltage reference buffer which can be
used as voltage reference for ADCs, DACs and external components through
dedicated VREF+ pin.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
---
 drivers/regulator/Kconfig         |  12 +++
 drivers/regulator/Makefile        |   1 +
 drivers/regulator/stm32-vrefbuf.c | 215 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 228 insertions(+)
 create mode 100644 drivers/regulator/stm32-vrefbuf.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 99b9362..2cb2c63 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -746,6 +746,18 @@ config REGULATOR_SKY81452
 	  This driver can also be built as a module. If so, the module
 	  will be called sky81452-regulator.
 
+config REGULATOR_STM32_VREFBUF
+	tristate "STMicroelectronics STM32 VREFBUF"
+	depends on ARCH_STM32 || 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.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called stm32-vrefbuf.
+
 config REGULATOR_TI_ABB
 	tristate "TI Adaptive Body Bias on-chip LDO"
 	depends on ARCH_OMAP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 95b1e86..5af3788 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -94,6 +94,7 @@ obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
 obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
 obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
 obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
+obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
 obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o
 obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
 obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o
diff --git a/drivers/regulator/stm32-vrefbuf.c b/drivers/regulator/stm32-vrefbuf.c
new file mode 100644
index 0000000..a4efa3b
--- /dev/null
+++ b/drivers/regulator/stm32-vrefbuf.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_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)
+
+struct stm32_vrefbuf {
+	void __iomem *base;
+	struct clk *clk;
+};
+
+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 = rdev_get_drvdata(rdev);
+	u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+	int ret;
+
+	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), 650, 10000);
+	if (ret) {
+		dev_err(&rdev->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 = rdev_get_drvdata(rdev);
+	u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+
+	val = (val & ~STM32_ENVR) | STM32_HIZ;
+	writel_relaxed(val, priv->base + STM32_VREFBUF_CSR);
+
+	return 0;
+}
+
+static int stm32_vrefbuf_is_enabled(struct regulator_dev *rdev)
+{
+	struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+
+	return readl_relaxed(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR;
+}
+
+static int stm32_vrefbuf_set_voltage_sel(struct regulator_dev *rdev,
+					 unsigned sel)
+{
+	struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+	u32 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 = rdev_get_drvdata(rdev);
+	u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+
+	return FIELD_GET(STM32_VRS, val);
+}
+
+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 regulator_desc stm32_vrefbuf_regu = {
+	.name = "vref",
+	.supply_name = "vdda",
+	.volt_table = stm32_vrefbuf_voltages,
+	.n_voltages = ARRAY_SIZE(stm32_vrefbuf_voltages),
+	.ops = &stm32_vrefbuf_volt_ops,
+	.type = REGULATOR_VOLTAGE,
+	.owner = THIS_MODULE,
+};
+
+static int stm32_vrefbuf_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct stm32_vrefbuf *priv;
+	struct regulator_config config = { };
+	struct regulator_dev *rdev;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	priv->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "clk prepare failed\n");
+		return ret;
+	}
+
+	config.dev = &pdev->dev;
+	config.driver_data = priv;
+	config.of_node = pdev->dev.of_node;
+	config.init_data = of_get_regulator_init_data(&pdev->dev,
+						      pdev->dev.of_node,
+						      &stm32_vrefbuf_regu);
+
+	rdev = regulator_register(&stm32_vrefbuf_regu, &config);
+	if (IS_ERR(rdev)) {
+		dev_err(&pdev->dev, "vrefbuf register failed\n");
+		ret = PTR_ERR(rdev);
+		goto err_clk_dis;
+	}
+	platform_set_drvdata(pdev, rdev);
+
+	dev_info(&pdev->dev, "STM32 VREFBUF initialized\n");
+
+	return 0;
+
+err_clk_dis:
+	clk_disable_unprepare(priv->clk);
+
+	return ret;
+}
+
+static int stm32_vrefbuf_remove(struct platform_device *pdev)
+{
+	struct regulator_dev *rdev = platform_get_drvdata(pdev);
+	struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+
+	regulator_unregister(rdev);
+	clk_disable_unprepare(priv->clk);
+
+	return 0;
+};
+
+static const struct of_device_id stm32_vrefbuf_of_match[] = {
+	{ .compatible = "st,stm32-vrefbuf", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, stm32_vrefbuf_of_match);
+
+static struct platform_driver stm32_vrefbuf_driver = {
+	.probe = stm32_vrefbuf_probe,
+	.remove = stm32_vrefbuf_remove,
+	.driver = {
+		.name  = "stm32-vrefbuf",
+		.of_match_table = of_match_ptr(stm32_vrefbuf_of_match),
+	},
+};
+
+static int __init stm32_vrefbuf_init(void)
+{
+	return platform_driver_register(&stm32_vrefbuf_driver);
+}
+subsys_initcall(stm32_vrefbuf_init);
+
+static void __exit stm32_vrefbuf_exit(void)
+{
+	platform_driver_unregister(&stm32_vrefbuf_driver);
+}
+module_exit(stm32_vrefbuf_exit)
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier-qxv4g6HH51o@public.gmane.org>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 VREFBUF driver");
+MODULE_ALIAS("platform:stm32-vrefbuf");
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

WARNING: multiple messages have this Message-ID (diff)
From: fabrice.gasnier@st.com (Fabrice Gasnier)
To: linux-arm-kernel@lists.infradead.org
Subject: [RESEND PATCH 2/3] regulator: Add support for stm32-vrefbuf
Date: Mon, 28 Aug 2017 14:58:52 +0200	[thread overview]
Message-ID: <1503925133-30722-3-git-send-email-fabrice.gasnier@st.com> (raw)
In-Reply-To: <1503925133-30722-1-git-send-email-fabrice.gasnier@st.com>

Add regulator driver for STM32 voltage reference buffer which can be
used as voltage reference for ADCs, DACs and external components through
dedicated VREF+ pin.

Signed-off-by: Fabrice Gasnier <fabrice.gasnier@st.com>
---
 drivers/regulator/Kconfig         |  12 +++
 drivers/regulator/Makefile        |   1 +
 drivers/regulator/stm32-vrefbuf.c | 215 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 228 insertions(+)
 create mode 100644 drivers/regulator/stm32-vrefbuf.c

diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 99b9362..2cb2c63 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -746,6 +746,18 @@ config REGULATOR_SKY81452
 	  This driver can also be built as a module. If so, the module
 	  will be called sky81452-regulator.
 
+config REGULATOR_STM32_VREFBUF
+	tristate "STMicroelectronics STM32 VREFBUF"
+	depends on ARCH_STM32 || 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.
+
+	  This driver can also be built as a module. If so, the module
+	  will be called stm32-vrefbuf.
+
 config REGULATOR_TI_ABB
 	tristate "TI Adaptive Body Bias on-chip LDO"
 	depends on ARCH_OMAP
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 95b1e86..5af3788 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -94,6 +94,7 @@ obj-$(CONFIG_REGULATOR_S2MPA01) += s2mpa01.o
 obj-$(CONFIG_REGULATOR_S2MPS11) += s2mps11.o
 obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
 obj-$(CONFIG_REGULATOR_SKY81452) += sky81452-regulator.o
+obj-$(CONFIG_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
 obj-$(CONFIG_REGULATOR_STW481X_VMMC) += stw481x-vmmc.o
 obj-$(CONFIG_REGULATOR_TI_ABB) += ti-abb-regulator.o
 obj-$(CONFIG_REGULATOR_TPS6105X) += tps6105x-regulator.o
diff --git a/drivers/regulator/stm32-vrefbuf.c b/drivers/regulator/stm32-vrefbuf.c
new file mode 100644
index 0000000..a4efa3b
--- /dev/null
+++ b/drivers/regulator/stm32-vrefbuf.c
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) STMicroelectronics 2017
+ *
+ * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
+ *
+ * License terms:  GNU General Public License (GPL), version 2
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_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)
+
+struct stm32_vrefbuf {
+	void __iomem *base;
+	struct clk *clk;
+};
+
+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 = rdev_get_drvdata(rdev);
+	u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+	int ret;
+
+	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), 650, 10000);
+	if (ret) {
+		dev_err(&rdev->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 = rdev_get_drvdata(rdev);
+	u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+
+	val = (val & ~STM32_ENVR) | STM32_HIZ;
+	writel_relaxed(val, priv->base + STM32_VREFBUF_CSR);
+
+	return 0;
+}
+
+static int stm32_vrefbuf_is_enabled(struct regulator_dev *rdev)
+{
+	struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+
+	return readl_relaxed(priv->base + STM32_VREFBUF_CSR) & STM32_ENVR;
+}
+
+static int stm32_vrefbuf_set_voltage_sel(struct regulator_dev *rdev,
+					 unsigned sel)
+{
+	struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+	u32 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 = rdev_get_drvdata(rdev);
+	u32 val = readl_relaxed(priv->base + STM32_VREFBUF_CSR);
+
+	return FIELD_GET(STM32_VRS, val);
+}
+
+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 regulator_desc stm32_vrefbuf_regu = {
+	.name = "vref",
+	.supply_name = "vdda",
+	.volt_table = stm32_vrefbuf_voltages,
+	.n_voltages = ARRAY_SIZE(stm32_vrefbuf_voltages),
+	.ops = &stm32_vrefbuf_volt_ops,
+	.type = REGULATOR_VOLTAGE,
+	.owner = THIS_MODULE,
+};
+
+static int stm32_vrefbuf_probe(struct platform_device *pdev)
+{
+	struct resource *res;
+	struct stm32_vrefbuf *priv;
+	struct regulator_config config = { };
+	struct regulator_dev *rdev;
+	int ret;
+
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	priv->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(priv->clk))
+		return PTR_ERR(priv->clk);
+
+	ret = clk_prepare_enable(priv->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "clk prepare failed\n");
+		return ret;
+	}
+
+	config.dev = &pdev->dev;
+	config.driver_data = priv;
+	config.of_node = pdev->dev.of_node;
+	config.init_data = of_get_regulator_init_data(&pdev->dev,
+						      pdev->dev.of_node,
+						      &stm32_vrefbuf_regu);
+
+	rdev = regulator_register(&stm32_vrefbuf_regu, &config);
+	if (IS_ERR(rdev)) {
+		dev_err(&pdev->dev, "vrefbuf register failed\n");
+		ret = PTR_ERR(rdev);
+		goto err_clk_dis;
+	}
+	platform_set_drvdata(pdev, rdev);
+
+	dev_info(&pdev->dev, "STM32 VREFBUF initialized\n");
+
+	return 0;
+
+err_clk_dis:
+	clk_disable_unprepare(priv->clk);
+
+	return ret;
+}
+
+static int stm32_vrefbuf_remove(struct platform_device *pdev)
+{
+	struct regulator_dev *rdev = platform_get_drvdata(pdev);
+	struct stm32_vrefbuf *priv = rdev_get_drvdata(rdev);
+
+	regulator_unregister(rdev);
+	clk_disable_unprepare(priv->clk);
+
+	return 0;
+};
+
+static const struct of_device_id stm32_vrefbuf_of_match[] = {
+	{ .compatible = "st,stm32-vrefbuf", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, stm32_vrefbuf_of_match);
+
+static struct platform_driver stm32_vrefbuf_driver = {
+	.probe = stm32_vrefbuf_probe,
+	.remove = stm32_vrefbuf_remove,
+	.driver = {
+		.name  = "stm32-vrefbuf",
+		.of_match_table = of_match_ptr(stm32_vrefbuf_of_match),
+	},
+};
+
+static int __init stm32_vrefbuf_init(void)
+{
+	return platform_driver_register(&stm32_vrefbuf_driver);
+}
+subsys_initcall(stm32_vrefbuf_init);
+
+static void __exit stm32_vrefbuf_exit(void)
+{
+	platform_driver_unregister(&stm32_vrefbuf_driver);
+}
+module_exit(stm32_vrefbuf_exit)
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics STM32 VREFBUF driver");
+MODULE_ALIAS("platform:stm32-vrefbuf");
-- 
1.9.1

  parent reply	other threads:[~2017-08-28 13:00 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2017-08-28 12:58 [RESEND PATCH 0/3] Add support for STM32 vrefbuf regulator Fabrice Gasnier
2017-08-28 12:58 ` Fabrice Gasnier
2017-08-28 12:58 ` Fabrice Gasnier
2017-08-28 12:58 ` [RESEND PATCH 1/3] dt-bindings: regulator: Add STM32 Voltage Reference Buffer Fabrice Gasnier
2017-08-28 12:58   ` Fabrice Gasnier
2017-08-28 12:58   ` Fabrice Gasnier
2017-08-28 12:58 ` Fabrice Gasnier [this message]
2017-08-28 12:58   ` [RESEND PATCH 2/3] regulator: Add support for stm32-vrefbuf Fabrice Gasnier
2017-08-28 12:58   ` Fabrice Gasnier
2017-08-29 18:57   ` Mark Brown
2017-08-29 18:57     ` Mark Brown
2017-08-30  9:11     ` Fabrice Gasnier
2017-08-30  9:11       ` Fabrice Gasnier
2017-08-30  9:11       ` Fabrice Gasnier
2017-08-30 15:02       ` Mark Brown
2017-08-30 15:02         ` Mark Brown
2017-08-30 15:23         ` Fabrice Gasnier
2017-08-30 15:23           ` Fabrice Gasnier
2017-08-30 15:23           ` Fabrice Gasnier
2017-08-28 12:58 ` [RESEND PATCH 3/3] ARM: dts: stm32: add vrefbuf to stm32h743 Fabrice Gasnier
2017-08-28 12:58   ` Fabrice Gasnier
2017-08-28 12:58   ` Fabrice Gasnier

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=1503925133-30722-3-git-send-email-fabrice.gasnier@st.com \
    --to=fabrice.gasnier@st.com \
    --cc=alexandre.torgue@st.com \
    --cc=broonie@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=lgirdwood@gmail.com \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@armlinux.org.uk \
    --cc=mark.rutland@arm.com \
    --cc=mcoquelin.stm32@gmail.com \
    --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.