summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAhmad Fatoum <a.fatoum@pengutronix.de>2020-02-20 11:01:10 +0100
committerSascha Hauer <s.hauer@pengutronix.de>2020-02-25 09:10:40 +0100
commit3477974f9eb21d8a3ecfb330af3b5ef0c08acee2 (patch)
tree01abd3281065a779e1a12412dd79739440aa0031
parentc4d7a0118e2b58eccc93267525105e368cb4e8f6 (diff)
downloadbarebox-3477974f9eb21d8a3ecfb330af3b5ef0c08acee2.tar.gz
barebox-3477974f9eb21d8a3ecfb330af3b5ef0c08acee2.tar.xz
regulator: port over Linux stm32 PWR regulator driver
This driver supports internal regulators (1V1, 1V8, 3V3) in the STMicroelectronics STM32 chips. Control of these regulators will be required when adding USB support later on. Imported here is the Linux v5.6-rc1 state of the driver. Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
-rw-r--r--drivers/regulator/Kconfig7
-rw-r--r--drivers/regulator/Makefile1
-rw-r--r--drivers/regulator/stm32-pwr.c215
3 files changed, 223 insertions, 0 deletions
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index f47a115da2..1ce057180a 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -21,6 +21,13 @@ config REGULATOR_PFUZE
depends on I2C
depends on ARCH_IMX6 || ARCH_IMX8MQ
+config REGULATOR_STM32_PWR
+ bool "STMicroelectronics STM32 PWR"
+ depends on ARCH_STM32MP
+ help
+ This driver supports internal regulators (1V1, 1V8, 3V3) in the
+ STMicroelectronics STM32 chips.
+
config REGULATOR_STPMIC1
tristate "STMicroelectronics STPMIC1 PMIC Regulators"
depends on MFD_STPMIC1
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index e27e155cf6..4d0bba6c52 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -5,3 +5,4 @@ obj-$(CONFIG_REGULATOR_BCM283X) += bcm2835.o
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
diff --git a/drivers/regulator/stm32-pwr.c b/drivers/regulator/stm32-pwr.c
new file mode 100644
index 0000000000..296f95bc4c
--- /dev/null
+++ b/drivers/regulator/stm32-pwr.c
@@ -0,0 +1,215 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) STMicroelectronics 2019
+// Authors: Gabriel Fernandez <gabriel.fernandez@st.com>
+// Pascal Paillet <p.paillet@st.com>.
+
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <linux/iopoll.h>
+#include <of.h>
+#include <regulator.h>
+
+/*
+ * Registers description
+ */
+#define REG_PWR_CR3 0x0C
+
+#define USB_3_3_EN BIT(24)
+#define USB_3_3_RDY BIT(26)
+#define REG_1_8_EN BIT(28)
+#define REG_1_8_RDY BIT(29)
+#define REG_1_1_EN BIT(30)
+#define REG_1_1_RDY BIT(31)
+
+/* list of supported regulators */
+enum {
+ PWR_REG11,
+ PWR_REG18,
+ PWR_USB33,
+ STM32PWR_REG_NUM_REGS
+};
+
+struct stm32_pwr_desc {
+ struct regulator_desc desc;
+ const char *name;
+ const char *supply_name;
+};
+
+static u32 ready_mask_table[STM32PWR_REG_NUM_REGS] = {
+ [PWR_REG11] = REG_1_1_RDY,
+ [PWR_REG18] = REG_1_8_RDY,
+ [PWR_USB33] = USB_3_3_RDY,
+};
+
+struct stm32_pwr_reg {
+ void __iomem *base;
+ struct device_d *dev;
+ u32 ready_mask;
+ struct regulator_dev rdev;
+ struct regulator *supply;
+};
+
+static inline struct stm32_pwr_reg *to_pwr_reg(struct regulator_dev *rdev)
+{
+ return container_of(rdev, struct stm32_pwr_reg, rdev);
+}
+
+static inline struct stm32_pwr_desc *to_desc(struct regulator_dev *rdev)
+{
+ return container_of(rdev->desc, struct stm32_pwr_desc, desc);
+}
+
+static int stm32_pwr_reg_is_ready(struct regulator_dev *rdev)
+{
+ struct stm32_pwr_reg *priv = to_pwr_reg(rdev);
+ u32 val;
+
+ val = readl(priv->base + REG_PWR_CR3);
+
+ return (val & priv->ready_mask);
+}
+
+static int stm32_pwr_reg_is_enabled(struct regulator_dev *rdev)
+{
+ struct stm32_pwr_reg *priv = to_pwr_reg(rdev);
+ u32 val;
+
+ val = readl(priv->base + REG_PWR_CR3);
+
+ return (val & rdev->desc->enable_mask);
+}
+
+static int stm32_pwr_reg_enable(struct regulator_dev *rdev)
+{
+ struct stm32_pwr_reg *priv = to_pwr_reg(rdev);
+ struct stm32_pwr_desc *desc = to_desc(rdev);
+ int ret;
+ u32 val;
+
+ regulator_enable(priv->supply);
+
+ val = readl(priv->base + REG_PWR_CR3);
+ val |= rdev->desc->enable_mask;
+ writel(val, priv->base + REG_PWR_CR3);
+
+ /* use an arbitrary timeout of 20ms */
+ ret = readx_poll_timeout(stm32_pwr_reg_is_ready, rdev, val, val,
+ 20 * USEC_PER_MSEC);
+ if (ret)
+ dev_err(priv->dev, "%s: regulator enable timed out!\n",
+ desc->name);
+
+ return ret;
+}
+
+static int stm32_pwr_reg_disable(struct regulator_dev *rdev)
+{
+ struct stm32_pwr_reg *priv = to_pwr_reg(rdev);
+ struct stm32_pwr_desc *desc = to_desc(rdev);
+ int ret;
+ u32 val;
+
+ val = readl(priv->base + REG_PWR_CR3);
+ val &= ~rdev->desc->enable_mask;
+ writel(val, priv->base + REG_PWR_CR3);
+
+ /* use an arbitrary timeout of 20ms */
+ ret = readx_poll_timeout(stm32_pwr_reg_is_ready, rdev, val, !val,
+ 20 * USEC_PER_MSEC);
+ if (ret)
+ dev_err(priv->dev, "%s: regulator disable timed out!\n",
+ desc->name);
+
+ regulator_disable(priv->supply);
+
+ return ret;
+}
+
+static const struct regulator_ops stm32_pwr_reg_ops = {
+ .enable = stm32_pwr_reg_enable,
+ .disable = stm32_pwr_reg_disable,
+ .is_enabled = stm32_pwr_reg_is_enabled,
+};
+
+#define PWR_REG(_id, _name, _volt, _en, _supply) \
+ [_id] = { { \
+ .n_voltages = 1, \
+ .ops = &stm32_pwr_reg_ops, \
+ .min_uV = _volt, \
+ .enable_mask = _en, \
+ }, .name = _name, .supply_name = _supply, }
+
+static const struct stm32_pwr_desc stm32_pwr_desc[] = {
+ PWR_REG(PWR_REG11, "reg11", 1100000, REG_1_1_EN, "vdd"),
+ PWR_REG(PWR_REG18, "reg18", 1800000, REG_1_8_EN, "vdd"),
+ PWR_REG(PWR_USB33, "usb33", 3300000, USB_3_3_EN, "vdd_3v3_usbfs"),
+};
+
+static int stm32_pwr_regulator_probe(struct device_d *dev)
+{
+ struct stm32_pwr_reg *priv;
+ struct device_node *child;
+ struct resource *iores;
+ int i, ret;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ for_each_child_of_node(dev->device_node, child) {
+ const struct stm32_pwr_desc *desc = NULL;
+
+ for (i = 0; i < STM32PWR_REG_NUM_REGS; i++) {
+ const char *name;
+
+ name = of_get_property(child, "regulator-name", NULL);
+ if (name && strcmp(stm32_pwr_desc[i].name, name)) {
+ desc = &stm32_pwr_desc[i];
+ break;
+ }
+ }
+
+ if (!desc) {
+ dev_warn(dev, "Skipping unknown child node %s\n",
+ child->name);
+ continue;
+ }
+
+ priv = xzalloc(sizeof(*priv));
+ priv->base = IOMEM(iores->start);
+ priv->ready_mask = ready_mask_table[i];
+ priv->dev = dev;
+
+ priv->rdev.desc = &desc->desc;
+
+ priv->supply = regulator_get(dev, desc->supply_name);
+ if (IS_ERR(priv->supply))
+ return PTR_ERR(priv->supply);
+
+ ret = of_regulator_register(&priv->rdev, child);
+ if (ret) {
+ dev_err(dev, "%s: Failed to register regulator: %d\n",
+ desc->name, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct of_device_id stm32_pwr_of_match[] = {
+ { .compatible = "st,stm32mp1,pwr-reg", },
+ { /* sentinel */ },
+};
+
+static struct driver_d stm32_pwr_driver = {
+ .probe = stm32_pwr_regulator_probe,
+ .name = "stm32-pwr-regulator",
+ .of_compatible = stm32_pwr_of_match,
+};
+device_platform_driver(stm32_pwr_driver);
+
+MODULE_DESCRIPTION("STM32MP1 PWR voltage regulator driver");
+MODULE_AUTHOR("Pascal Paillet <p.paillet@st.com>");
+MODULE_LICENSE("GPL v2");