diff options
Diffstat (limited to 'drivers/power')
-rw-r--r-- | drivers/power/domain/Kconfig | 7 | ||||
-rw-r--r-- | drivers/power/domain/Makefile | 3 | ||||
-rw-r--r-- | drivers/power/domain/imx8m-power-domain.c | 21 | ||||
-rw-r--r-- | drivers/power/domain/imx8mp-hsiomix.c | 4 | ||||
-rw-r--r-- | drivers/power/domain/imx8mp-mediamix.c | 212 | ||||
-rw-r--r-- | drivers/power/domain/power-domain-uclass.c | 90 | ||||
-rw-r--r-- | drivers/power/domain/sandbox-power-domain-test.c | 15 | ||||
-rw-r--r-- | drivers/power/domain/sandbox-power-domain.c | 4 | ||||
-rw-r--r-- | drivers/power/pmic/Kconfig | 10 | ||||
-rw-r--r-- | drivers/power/pmic/Makefile | 31 | ||||
-rw-r--r-- | drivers/power/pmic/axp.c | 1 | ||||
-rw-r--r-- | drivers/power/pmic/cpcap.c | 125 | ||||
-rw-r--r-- | drivers/power/regulator/Kconfig | 16 | ||||
-rw-r--r-- | drivers/power/regulator/Makefile | 48 | ||||
-rw-r--r-- | drivers/power/regulator/axp_drivevbus.c | 57 | ||||
-rw-r--r-- | drivers/power/regulator/cpcap_regulator.c | 275 | ||||
-rw-r--r-- | drivers/power/regulator/scmi_regulator.c | 9 |
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) |