Add a DB8500 regulator driver for the VAPE and VSMPS2 compatibility nodes.

Back the regulator enable state with the corresponding power domains.

This is done for off-chip consumers: the corresponding voltage rails are
routed out so they are used for powering different peripherals using
these voltages as supplies.

Assisted-by: Codex:gpt-5-5
Signed-off-by: Linus Walleij <[email protected]>
---
 arch/arm/boot/dts/st/ste-dbx5x0.dtsi |   2 +
 drivers/regulator/Kconfig            |  11 ++
 drivers/regulator/Makefile           |   1 +
 drivers/regulator/db8500-regulator.c | 221 +++++++++++++++++++++++++++++++++++
 4 files changed, 235 insertions(+)

diff --git a/arch/arm/boot/dts/st/ste-dbx5x0.dtsi 
b/arch/arm/boot/dts/st/ste-dbx5x0.dtsi
index a6fef302c994..fd6a075e4c93 100644
--- a/arch/arm/boot/dts/st/ste-dbx5x0.dtsi
+++ b/arch/arm/boot/dts/st/ste-dbx5x0.dtsi
@@ -673,6 +673,7 @@ db8500-prcmu-regulators {
                                // DB8500_REGULATOR_VAPE
                                db8500_vape_reg: db8500_vape {
                                        regulator-always-on;
+                                       power-domains = <&pm_domains 
DOMAIN_VAPE>;
                                };
 
                                // DB8500_REGULATOR_VARM
@@ -693,6 +694,7 @@ db8500_vsmps1_reg: db8500_vsmps1 {
 
                                // DB8500_REGULATOR_VSMPS2
                                db8500_vsmps2_reg: db8500_vsmps2 {
+                                       power-domains = <&pm_domains 
DOMAIN_VSMPS2>;
                                };
 
                                // DB8500_REGULATOR_VSMPS3
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index acc698c17bd2..8db63d8d3fa4 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -397,6 +397,17 @@ config REGULATOR_DA9210
          converter 12A DC-DC Buck controlled through an I2C
          interface.
 
+config REGULATOR_DB8500
+       bool "ST-Ericsson DB8500 power domain regulators"
+       depends on MFD_DB8500_PRCMU && UX500_PM_DOMAIN && OF
+       default ARCH_U8500
+       help
+         This driver supports the DB8500 VAPE and VSMPS2 regulators.
+         These supplies are represented by generic power domains in hardware,
+         but the same voltage rails are routed out of the chip and used to
+         supply external peripherals.
+         Enable this driver to bridge those regulator consumers to genpd.
+
 config REGULATOR_DA9211
        tristate "Dialog Semiconductor 
DA9211/DA9212/DA9213/DA9223/DA9214/DA9224/DA9215/DA9225 regulator"
        depends on I2C
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 96a02063b843..f4109549525a 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -48,6 +48,7 @@ obj-$(CONFIG_REGULATOR_DA9063)        += da9063-regulator.o
 obj-$(CONFIG_REGULATOR_DA9121) += da9121-regulator.o
 obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o
 obj-$(CONFIG_REGULATOR_DA9211) += da9211-regulator.o
+obj-$(CONFIG_REGULATOR_DB8500) += db8500-regulator.o
 obj-$(CONFIG_REGULATOR_FAN53555) += fan53555.o
 obj-$(CONFIG_REGULATOR_FAN53880) += fan53880.o
 obj-$(CONFIG_REGULATOR_GPIO) += gpio-regulator.o
diff --git a/drivers/regulator/db8500-regulator.c 
b/drivers/regulator/db8500-regulator.c
new file mode 100644
index 000000000000..c5a9a1baaf8e
--- /dev/null
+++ b/drivers/regulator/db8500-regulator.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Linus Walleij <[email protected]>
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/slab.h>
+
+struct db8500_regulator_info {
+       struct regulator_desc desc;
+       struct regulator_init_data init_data;
+       struct device pd_dev;
+       bool enabled;
+};
+
+struct db8500_regulator_match {
+       const char *name;
+       const char *supply_name;
+       const char *constraint_name;
+       int fixed_uV;
+       bool always_on;
+};
+
+static const struct db8500_regulator_match db8500_regulator_matches[] = {
+       {
+               .name = "db8500_vape",
+               .supply_name = "db8500-vape",
+               .constraint_name = "db8500-vape",
+               .always_on = true,
+       }, {
+               .name = "db8500_vsmps2",
+               .supply_name = "db8500-vsmps2",
+               .constraint_name = "db8500-vsmps2",
+               .fixed_uV = 1800000,
+       },
+};
+
+static int db8500_regulator_enable(struct regulator_dev *rdev)
+{
+       struct db8500_regulator_info *info = rdev_get_drvdata(rdev);
+       int ret;
+
+       ret = pm_runtime_resume_and_get(&info->pd_dev);
+       if (ret)
+               return ret;
+
+       info->enabled = true;
+       return 0;
+}
+
+static int db8500_regulator_disable(struct regulator_dev *rdev)
+{
+       struct db8500_regulator_info *info = rdev_get_drvdata(rdev);
+       int ret;
+
+       ret = pm_runtime_put_sync_suspend(&info->pd_dev);
+       if (ret)
+               return ret;
+
+       info->enabled = false;
+       return 0;
+}
+
+static int db8500_regulator_is_enabled(struct regulator_dev *rdev)
+{
+       struct db8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+       return info->enabled;
+}
+
+static int db8500_regulator_get_voltage(struct regulator_dev *rdev)
+{
+       struct db8500_regulator_info *info = rdev_get_drvdata(rdev);
+
+       if (!info->desc.fixed_uV)
+               return -EINVAL;
+
+       return info->desc.fixed_uV;
+}
+
+static const struct regulator_ops db8500_regulator_ops = {
+       .enable = db8500_regulator_enable,
+       .disable = db8500_regulator_disable,
+       .is_enabled = db8500_regulator_is_enabled,
+       .get_voltage = db8500_regulator_get_voltage,
+};
+
+static void db8500_regulator_release(struct device *dev)
+{
+}
+
+static void db8500_regulator_cleanup(void *data)
+{
+       struct db8500_regulator_info *info = data;
+
+       pm_runtime_disable(&info->pd_dev);
+       dev_pm_domain_detach(&info->pd_dev, true);
+       put_device(&info->pd_dev);
+}
+
+static const struct db8500_regulator_match *
+db8500_regulator_match(struct device_node *np)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(db8500_regulator_matches); i++) {
+               if (of_node_name_eq(np, db8500_regulator_matches[i].name))
+                       return &db8500_regulator_matches[i];
+       }
+
+       return NULL;
+}
+
+static int db8500_regulator_register(struct platform_device *pdev,
+                                    struct device_node *np)
+{
+       const struct db8500_regulator_match *match;
+       struct regulator_config config = { };
+       struct db8500_regulator_info *info;
+       struct of_phandle_args pd_args;
+       struct regulator_dev *rdev;
+       const char *cells = "#power-domain-cells";
+       int ret;
+
+       match = db8500_regulator_match(np);
+       if (!match)
+               return 0;
+
+       info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       device_initialize(&info->pd_dev);
+       info->pd_dev.parent = &pdev->dev;
+       info->pd_dev.of_node = np;
+       info->pd_dev.release = db8500_regulator_release;
+       ret = dev_set_name(&info->pd_dev, "%s-pd", match->name);
+       if (ret)
+               goto put_device;
+
+       ret = of_parse_phandle_with_args(np, "power-domains", cells, 0, 
&pd_args);
+       if (ret)
+               goto put_device;
+
+       ret = of_genpd_add_device(&pd_args, &info->pd_dev);
+       of_node_put(pd_args.np);
+       if (ret)
+               goto put_device;
+
+       pm_runtime_enable(&info->pd_dev);
+       ret = devm_add_action_or_reset(&pdev->dev, db8500_regulator_cleanup, 
info);
+       if (ret)
+               return ret;
+
+       info->init_data.constraints.name = match->constraint_name;
+       info->init_data.constraints.valid_ops_mask = REGULATOR_CHANGE_STATUS;
+       info->init_data.constraints.always_on = match->always_on;
+
+       info->desc.name = match->supply_name;
+       info->desc.of_match = match->name;
+       info->desc.ops = &db8500_regulator_ops;
+       info->desc.type = REGULATOR_VOLTAGE;
+       info->desc.owner = THIS_MODULE;
+       info->desc.fixed_uV = match->fixed_uV;
+       config.dev = &pdev->dev;
+       config.init_data = &info->init_data;
+       config.driver_data = info;
+       config.of_node = np;
+       rdev = devm_regulator_register(&pdev->dev, &info->desc, &config);
+       if (IS_ERR(rdev))
+               return PTR_ERR(rdev);
+
+       return 0;
+
+put_device:
+       put_device(&info->pd_dev);
+       return ret;
+}
+
+static int db8500_regulator_probe(struct platform_device *pdev)
+{
+       struct device_node *np;
+       int ret;
+
+       for_each_available_child_of_node(pdev->dev.of_node, np) {
+               ret = db8500_regulator_register(pdev, np);
+               if (ret) {
+                       of_node_put(np);
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static const struct of_device_id db8500_regulator_match_table[] = {
+       { .compatible = "stericsson,db8500-prcmu-regulator" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, db8500_regulator_match_table);
+
+static struct platform_driver db8500_regulator_driver = {
+       .driver = {
+               .name = "db8500-prcmu-regulators",
+               .of_match_table = db8500_regulator_match_table,
+       },
+       .probe = db8500_regulator_probe,
+};
+module_platform_driver(db8500_regulator_driver);
+
+MODULE_AUTHOR("Linus Walleij <[email protected]>");
+MODULE_DESCRIPTION("DB8500 power-domain regulator driver");
+MODULE_LICENSE("GPL");

-- 
2.54.0

Reply via email to