summaryrefslogtreecommitdiff
path: root/drivers/power
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power')
-rw-r--r--drivers/power/domain/Kconfig7
-rw-r--r--drivers/power/domain/Makefile3
-rw-r--r--drivers/power/domain/imx8m-power-domain.c21
-rw-r--r--drivers/power/domain/imx8mp-hsiomix.c4
-rw-r--r--drivers/power/domain/imx8mp-mediamix.c212
-rw-r--r--drivers/power/domain/power-domain-uclass.c90
-rw-r--r--drivers/power/domain/sandbox-power-domain-test.c15
-rw-r--r--drivers/power/domain/sandbox-power-domain.c4
-rw-r--r--drivers/power/pmic/Kconfig10
-rw-r--r--drivers/power/pmic/Makefile31
-rw-r--r--drivers/power/pmic/axp.c1
-rw-r--r--drivers/power/pmic/cpcap.c125
-rw-r--r--drivers/power/regulator/Kconfig16
-rw-r--r--drivers/power/regulator/Makefile48
-rw-r--r--drivers/power/regulator/axp_drivevbus.c57
-rw-r--r--drivers/power/regulator/cpcap_regulator.c275
-rw-r--r--drivers/power/regulator/scmi_regulator.c9
17 files changed, 882 insertions, 46 deletions
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
index bd82d2f7044..5f5218bd8b5 100644
--- a/drivers/power/domain/Kconfig
+++ b/drivers/power/domain/Kconfig
@@ -47,6 +47,13 @@ config IMX8MP_HSIOMIX_BLKCTRL
help
Enable support for manipulating NXP i.MX8MP on-SoC HSIOMIX block controller.
+config IMX8MP_MEDIAMIX_BLKCTRL
+ bool "Enable i.MX8MP MEDIAMIX domain driver"
+ depends on POWER_DOMAIN && IMX8MP
+ select CLK
+ help
+ Enable support for manipulating NXP i.MX8MP on-SoC MEDIAMIX block controller.
+
config MTK_POWER_DOMAIN
bool "Enable the MediaTek power domain driver"
depends on POWER_DOMAIN && ARCH_MEDIATEK
diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile
index 110646c503a..4d20c97d26c 100644
--- a/drivers/power/domain/Makefile
+++ b/drivers/power/domain/Makefile
@@ -3,12 +3,13 @@
# Copyright (c) 2016, NVIDIA CORPORATION.
#
-obj-$(CONFIG_$(XPL_)POWER_DOMAIN) += power-domain-uclass.o
+obj-$(CONFIG_$(PHASE_)POWER_DOMAIN) += power-domain-uclass.o
obj-$(CONFIG_APPLE_PMGR_POWER_DOMAIN) += apple-pmgr.o
obj-$(CONFIG_BCM6328_POWER_DOMAIN) += bcm6328-power-domain.o
obj-$(CONFIG_IMX8_POWER_DOMAIN) += imx8-power-domain-legacy.o imx8-power-domain.o
obj-$(CONFIG_IMX8M_POWER_DOMAIN) += imx8m-power-domain.o
obj-$(CONFIG_IMX8MP_HSIOMIX_BLKCTRL) += imx8mp-hsiomix.o
+obj-$(CONFIG_IMX8MP_MEDIAMIX_BLKCTRL) += imx8mp-mediamix.o
obj-$(CONFIG_MTK_POWER_DOMAIN) += mtk-power-domain.o
obj-$(CONFIG_MESON_GX_VPU_POWER_DOMAIN) += meson-gx-pwrc-vpu.o
obj-$(CONFIG_MESON_EE_POWER_DOMAIN) += meson-ee-pwrc.o
diff --git a/drivers/power/domain/imx8m-power-domain.c b/drivers/power/domain/imx8m-power-domain.c
index c22fbe60675..b44aae78e6d 100644
--- a/drivers/power/domain/imx8m-power-domain.c
+++ b/drivers/power/domain/imx8m-power-domain.c
@@ -40,6 +40,7 @@ DECLARE_GLOBAL_DATA_PTR;
#define IMX8MN_MIPI_A53_DOMAIN BIT(2)
#define IMX8MP_HSIOMIX_A53_DOMAIN BIT(19)
+#define IMX8MP_MEDIAMIX_A53_DOMAIN BIT(12)
#define IMX8MP_USB2_PHY_A53_DOMAIN BIT(5)
#define IMX8MP_USB1_PHY_A53_DOMAIN BIT(4)
#define IMX8MP_PCIE_PHY_A53_DOMAIN BIT(3)
@@ -63,6 +64,7 @@ DECLARE_GLOBAL_DATA_PTR;
#define IMX8MN_MIPI_SW_Pxx_REQ BIT(0)
#define IMX8MP_HSIOMIX_Pxx_REQ BIT(17)
+#define IMX8MP_MEDIAMIX_Pxx_REQ BIT(10)
#define IMX8MP_USB2_PHY_Pxx_REQ BIT(3)
#define IMX8MP_USB1_PHY_Pxx_REQ BIT(2)
#define IMX8MP_PCIE_PHY_SW_Pxx_REQ BIT(1)
@@ -81,6 +83,9 @@ DECLARE_GLOBAL_DATA_PTR;
#define IMX8MP_HSIOMIX_PWRDNACKN BIT(28)
#define IMX8MP_HSIOMIX_PWRDNREQN BIT(12)
+#define IMX8MP_MEDIAMIX_PWRDNACKN BIT(30)
+#define IMX8MP_MEDIAMIX_PWRDNREQN BIT(14)
+
/*
* The PGC offset values in Reference Manual
* (Rev. 1, 01/2018 and the older ones) GPC chapter's
@@ -101,6 +106,7 @@ DECLARE_GLOBAL_DATA_PTR;
#define IMX8MP_PGC_PCIE 13
#define IMX8MP_PGC_USB1 14
#define IMX8MP_PGC_USB2 15
+#define IMX8MP_PGC_MEDIAMIX 22
#define IMX8MP_PGC_HSIOMIX 29
#define GPC_PGC_CTRL(n) (0x800 + (n) * 0x40)
@@ -303,6 +309,17 @@ static const struct imx_pgc_domain imx8mp_pgc_domains[] = {
.pgc = BIT(IMX8MP_PGC_HSIOMIX),
.keep_clocks = true,
},
+
+ [IMX8MP_POWER_DOMAIN_MEDIAMIX] = {
+ .bits = {
+ .pxx = IMX8MP_MEDIAMIX_Pxx_REQ,
+ .map = IMX8MP_MEDIAMIX_A53_DOMAIN,
+ .hskreq = IMX8MP_MEDIAMIX_PWRDNREQN,
+ .hskack = IMX8MP_MEDIAMIX_PWRDNACKN,
+ },
+ .pgc = BIT(IMX8MP_PGC_MEDIAMIX),
+ .keep_clocks = true,
+ },
};
static const struct imx_pgc_regs imx8mp_pgc_regs = {
@@ -489,8 +506,12 @@ static int imx8m_power_domain_bind(struct udevice *dev)
static int imx8m_power_domain_probe(struct udevice *dev)
{
struct imx8m_power_domain_plat *pdata = dev_get_plat(dev);
+ struct power_domain_plat *plat = dev_get_uclass_plat(dev);
int ret;
+ /* Every subdomain has its own device node */
+ plat->subdomains = 1;
+
/* Nothing to do for non-"power-domain" driver instances. */
if (!strstr(dev->name, "power-domain"))
return 0;
diff --git a/drivers/power/domain/imx8mp-hsiomix.c b/drivers/power/domain/imx8mp-hsiomix.c
index 455ad53ef52..1ca43880ef5 100644
--- a/drivers/power/domain/imx8mp-hsiomix.c
+++ b/drivers/power/domain/imx8mp-hsiomix.c
@@ -201,8 +201,12 @@ int imx8mp_hsiomix_bind(struct udevice *dev)
static int imx8mp_hsiomix_probe(struct udevice *dev)
{
struct imx8mp_hsiomix_priv *priv = dev_get_priv(dev);
+ struct power_domain_plat *plat = dev_get_uclass_plat(dev);
int ret;
+ /* Definitions are in imx8mp-power.h */
+ plat->subdomains = 5;
+
priv->base = dev_read_addr_ptr(dev);
ret = clk_get_by_name(dev, "usb", &priv->clk_usb);
diff --git a/drivers/power/domain/imx8mp-mediamix.c b/drivers/power/domain/imx8mp-mediamix.c
new file mode 100644
index 00000000000..504c22f7d36
--- /dev/null
+++ b/drivers/power/domain/imx8mp-mediamix.c
@@ -0,0 +1,212 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * i.MX8 MEDIAMIX control block driver
+ * Copyright (C) 2024 Miquel Raynal <miquel.raynal@bootlin.com>
+ * Inspired from Marek Vasut <marex@denx.de> work on the hsiomix driver.
+ */
+
+#include <asm/io.h>
+#include <clk.h>
+#include <dm.h>
+#include <power-domain-uclass.h>
+#include <linux/delay.h>
+
+#include <dt-bindings/power/imx8mp-power.h>
+
+#define BLK_SFT_RSTN 0x0
+#define BLK_CLK_EN 0x4
+
+struct imx8mp_mediamix_priv {
+ void __iomem *base;
+ struct clk clk_apb;
+ struct clk clk_axi;
+ struct clk clk_disp2;
+ struct power_domain pd_bus;
+ struct power_domain pd_lcdif2;
+};
+
+static int imx8mp_mediamix_on(struct power_domain *power_domain)
+{
+ struct udevice *dev = power_domain->dev;
+ struct imx8mp_mediamix_priv *priv = dev_get_priv(dev);
+ struct power_domain *domain;
+ struct clk *clk;
+ u32 reset;
+ int ret;
+
+ switch (power_domain->id) {
+ case IMX8MP_MEDIABLK_PD_LCDIF_2:
+ domain = &priv->pd_lcdif2;
+ clk = &priv->clk_disp2;
+ reset = BIT(11) | BIT(12) | BIT(24);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Make sure bus domain is awake */
+ ret = power_domain_on(&priv->pd_bus);
+ if (ret)
+ return ret;
+
+ /* Put devices into reset */
+ clrbits_le32(priv->base + BLK_SFT_RSTN, reset);
+
+ /* Enable upstream clocks */
+ ret = clk_enable(&priv->clk_apb);
+ if (ret)
+ goto dis_bus_pd;
+
+ ret = clk_enable(&priv->clk_axi);
+ if (ret)
+ goto dis_apb_clk;
+
+ /* Enable blk-ctrl clock to allow reset to propagate */
+ ret = clk_enable(clk);
+ if (ret)
+ goto dis_axi_clk;
+ setbits_le32(priv->base + BLK_CLK_EN, reset);
+
+ /* Power up upstream GPC domain */
+ ret = power_domain_on(domain);
+ if (ret)
+ goto dis_lcdif_clk;
+
+ /* Wait for reset to propagate */
+ udelay(5);
+
+ /* Release reset */
+ setbits_le32(priv->base + BLK_SFT_RSTN, reset);
+
+ return 0;
+
+dis_lcdif_clk:
+ clk_disable(clk);
+dis_axi_clk:
+ clk_disable(&priv->clk_axi);
+dis_apb_clk:
+ clk_disable(&priv->clk_apb);
+dis_bus_pd:
+ power_domain_off(&priv->pd_bus);
+
+ return ret;
+}
+
+static int imx8mp_mediamix_off(struct power_domain *power_domain)
+{
+ struct udevice *dev = power_domain->dev;
+ struct imx8mp_mediamix_priv *priv = dev_get_priv(dev);
+ struct power_domain *domain;
+ struct clk *clk;
+ u32 reset;
+
+ switch (power_domain->id) {
+ case IMX8MP_MEDIABLK_PD_LCDIF_2:
+ domain = &priv->pd_lcdif2;
+ clk = &priv->clk_disp2;
+ reset = BIT(11) | BIT(12) | BIT(24);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Put devices into reset and disable clocks */
+ clrbits_le32(priv->base + BLK_SFT_RSTN, reset);
+ clrbits_le32(priv->base + BLK_CLK_EN, reset);
+
+ /* Power down upstream GPC domain */
+ power_domain_off(domain);
+
+ clk_disable(clk);
+ clk_disable(&priv->clk_axi);
+ clk_disable(&priv->clk_apb);
+
+ /* Allow bus domain to suspend */
+ power_domain_off(&priv->pd_bus);
+
+ return 0;
+}
+
+static int imx8mp_mediamix_of_xlate(struct power_domain *power_domain,
+ struct ofnode_phandle_args *args)
+{
+ power_domain->id = args->args[0];
+
+ return 0;
+}
+
+static int imx8mp_mediamix_bind(struct udevice *dev)
+{
+ /* Bind child lcdif */
+ return dm_scan_fdt_dev(dev);
+}
+
+static int imx8mp_mediamix_probe(struct udevice *dev)
+{
+ struct power_domain_plat *plat = dev_get_uclass_plat(dev);
+ struct imx8mp_mediamix_priv *priv = dev_get_priv(dev);
+ int ret;
+
+ /* Definitions are in imx8mp-power.h */
+ plat->subdomains = 9;
+
+ priv->base = dev_read_addr_ptr(dev);
+
+ ret = clk_get_by_name(dev, "apb", &priv->clk_apb);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_get_by_name(dev, "axi", &priv->clk_axi);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_get_by_name(dev, "disp2", &priv->clk_disp2);
+ if (ret < 0)
+ return ret;
+
+ ret = power_domain_get_by_name(dev, &priv->pd_bus, "bus");
+ if (ret < 0)
+ return ret;
+
+ ret = power_domain_get_by_name(dev, &priv->pd_lcdif2, "lcdif2");
+ if (ret < 0)
+ goto free_bus_pd;
+
+ return 0;
+
+free_bus_pd:
+ power_domain_free(&priv->pd_bus);
+ return ret;
+}
+
+static int imx8mp_mediamix_remove(struct udevice *dev)
+{
+ struct imx8mp_mediamix_priv *priv = dev_get_priv(dev);
+
+ power_domain_free(&priv->pd_lcdif2);
+ power_domain_free(&priv->pd_bus);
+
+ return 0;
+}
+
+static const struct udevice_id imx8mp_mediamix_ids[] = {
+ { .compatible = "fsl,imx8mp-media-blk-ctrl" },
+ { }
+};
+
+struct power_domain_ops imx8mp_mediamix_ops = {
+ .on = imx8mp_mediamix_on,
+ .off = imx8mp_mediamix_off,
+ .of_xlate = imx8mp_mediamix_of_xlate,
+};
+
+U_BOOT_DRIVER(imx8mp_mediamix) = {
+ .name = "imx8mp_mediamix",
+ .id = UCLASS_POWER_DOMAIN,
+ .of_match = imx8mp_mediamix_ids,
+ .bind = imx8mp_mediamix_bind,
+ .probe = imx8mp_mediamix_probe,
+ .remove = imx8mp_mediamix_remove,
+ .priv_auto = sizeof(struct imx8mp_mediamix_priv),
+ .ops = &imx8mp_mediamix_ops,
+};
diff --git a/drivers/power/domain/power-domain-uclass.c b/drivers/power/domain/power-domain-uclass.c
index 938bd8cbc9f..d9fa8ad4bd2 100644
--- a/drivers/power/domain/power-domain-uclass.c
+++ b/drivers/power/domain/power-domain-uclass.c
@@ -12,6 +12,10 @@
#include <power-domain-uclass.h>
#include <dm/device-internal.h>
+struct power_domain_priv {
+ int *on_count;
+};
+
static inline struct power_domain_ops *power_domain_dev_ops(struct udevice *dev)
{
return (struct power_domain_ops *)dev->driver->ops;
@@ -107,22 +111,67 @@ int power_domain_free(struct power_domain *power_domain)
return ops->rfree ? ops->rfree(power_domain) : 0;
}
-int power_domain_on(struct power_domain *power_domain)
+int power_domain_on_lowlevel(struct power_domain *power_domain)
{
+ struct power_domain_priv *priv = dev_get_uclass_priv(power_domain->dev);
+ struct power_domain_plat *plat = dev_get_uclass_plat(power_domain->dev);
struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
+ int *on_count = plat->subdomains ? &priv->on_count[power_domain->id] : NULL;
+ int ret;
- debug("%s(power_domain=%p)\n", __func__, power_domain);
+ /* Refcounting is not enabled on all drivers by default */
+ if (on_count) {
+ debug("Enable power domain %s.%ld: %d -> %d (%s)\n",
+ power_domain->dev->name, power_domain->id, *on_count, *on_count + 1,
+ (((*on_count + 1) > 1) ? "EALREADY" : "todo"));
+
+ (*on_count)++;
+ if (*on_count > 1)
+ return -EALREADY;
+ }
+
+ ret = ops->on ? ops->on(power_domain) : 0;
+ if (ret) {
+ if (on_count)
+ (*on_count)--;
+ return ret;
+ }
- return ops->on ? ops->on(power_domain) : 0;
+ return 0;
}
-int power_domain_off(struct power_domain *power_domain)
+int power_domain_off_lowlevel(struct power_domain *power_domain)
{
+ struct power_domain_priv *priv = dev_get_uclass_priv(power_domain->dev);
+ struct power_domain_plat *plat = dev_get_uclass_plat(power_domain->dev);
struct power_domain_ops *ops = power_domain_dev_ops(power_domain->dev);
+ int *on_count = plat->subdomains ? &priv->on_count[power_domain->id] : NULL;
+ int ret;
- debug("%s(power_domain=%p)\n", __func__, power_domain);
+ /* Refcounting is not enabled on all drivers by default */
+ if (on_count) {
+ debug("Disable power domain %s.%ld: %d -> %d (%s%s)\n",
+ power_domain->dev->name, power_domain->id, *on_count, *on_count - 1,
+ (((*on_count) <= 0) ? "EALREADY" : ""),
+ (((*on_count - 1) > 0) ? "BUSY" : "todo"));
+
+ if (*on_count <= 0)
+ return -EALREADY;
+
+ (*on_count)--;
+ if (*on_count > 0)
+ return -EBUSY;
+ }
+
+ ret = ops->off ? ops->off(power_domain) : 0;
+ if (ret) {
+ if (on_count)
+ (*on_count)++;
- return ops->off ? ops->off(power_domain) : 0;
+ return ret;
+ }
+
+ return 0;
}
#if CONFIG_IS_ENABLED(OF_REAL)
@@ -177,7 +226,36 @@ int dev_power_domain_off(struct udevice *dev)
}
#endif /* OF_REAL */
+static int power_domain_post_probe(struct udevice *dev)
+{
+ struct power_domain_priv *priv = dev_get_uclass_priv(dev);
+ struct power_domain_plat *plat = dev_get_uclass_plat(dev);
+
+ if (plat->subdomains) {
+ priv->on_count = calloc(sizeof(int), plat->subdomains);
+ if (!priv->on_count)
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static int power_domain_pre_remove(struct udevice *dev)
+{
+ struct power_domain_priv *priv = dev_get_uclass_priv(dev);
+ struct power_domain_plat *plat = dev_get_uclass_plat(dev);
+
+ if (plat->subdomains)
+ free(priv->on_count);
+
+ return 0;
+}
+
UCLASS_DRIVER(power_domain) = {
.id = UCLASS_POWER_DOMAIN,
.name = "power_domain",
+ .post_probe = power_domain_post_probe,
+ .pre_remove = power_domain_pre_remove,
+ .per_device_auto = sizeof(struct power_domain_priv),
+ .per_device_plat_auto = sizeof(struct power_domain_plat),
};
diff --git a/drivers/power/domain/sandbox-power-domain-test.c b/drivers/power/domain/sandbox-power-domain-test.c
index 08c15ef342b..df063001f51 100644
--- a/drivers/power/domain/sandbox-power-domain-test.c
+++ b/drivers/power/domain/sandbox-power-domain-test.c
@@ -34,6 +34,20 @@ int sandbox_power_domain_test_off(struct udevice *dev)
return power_domain_off(&sbrt->pd);
}
+int sandbox_power_domain_test_on_ll(struct udevice *dev)
+{
+ struct sandbox_power_domain_test *sbrt = dev_get_priv(dev);
+
+ return power_domain_on_lowlevel(&sbrt->pd);
+}
+
+int sandbox_power_domain_test_off_ll(struct udevice *dev)
+{
+ struct sandbox_power_domain_test *sbrt = dev_get_priv(dev);
+
+ return power_domain_off_lowlevel(&sbrt->pd);
+}
+
int sandbox_power_domain_test_free(struct udevice *dev)
{
struct sandbox_power_domain_test *sbrt = dev_get_priv(dev);
@@ -51,4 +65,5 @@ U_BOOT_DRIVER(sandbox_power_domain_test) = {
.id = UCLASS_MISC,
.of_match = sandbox_power_domain_test_ids,
.priv_auto = sizeof(struct sandbox_power_domain_test),
+ .flags = DM_FLAG_DEFAULT_PD_CTRL_OFF,
};
diff --git a/drivers/power/domain/sandbox-power-domain.c b/drivers/power/domain/sandbox-power-domain.c
index 9dd490b14a3..a8031657638 100644
--- a/drivers/power/domain/sandbox-power-domain.c
+++ b/drivers/power/domain/sandbox-power-domain.c
@@ -64,8 +64,12 @@ static int sandbox_power_domain_bind(struct udevice *dev)
static int sandbox_power_domain_probe(struct udevice *dev)
{
+ struct power_domain_plat *plat = dev_get_uclass_plat(dev);
+
debug("%s(dev=%p)\n", __func__, dev);
+ plat->subdomains = 1;
+
return 0;
}
diff --git a/drivers/power/pmic/Kconfig b/drivers/power/pmic/Kconfig
index bbcbcee4c35..5a61cd45b8c 100644
--- a/drivers/power/pmic/Kconfig
+++ b/drivers/power/pmic/Kconfig
@@ -295,6 +295,16 @@ config DM_PMIC_SANDBOX
Driver binding info: doc/device-tree-bindings/pmic/sandbox.txt
+config DM_PMIC_CPCAP
+ bool "Enable Driver Model for Motorola CPCAP"
+ help
+ The CPCAP is a Motorola/ST-Ericsson creation, a multifunctional IC
+ whose main purpose is power control. It was used in a wide variety of
+ Motorola products, both Tegra and OMAP based. The most notable devices
+ using this PMIC are the Motorola Droid 4, Atrix 4G, and Droid X2.
+ Unlike most PMICs, this one is not I2C based; it uses the SPI bus. The
+ core driver provides both read and write access to the device registers.
+
config PMIC_S5M8767
bool "Enable Driver Model for the Samsung S5M8767 PMIC"
---help---
diff --git a/drivers/power/pmic/Makefile b/drivers/power/pmic/Makefile
index bc138f563ff..2210b1a64ae 100644
--- a/drivers/power/pmic/Makefile
+++ b/drivers/power/pmic/Makefile
@@ -4,41 +4,42 @@
# Lukasz Majewski <l.majewski@samsung.com>
obj-$(CONFIG_$(PHASE_)DM_PMIC) += pmic-uclass.o
-obj-$(CONFIG_$(XPL_)DM_PMIC_FAN53555) += fan53555.o
-obj-$(CONFIG_$(XPL_)DM_PMIC_DA9063) += da9063.o
-obj-$(CONFIG_$(XPL_)DM_PMIC_MAX77663) += max77663.o
+obj-$(CONFIG_$(PHASE_)DM_PMIC_FAN53555) += fan53555.o
+obj-$(CONFIG_$(PHASE_)DM_PMIC_DA9063) += da9063.o
+obj-$(CONFIG_$(PHASE_)DM_PMIC_MAX77663) += max77663.o
obj-$(CONFIG_DM_PMIC_MAX77686) += max77686.o
obj-$(CONFIG_DM_PMIC_MAX8998) += max8998.o
obj-$(CONFIG_DM_PMIC_MC34708) += mc34708.o
-obj-$(CONFIG_$(XPL_)DM_PMIC_BD71837) += bd71837.o
-obj-$(CONFIG_$(XPL_)DM_PMIC_MP5416) += mp5416.o
-obj-$(CONFIG_$(XPL_)DM_PMIC_PFUZE100) += pfuze100.o
-obj-$(CONFIG_$(XPL_)DM_PMIC_PCA9450) += pca9450.o
+obj-$(CONFIG_$(PHASE_)DM_PMIC_BD71837) += bd71837.o
+obj-$(CONFIG_$(PHASE_)DM_PMIC_MP5416) += mp5416.o
+obj-$(CONFIG_$(PHASE_)DM_PMIC_PFUZE100) += pfuze100.o
+obj-$(CONFIG_$(PHASE_)DM_PMIC_PCA9450) += pca9450.o
obj-$(CONFIG_PMIC_S2MPS11) += s2mps11.o
obj-$(CONFIG_DM_PMIC_SANDBOX) += sandbox.o i2c_pmic_emul.o
obj-$(CONFIG_PMIC_AB8500) += ab8500.o
obj-$(CONFIG_PMIC_ACT8846) += act8846.o
obj-$(CONFIG_PMIC_AS3722) += as3722.o as3722_gpio.o
-obj-$(CONFIG_$(XPL_)PMIC_AXP) += axp.o
+obj-$(CONFIG_$(PHASE_)PMIC_AXP) += axp.o
obj-$(CONFIG_PMIC_MAX8997) += max8997.o
obj-$(CONFIG_PMIC_QCOM) += pmic_qcom.o
obj-$(CONFIG_$(PHASE_)PMIC_RK8XX) += rk8xx.o
-obj-$(CONFIG_$(XPL_)PMIC_RN5T567) += rn5t567.o
+obj-$(CONFIG_$(PHASE_)PMIC_RN5T567) += rn5t567.o
obj-$(CONFIG_PMIC_TPS65090) += tps65090.o
obj-$(CONFIG_PMIC_S5M8767) += s5m8767.o
obj-$(CONFIG_DM_PMIC_TPS65910) += pmic_tps65910_dm.o
-obj-$(CONFIG_$(XPL_)DM_PMIC_TPS80031) += tps80031.o
-obj-$(CONFIG_$(XPL_)PMIC_PALMAS) += palmas.o
-obj-$(CONFIG_$(XPL_)PMIC_LP873X) += lp873x.o
-obj-$(CONFIG_$(XPL_)PMIC_LP87565) += lp87565.o
+obj-$(CONFIG_$(PHASE_)DM_PMIC_TPS80031) += tps80031.o
+obj-$(CONFIG_$(PHASE_)PMIC_PALMAS) += palmas.o
+obj-$(CONFIG_$(PHASE_)PMIC_LP873X) += lp873x.o
+obj-$(CONFIG_$(PHASE_)PMIC_LP87565) += lp87565.o
obj-$(CONFIG_PMIC_STPMIC1) += stpmic1.o
obj-$(CONFIG_PMIC_TPS65217) += pmic_tps65217.o
obj-$(CONFIG_PMIC_TPS65219) += tps65219.o
obj-$(CONFIG_PMIC_TPS65941) += tps65941.o
obj-$(CONFIG_PMIC_RAA215300) += raa215300.o
obj-$(CONFIG_POWER_TPS65218) += pmic_tps65218.o
+obj-$(CONFIG_$(PHASE_)DM_PMIC_CPCAP) += cpcap.o
-ifeq ($(CONFIG_$(XPL_)POWER_LEGACY),y)
+ifeq ($(CONFIG_$(PHASE_)POWER_LEGACY),y)
obj-$(CONFIG_POWER_LTC3676) += pmic_ltc3676.o
obj-$(CONFIG_POWER_PCA9450) += pmic_pca9450.o
obj-$(CONFIG_POWER_PFUZE100) += pmic_pfuze100.o
@@ -47,5 +48,5 @@ obj-$(CONFIG_POWER_HI6553) += pmic_hi6553.o
obj-$(CONFIG_POWER_MC34VR500) += pmic_mc34vr500.o
endif
-obj-$(CONFIG_$(XPL_)POWER_TPS62362) += pmic_tps62362.o
+obj-$(CONFIG_$(PHASE_)POWER_TPS62362) += pmic_tps62362.o
obj-$(CONFIG_SPL_POWER_TPS65910) += pmic_tps65910.o
diff --git a/drivers/power/pmic/axp.c b/drivers/power/pmic/axp.c
index 521a39dd566..c300fd2bbc2 100644
--- a/drivers/power/pmic/axp.c
+++ b/drivers/power/pmic/axp.c
@@ -51,6 +51,7 @@ static const struct pmic_child_info axp_pmic_child_info[] = {
{ "cldo", "axp_regulator" },
{ "dc", "axp_regulator" },
{ "dldo", "axp_regulator" },
+ { "drivevbus", "axp_drivevbus" },
{ "eldo", "axp_regulator" },
{ "fldo", "axp_regulator" },
{ "ldo", "axp_regulator" },
diff --git a/drivers/power/pmic/cpcap.c b/drivers/power/pmic/cpcap.c
new file mode 100644
index 00000000000..f2076afff43
--- /dev/null
+++ b/drivers/power/pmic/cpcap.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (c) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <dm.h>
+#include <dm/lists.h>
+#include <log.h>
+#include <power/pmic.h>
+#include <power/cpcap.h>
+#include <spi.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+static const struct pmic_child_info pmic_children_info[] = {
+ { .prefix = "sw", .driver = CPCAP_SW_DRIVER },
+ { .prefix = "v", .driver = CPCAP_LDO_DRIVER },
+ { },
+};
+
+static int cpcap_write(struct udevice *dev, uint reg, const uint8_t *buff, int len)
+{
+ u8 buf[4];
+ u16 data = *(u16 *)buff;
+ int ret;
+
+ buf[0] = ((reg >> 8) & 0xff) | 0x80;
+ buf[1] = reg & 0xff;
+ buf[2] = data >> 8 & 0xff;
+ buf[3] = data & 0xff;
+
+ ret = dm_spi_xfer(dev, 32, buf, NULL, SPI_XFER_ONCE);
+
+ log_debug("%s: reg 0x%x, data 0x%04x, ret %d\n", __func__, reg, data, ret);
+
+ return ret;
+}
+
+static int cpcap_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
+{
+ u8 buf[4];
+ int ret;
+
+ buf[0] = (reg >> 8) & 0xff;
+ buf[1] = reg & 0xff;
+ buf[2] = 0;
+ buf[3] = 0;
+
+ ret = dm_spi_xfer(dev, 32, buf, buf, SPI_XFER_ONCE);
+ *buff = (buf[2] << 8) | buf[3];
+
+ log_debug("%s: reg 0x%x, data 0x%04x, ret %d\n", __func__, reg, *buff, ret);
+ return ret;
+}
+
+static int cpcap_bind(struct udevice *dev)
+{
+ ofnode regulators_node;
+ int children;
+
+ /* Regulator device node of PMIC */
+ regulators_node = dev_read_subnode(dev, "regulator");
+ if (!ofnode_valid(regulators_node)) {
+ log_err("%s regulator subnode not found!\n", dev->name);
+ return -ENXIO;
+ }
+
+ /* Actual regulators container */
+ regulators_node = ofnode_find_subnode(regulators_node, "regulators");
+ if (!ofnode_valid(regulators_node)) {
+ log_err("%s regulators subnode not found!\n", dev->name);
+ return -ENXIO;
+ }
+
+ debug("%s: '%s' - found regulators subnode\n", __func__, dev->name);
+
+ children = pmic_bind_children(dev, regulators_node, pmic_children_info);
+ if (!children)
+ log_err("%s - no child found\n", dev->name);
+
+ return dm_scan_fdt_dev(dev);
+}
+
+static int cpcap_probe(struct udevice *dev)
+{
+ struct spi_slave *slave = dev_get_parent_priv(dev);
+ int ret;
+
+ ret = spi_claim_bus(slave);
+ if (ret) {
+ log_err("SPI bus allocation failed (%d)\n", ret);
+ return ret;
+ }
+
+ u16 id = pmic_reg_read(dev, CPCAP_REG_VERSC1);
+
+ u16 ven = (id >> 6) & 0x7;
+ u16 rev = ((id >> 3) & 0x7) | ((id << 3) & 0x38);
+
+ log_debug("%s: vendor %s rev: %i.%i (%x)\n", __func__,
+ ven == CPCAP_VENDOR_ST ? "ST" : "TI",
+ CPCAP_REVISION_MAJOR(rev), CPCAP_REVISION_MINOR(rev),
+ rev);
+ return 0;
+}
+
+static struct dm_pmic_ops cpcap_ops = {
+ .read = cpcap_read,
+ .write = cpcap_write,
+};
+
+static const struct udevice_id cpcap_ids[] = {
+ { .compatible = "motorola,cpcap" },
+ { .compatible = "st,6556002" },
+ { }
+};
+
+U_BOOT_DRIVER(pmic_cpcap) = {
+ .name = "cpcap_pmic",
+ .id = UCLASS_PMIC,
+ .of_match = cpcap_ids,
+ .bind = cpcap_bind,
+ .probe = cpcap_probe,
+ .ops = &cpcap_ops,
+};
diff --git a/drivers/power/regulator/Kconfig b/drivers/power/regulator/Kconfig
index bab68317cfa..95912ef5633 100644
--- a/drivers/power/regulator/Kconfig
+++ b/drivers/power/regulator/Kconfig
@@ -57,6 +57,13 @@ config SPL_REGULATOR_AXP
Enable support in SPL for the regulators (DCDCs, LDOs) in the
X-Powers AXP152, AXP2xx, and AXP8xx PMICs.
+config REGULATOR_AXP_DRIVEVBUS
+ bool "Enable driver for X-Powers AXP PMIC drivevbus"
+ depends on DM_REGULATOR && PMIC_AXP
+ help
+ Enable support for sensing or driving the USB VBUS on
+ X-Powers AXP2xx and AXP8xx PMICs.
+
config REGULATOR_AXP_USB_POWER
bool "Enable driver for X-Powers AXP PMIC USB power supply"
depends on DM_REGULATOR && PMIC_AXP
@@ -493,3 +500,12 @@ config REGULATOR_RZG2L_USBPHY
Enable this option to support controlling the VBUS supply in
the USB PHY peripheral of the Renesas RZ/G2L SoC. This option
is required in order to use the USB OTG port.
+
+config DM_REGULATOR_CPCAP
+ bool "Enable driver for CPCAP PMIC regulators"
+ depends on DM_REGULATOR && DM_PMIC_CPCAP
+ ---help---
+ Enable implementation of driver-model regulator uclass features for
+ REGULATOR CPCAP. The driver supports both DC-to-DC Step-Down Switching
+ (SW) Regulators and Low-Dropout Linear (LDO) Regulators found in CPCAP
+ PMIC and implements get/set api for voltage and state.
diff --git a/drivers/power/regulator/Makefile b/drivers/power/regulator/Makefile
index 054a93e129a..0ee5d908a2a 100644
--- a/drivers/power/regulator/Makefile
+++ b/drivers/power/regulator/Makefile
@@ -4,23 +4,24 @@
# Przemyslaw Marczak <p.marczak@samsung.com>
#
-obj-$(CONFIG_$(XPL_)DM_REGULATOR) += regulator-uclass.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR) += regulator-uclass.o
obj-$(CONFIG_REGULATOR_ACT8846) += act8846.o
obj-$(CONFIG_REGULATOR_AS3722) += as3722_regulator.o
-obj-$(CONFIG_$(XPL_)REGULATOR_AXP) += axp_regulator.o
-obj-$(CONFIG_$(XPL_)REGULATOR_AXP_USB_POWER) += axp_usb_power.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_DA9063) += da9063.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_MAX77663) += max77663_regulator.o
+obj-$(CONFIG_$(PHASE_)REGULATOR_AXP) += axp_regulator.o
+obj-$(CONFIG_$(PHASE_)REGULATOR_AXP_DRIVEVBUS) += axp_drivevbus.o
+obj-$(CONFIG_$(PHASE_)REGULATOR_AXP_USB_POWER) += axp_usb_power.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_DA9063) += da9063.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_MAX77663) += max77663_regulator.o
obj-$(CONFIG_DM_REGULATOR_MAX77686) += max77686.o
obj-$(CONFIG_DM_REGULATOR_NPCM8XX) += npcm8xx_regulator.o
-obj-$(CONFIG_$(XPL_)DM_PMIC_PFUZE100) += pfuze100.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_BD71837) += bd71837.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_PCA9450) += pca9450.o
-obj-$(CONFIG_$(XPL_)REGULATOR_PWM) += pwm_regulator.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_FAN53555) += fan53555.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_COMMON) += regulator_common.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_FIXED) += fixed.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_GPIO) += gpio-regulator.o
+obj-$(CONFIG_$(PHASE_)DM_PMIC_PFUZE100) += pfuze100.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_BD71837) += bd71837.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PCA9450) += pca9450.o
+obj-$(CONFIG_$(PHASE_)REGULATOR_PWM) += pwm_regulator.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_FAN53555) += fan53555.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_COMMON) += regulator_common.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_FIXED) += fixed.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_GPIO) += gpio-regulator.o
obj-$(CONFIG_DM_REGULATOR_QCOM_RPMH) += qcom-rpmh-regulator.o
obj-$(CONFIG_DM_REGULATOR_QCOM_USB_VBUS) += qcom_usb_vbus_regulator.o
obj-$(CONFIG_$(PHASE_)REGULATOR_RK8XX) += rk8xx.o
@@ -28,19 +29,20 @@ obj-$(CONFIG_DM_REGULATOR_S2MPS11) += s2mps11_regulator.o
obj-$(CONFIG_REGULATOR_S5M8767) += s5m8767.o
obj-$(CONFIG_DM_REGULATOR_SANDBOX) += sandbox.o
obj-$(CONFIG_REGULATOR_TPS65090) += tps65090_regulator.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_PALMAS) += palmas_regulator.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_PBIAS) += pbias_regulator.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_LP873X) += lp873x_regulator.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_LP87565) += lp87565_regulator.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PALMAS) += palmas_regulator.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_PBIAS) += pbias_regulator.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_LP873X) += lp873x_regulator.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_LP87565) += lp87565_regulator.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_STM32_VREFBUF) += stm32-vrefbuf.o
obj-$(CONFIG_DM_REGULATOR_TPS65910) += tps65910_regulator.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_TPS65911) += tps65911_regulator.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_TPS65911) += tps65911_regulator.o
obj-$(CONFIG_DM_REGULATOR_TPS62360) += tps62360_regulator.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_TPS6287X) += tps6287x_regulator.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_TPS80031) += tps80031_regulator.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_STPMIC1) += stpmic1.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_TPS6287X) += tps6287x_regulator.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_TPS80031) += tps80031_regulator.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_STPMIC1) += stpmic1.o
obj-$(CONFIG_DM_REGULATOR_TPS65941) += tps65941_regulator.o
obj-$(CONFIG_DM_REGULATOR_SCMI) += scmi_regulator.o
-obj-$(CONFIG_$(XPL_)DM_REGULATOR_ANATOP) += anatop_regulator.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_ANATOP) += anatop_regulator.o
obj-$(CONFIG_DM_REGULATOR_TPS65219) += tps65219_regulator.o
obj-$(CONFIG_REGULATOR_RZG2L_USBPHY) += rzg2l-usbphy-regulator.o
+obj-$(CONFIG_$(PHASE_)DM_REGULATOR_CPCAP) += cpcap_regulator.o
diff --git a/drivers/power/regulator/axp_drivevbus.c b/drivers/power/regulator/axp_drivevbus.c
new file mode 100644
index 00000000000..c463de8786b
--- /dev/null
+++ b/drivers/power/regulator/axp_drivevbus.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0+
+
+#include <dm.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+
+#define AXP_VBUS_IPSOUT 0x30
+#define AXP_VBUS_IPSOUT_DRIVEBUS BIT(2)
+#define AXP_MISC_CTRL 0x8f
+#define AXP_MISC_CTRL_N_VBUSEN_FUNC BIT(4)
+
+static int axp_drivevbus_get_enable(struct udevice *dev)
+{
+ int ret;
+
+ ret = pmic_reg_read(dev->parent, AXP_VBUS_IPSOUT);
+ if (ret < 0)
+ return ret;
+
+ return !!(ret & AXP_VBUS_IPSOUT_DRIVEBUS);
+}
+
+static int axp_drivevbus_set_enable(struct udevice *dev, bool enable)
+{
+ return pmic_clrsetbits(dev->parent, AXP_VBUS_IPSOUT,
+ AXP_VBUS_IPSOUT_DRIVEBUS,
+ enable ? AXP_VBUS_IPSOUT_DRIVEBUS : 0);
+}
+
+static const struct dm_regulator_ops axp_drivevbus_ops = {
+ .get_enable = axp_drivevbus_get_enable,
+ .set_enable = axp_drivevbus_set_enable,
+};
+
+static int axp_drivevbus_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_plat *uc_plat = dev_get_uclass_plat(dev);
+ int ret;
+
+ uc_plat->type = REGULATOR_TYPE_FIXED;
+
+ if (dev_read_bool(dev->parent, "x-powers,drive-vbus-en")) {
+ ret = pmic_clrsetbits(dev->parent, AXP_MISC_CTRL,
+ AXP_MISC_CTRL_N_VBUSEN_FUNC, 0);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+U_BOOT_DRIVER(axp_drivevbus) = {
+ .name = "axp_drivevbus",
+ .id = UCLASS_REGULATOR,
+ .probe = axp_drivevbus_probe,
+ .ops = &axp_drivevbus_ops,
+};
diff --git a/drivers/power/regulator/cpcap_regulator.c b/drivers/power/regulator/cpcap_regulator.c
new file mode 100644
index 00000000000..04cd6651374
--- /dev/null
+++ b/drivers/power/regulator/cpcap_regulator.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright(C) 2025 Svyatoslav Ryhel <clamor95@gmail.com>
+ */
+
+#include <dm.h>
+#include <power/pmic.h>
+#include <power/regulator.h>
+#include <power/cpcap.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+
+/* CPCAP_REG_ASSIGN2 bits - Resource Assignment 2 */
+#define CPCAP_BIT_VSDIO_SEL BIT(15)
+#define CPCAP_BIT_VDIG_SEL BIT(14)
+#define CPCAP_BIT_VCAM_SEL BIT(13)
+#define CPCAP_BIT_SW6_SEL BIT(12)
+#define CPCAP_BIT_SW5_SEL BIT(11)
+#define CPCAP_BIT_SW4_SEL BIT(10)
+#define CPCAP_BIT_SW3_SEL BIT(9)
+#define CPCAP_BIT_SW2_SEL BIT(8)
+#define CPCAP_BIT_SW1_SEL BIT(7)
+
+/* CPCAP_REG_ASSIGN3 bits - Resource Assignment 3 */
+#define CPCAP_BIT_VUSBINT2_SEL BIT(15)
+#define CPCAP_BIT_VUSBINT1_SEL BIT(14)
+#define CPCAP_BIT_VVIB_SEL BIT(13)
+#define CPCAP_BIT_VWLAN1_SEL BIT(12)
+#define CPCAP_BIT_VRF1_SEL BIT(11)
+#define CPCAP_BIT_VHVIO_SEL BIT(10)
+#define CPCAP_BIT_VDAC_SEL BIT(9)
+#define CPCAP_BIT_VUSB_SEL BIT(8)
+#define CPCAP_BIT_VSIM_SEL BIT(7)
+#define CPCAP_BIT_VRFREF_SEL BIT(6)
+#define CPCAP_BIT_VPLL_SEL BIT(5)
+#define CPCAP_BIT_VFUSE_SEL BIT(4)
+#define CPCAP_BIT_VCSI_SEL BIT(3)
+#define CPCAP_BIT_SPARE_14_2 BIT(2)
+#define CPCAP_BIT_VWLAN2_SEL BIT(1)
+#define CPCAP_BIT_VRF2_SEL BIT(0)
+#define CPCAP_BIT_NONE 0
+
+/* CPCAP_REG_ASSIGN4 bits - Resource Assignment 4 */
+#define CPCAP_BIT_VAUDIO_SEL BIT(0)
+
+/*
+ * Off mode configuration bit. Used currently only by SW5 on omap4. There's
+ * the following comment in Motorola Linux kernel tree for it:
+ *
+ * When set in the regulator mode, the regulator assignment will be changed
+ * to secondary when the regulator is disabled. The mode will be set back to
+ * primary when the regulator is turned on.
+ */
+#define CPCAP_REG_OFF_MODE_SEC BIT(15)
+
+#define CPCAP_REG(_reg, _assignment_reg, _assignment_mask, _mode_mask, \
+ _volt_mask, _volt_shft, _mode_val, _off_mode_val, _val_tbl, \
+ _mode_cntr, _volt_trans_time, _turn_on_time, _bit_offset) { \
+ .reg = CPCAP_REG_##_reg, \
+ .assignment_reg = CPCAP_REG_##_assignment_reg, \
+ .assignment_mask = CPCAP_BIT_##_assignment_mask, \
+ .mode_mask = _mode_mask, \
+ .volt_mask = _volt_mask, \
+ .volt_shft = _volt_shft, \
+ .mode_val = _mode_val, \
+ .off_mode_val = _off_mode_val, \
+ .val_tbl_sz = ARRAY_SIZE(_val_tbl), \
+ .val_tbl = _val_tbl, \
+ .mode_cntr = _mode_cntr, \
+ .volt_trans_time = _volt_trans_time, \
+ .turn_on_time = _turn_on_time, \
+ .bit_offset_from_cpcap_lowest_voltage = _bit_offset, \
+}
+
+static const struct cpcap_regulator_data tegra20_regulators[CPCAP_REGULATORS_COUNT] = {
+ /* BUCK */
+ [CPCAP_SW1] = CPCAP_REG(S1C1, ASSIGN2, SW1_SEL, 0x6f00, 0x007f,
+ 0, 0x6800, 0, sw1_val_tbl, 0, 0, 1500, 0x0c),
+ [CPCAP_SW2] = CPCAP_REG(S2C1, ASSIGN2, SW2_SEL, 0x6f00, 0x007f,
+ 0, 0x4804, 0, sw2_sw4_val_tbl, 0, 0, 1500, 0x18),
+ [CPCAP_SW3] = CPCAP_REG(S3C, ASSIGN2, SW3_SEL, 0x0578, 0x0003,
+ 0, 0x043c, 0, sw3_val_tbl, 0, 0, 0, 0),
+ [CPCAP_SW4] = CPCAP_REG(S4C1, ASSIGN2, SW4_SEL, 0x6f00, 0x007f,
+ 0, 0x4909, 0, sw2_sw4_val_tbl, 0, 0, 1500, 0x18),
+ [CPCAP_SW5] = CPCAP_REG(S5C, ASSIGN2, SW5_SEL, 0x0028, 0x0000,
+ 0, 0x0020, 0, sw5_val_tbl, 0, 0, 1500, 0),
+ [CPCAP_SW6] = CPCAP_REG(S6C, ASSIGN2, SW6_SEL, 0x0000, 0x0000,
+ 0, 0, 0, unknown_val_tbl, 0, 0, 0, 0),
+ /* LDO */
+ [CPCAP_VCAM] = CPCAP_REG(VCAMC, ASSIGN2, VCAM_SEL, 0x0087, 0x0030,
+ 4, 0x7, 0, vcam_val_tbl, 0, 420, 1000, 0),
+ [CPCAP_VCSI] = CPCAP_REG(VCSIC, ASSIGN3, VCSI_SEL, 0x0047, 0x0010,
+ 4, 0x7, 0, vcsi_val_tbl, 0, 350, 1000, 0),
+ [CPCAP_VDAC] = CPCAP_REG(VDACC, ASSIGN3, VDAC_SEL, 0x0087, 0x0030,
+ 4, 0x0, 0, vdac_val_tbl, 0, 420, 1000, 0),
+ [CPCAP_VDIG] = CPCAP_REG(VDIGC, ASSIGN2, VDIG_SEL, 0x0087, 0x0030,
+ 4, 0x0, 0, vdig_val_tbl, 0, 420, 1000, 0),
+ [CPCAP_VFUSE] = CPCAP_REG(VFUSEC, ASSIGN3, VFUSE_SEL, 0x00a0, 0x000f,
+ 0, 0x0, 0, vfuse_val_tbl, 0, 420, 1000, 0),
+ [CPCAP_VHVIO] = CPCAP_REG(VHVIOC, ASSIGN3, VHVIO_SEL, 0x0017, 0x0000,
+ 0, 0x2, 0, vhvio_val_tbl, 0, 0, 1000, 0),
+ [CPCAP_VSDIO] = CPCAP_REG(VSDIOC, ASSIGN2, VSDIO_SEL, 0x0087, 0x0038,
+ 3, 0x2, 0, vsdio_val_tbl, 0, 420, 1000, 0),
+ [CPCAP_VPLL] = CPCAP_REG(VPLLC, ASSIGN3, VPLL_SEL, 0x0047, 0x0018,
+ 3, 0x1, 0, vpll_val_tbl, 0, 420, 100, 0),
+ [CPCAP_VRF1] = CPCAP_REG(VRF1C, ASSIGN3, VRF1_SEL, 0x00ac, 0x0002,
+ 1, 0x0, 0, vrf1_val_tbl, 0, 10, 1000, 0),
+ [CPCAP_VRF2] = CPCAP_REG(VRF2C, ASSIGN3, VRF2_SEL, 0x0023, 0x0008,
+ 3, 0x0, 0, vrf2_val_tbl, 0, 10, 1000, 0),
+ [CPCAP_VRFREF] = CPCAP_REG(VRFREFC, ASSIGN3, VRFREF_SEL, 0x0023, 0x0008,
+ 3, 0x0, 0, vrfref_val_tbl, 0, 420, 100, 0),
+ [CPCAP_VWLAN1] = CPCAP_REG(VWLAN1C, ASSIGN3, VWLAN1_SEL, 0x0047, 0x0010,
+ 4, 0x0, 0, vwlan1_val_tbl, 0, 420, 1000, 0),
+ [CPCAP_VWLAN2] = CPCAP_REG(VWLAN2C, ASSIGN3, VWLAN2_SEL, 0x020c, 0x00c0,
+ 6, 0xd, 0, vwlan2_val_tbl, 0, 420, 1000, 0),
+ [CPCAP_VSIM] = CPCAP_REG(VSIMC, ASSIGN3, NONE, 0x0023, 0x0008,
+ 3, 0x0, 0, vsim_val_tbl, 0, 420, 1000, 0),
+ [CPCAP_VSIMCARD] = CPCAP_REG(VSIMC, ASSIGN3, NONE, 0x1e80, 0x0008,
+ 3, 0x1E00, 0, vsimcard_val_tbl, 0, 420, 1000, 0),
+ [CPCAP_VVIB] = CPCAP_REG(VVIBC, ASSIGN3, VVIB_SEL, 0x0001, 0x000c,
+ 2, 0x1, 0, vvib_val_tbl, 0, 500, 500, 0),
+ [CPCAP_VUSB] = CPCAP_REG(VUSBC, ASSIGN3, VUSB_SEL, 0x011c, 0x0040,
+ 6, 0xc, 0, vusb_val_tbl, 0, 0, 1000, 0),
+ [CPCAP_VAUDIO] = CPCAP_REG(VAUDIOC, ASSIGN4, VAUDIO_SEL, 0x0016, 0x0001,
+ 0, 0x5, 0, vaudio_val_tbl, 0, 0, 1000, 0),
+};
+
+static int cpcap_regulator_get_value(struct udevice *dev)
+{
+ const struct cpcap_regulator_data *regulator =
+ &tegra20_regulators[dev->driver_data];
+ int value, volt_shift = regulator->volt_shft;
+
+ value = pmic_reg_read(dev->parent, regulator->reg);
+ if (value < 0)
+ return value;
+
+ if (!(value & regulator->mode_mask))
+ return 0;
+
+ value &= regulator->volt_mask;
+ value -= regulator->bit_offset_from_cpcap_lowest_voltage;
+
+ return regulator->val_tbl[value >> volt_shift];
+}
+
+static int cpcap_regulator_set_value(struct udevice *dev, int uV)
+{
+ const struct cpcap_regulator_data *regulator =
+ &tegra20_regulators[dev->driver_data];
+ int value, ret, volt_shift = regulator->volt_shft;
+
+ if (dev->driver_data == CPCAP_VRF1) {
+ if (uV > 2500000)
+ value = 0;
+ else
+ value = regulator->volt_mask;
+ } else {
+ for (value = 0; value < regulator->val_tbl_sz; value++)
+ if (regulator->val_tbl[value] >= uV)
+ break;
+
+ if (value >= regulator->val_tbl_sz)
+ value = regulator->val_tbl_sz;
+
+ value <<= volt_shift;
+ value += regulator->bit_offset_from_cpcap_lowest_voltage;
+ }
+
+ ret = pmic_clrsetbits(dev->parent, regulator->reg, regulator->volt_mask,
+ value);
+ if (ret)
+ return ret;
+
+ if (regulator->volt_trans_time)
+ udelay(regulator->volt_trans_time);
+
+ return 0;
+}
+
+static int cpcap_regulator_get_enable(struct udevice *dev)
+{
+ const struct cpcap_regulator_data *regulator =
+ &tegra20_regulators[dev->driver_data];
+ int value;
+
+ value = pmic_reg_read(dev->parent, regulator->reg);
+ if (value < 0)
+ return value;
+
+ return (value & regulator->mode_mask) ? 1 : 0;
+}
+
+static int cpcap_regulator_set_enable(struct udevice *dev, bool enable)
+{
+ const struct cpcap_regulator_data *regulator =
+ &tegra20_regulators[dev->driver_data];
+ int ret;
+
+ if (enable) {
+ ret = pmic_clrsetbits(dev->parent, regulator->reg, regulator->mode_mask,
+ regulator->mode_val);
+ if (ret)
+ return ret;
+ }
+
+ if (regulator->mode_val & CPCAP_REG_OFF_MODE_SEC) {
+ ret = pmic_clrsetbits(dev->parent, regulator->assignment_reg,
+ regulator->assignment_mask,
+ enable ? 0 : regulator->assignment_mask);
+ if (ret)
+ return ret;
+ }
+
+ if (!enable) {
+ ret = pmic_clrsetbits(dev->parent, regulator->reg, regulator->mode_mask,
+ regulator->off_mode_val);
+ if (ret)
+ return ret;
+ }
+
+ if (regulator->turn_on_time)
+ udelay(regulator->turn_on_time);
+
+ return 0;
+}
+
+static int cpcap_regulator_probe(struct udevice *dev)
+{
+ struct dm_regulator_uclass_plat *uc_pdata = dev_get_uclass_plat(dev);
+ int id;
+
+ for (id = 0; id < CPCAP_REGULATORS_COUNT; id++)
+ if (cpcap_regulator_to_name[id])
+ if (!strcmp(dev->name, cpcap_regulator_to_name[id]))
+ break;
+
+ switch (id) {
+ case CPCAP_SW1 ... CPCAP_SW6:
+ uc_pdata->type = REGULATOR_TYPE_BUCK;
+ break;
+
+ case CPCAP_VCAM ... CPCAP_VAUDIO:
+ uc_pdata->type = REGULATOR_TYPE_LDO;
+ break;
+
+ default:
+ log_err("CPCAP: Invalid regulator ID\n");
+ return -ENODEV;
+ }
+
+ dev->driver_data = id;
+ return 0;
+}
+
+static const struct dm_regulator_ops cpcap_regulator_ops = {
+ .get_value = cpcap_regulator_get_value,
+ .set_value = cpcap_regulator_set_value,
+ .get_enable = cpcap_regulator_get_enable,
+ .set_enable = cpcap_regulator_set_enable,
+};
+
+U_BOOT_DRIVER(cpcap_sw) = {
+ .name = CPCAP_SW_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &cpcap_regulator_ops,
+ .probe = cpcap_regulator_probe,
+};
+
+U_BOOT_DRIVER(cpcap_ldo) = {
+ .name = CPCAP_LDO_DRIVER,
+ .id = UCLASS_REGULATOR,
+ .ops = &cpcap_regulator_ops,
+ .probe = cpcap_regulator_probe,
+};
diff --git a/drivers/power/regulator/scmi_regulator.c b/drivers/power/regulator/scmi_regulator.c
index 99f6506f162..79db1a6a8aa 100644
--- a/drivers/power/regulator/scmi_regulator.c
+++ b/drivers/power/regulator/scmi_regulator.c
@@ -175,12 +175,19 @@ U_BOOT_DRIVER(scmi_regulator) = {
static int scmi_regulator_bind(struct udevice *dev)
{
struct driver *drv;
+ ofnode regul_node;
ofnode node;
int ret;
+ regul_node = ofnode_find_subnode(dev_ofnode(dev), "regulators");
+ if (!ofnode_valid(regul_node)) {
+ dev_err(dev, "no regulators node\n");
+ return -ENXIO;
+ }
+
drv = DM_DRIVER_GET(scmi_regulator);
- ofnode_for_each_subnode(node, dev_ofnode(dev)) {
+ ofnode_for_each_subnode(node, regul_node) {
ret = device_bind(dev, drv, ofnode_get_name(node),
NULL, node, NULL);
if (ret)