summaryrefslogtreecommitdiff
path: root/drivers/power/domain
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/power/domain')
-rw-r--r--drivers/power/domain/Kconfig24
-rw-r--r--drivers/power/domain/Makefile2
-rw-r--r--drivers/power/domain/apple-pmgr.c5
-rw-r--r--drivers/power/domain/bcm6328-power-domain.c2
-rw-r--r--drivers/power/domain/imx8-power-domain-legacy.c2
-rw-r--r--drivers/power/domain/imx8-power-domain.c2
-rw-r--r--drivers/power/domain/imx8m-power-domain.c6
-rw-r--r--drivers/power/domain/imx8mp-mediamix.c2
-rw-r--r--drivers/power/domain/meson-ee-pwrc.c15
-rw-r--r--drivers/power/domain/meson-gx-pwrc-vpu.c15
-rw-r--r--drivers/power/domain/meson-secure-pwrc.c2
-rw-r--r--drivers/power/domain/mtk-power-domain.c2
-rw-r--r--drivers/power/domain/power-domain-uclass.c7
-rw-r--r--drivers/power/domain/qcom-rpmhpd.c278
-rw-r--r--drivers/power/domain/renesas-r8a78000-power-domain.c425
-rw-r--r--drivers/power/domain/sandbox-power-domain.c2
-rw-r--r--drivers/power/domain/scmi-power-domain.c10
-rw-r--r--drivers/power/domain/tegra186-power-domain.c2
-rw-r--r--drivers/power/domain/zynqmp-power-domain.c2
19 files changed, 759 insertions, 46 deletions
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
index 2f63a8e54e5..bb9c52155d2 100644
--- a/drivers/power/domain/Kconfig
+++ b/drivers/power/domain/Kconfig
@@ -35,10 +35,10 @@ config BCM6328_POWER_DOMAIN
config IMX8_POWER_DOMAIN
bool "Enable i.MX8 power domain driver"
- depends on ARCH_IMX8
- help
- Enable support for manipulating NXP i.MX8 on-SoC power domains via IPC
- requests to the SCU.
+ depends on ARCH_IMX8
+ help
+ Enable support for manipulating NXP i.MX8 on-SoC power domains via IPC
+ requests to the SCU.
config IMX8M_POWER_DOMAIN
bool "Enable i.MX8M power domain driver"
@@ -90,6 +90,22 @@ config MESON_SECURE_POWER_DOMAIN
Enable support for manipulating Amlogic Meson Secure power domains.
Support for Amlogic A1 series.
+config QCOM_RPMH_POWER_DOMAIN
+ bool "Enable the QCOM RPMH Power domain driver"
+ depends on POWER_DOMAIN && ARCH_SNAPDRAGON
+ help
+ Generic RPMH power domain implementation for QCOM devices.
+ The RPMH power domain driver is responsible for managing power
+ domains on Qualcomm SoCs.
+
+config RENESAS_R8A78000_POWER_DOMAIN
+ bool "Enable the Renesas R-Car MDLC Power domain and reset driver"
+ depends on POWER_DOMAIN && ARCH_RENESAS
+ help
+ Enable support for Renesas R-Car R8A78000 X5H MDLC Power domain
+ and reset driver. The MDLC is responsible for managing both
+ power domains and resets on R-Car R8A78000 X5H SoC.
+
config SANDBOX_POWER_DOMAIN
bool "Enable the sandbox power domain test driver"
depends on POWER_DOMAIN && SANDBOX
diff --git a/drivers/power/domain/Makefile b/drivers/power/domain/Makefile
index b2c0bd8a61a..110153d5cf8 100644
--- a/drivers/power/domain/Makefile
+++ b/drivers/power/domain/Makefile
@@ -15,6 +15,7 @@ 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
obj-$(CONFIG_MESON_SECURE_POWER_DOMAIN) += meson-secure-pwrc.o
+obj-$(CONFIG_RENESAS_R8A78000_POWER_DOMAIN) += renesas-r8a78000-power-domain.o
obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain.o
obj-$(CONFIG_SANDBOX_POWER_DOMAIN) += sandbox-power-domain-test.o
obj-$(CONFIG_SCMI_POWER_DOMAIN) += scmi-power-domain.o
@@ -23,3 +24,4 @@ obj-$(CONFIG_TI_SCI_POWER_DOMAIN) += ti-sci-power-domain.o
obj-$(CONFIG_TI_POWER_DOMAIN) += ti-power-domain.o
obj-$(CONFIG_TI_OMAP_PRM_POWER_DOMAIN) += ti-omap-prm.o
obj-$(CONFIG_ZYNQMP_POWER_DOMAIN) += zynqmp-power-domain.o
+obj-$(CONFIG_QCOM_RPMH_POWER_DOMAIN) += qcom-rpmhpd.o
diff --git a/drivers/power/domain/apple-pmgr.c b/drivers/power/domain/apple-pmgr.c
index bf9940621ee..9873d3cd8db 100644
--- a/drivers/power/domain/apple-pmgr.c
+++ b/drivers/power/domain/apple-pmgr.c
@@ -67,7 +67,7 @@ static int apple_reset_deassert(struct reset_ctl *reset_ctl)
return 0;
}
-struct reset_ops apple_reset_ops = {
+static const struct reset_ops apple_reset_ops = {
.of_xlate = apple_reset_of_xlate,
.rst_assert = apple_reset_assert,
.rst_deassert = apple_reset_deassert,
@@ -110,6 +110,7 @@ static int apple_pmgr_of_xlate(struct power_domain *power_domain,
}
static const struct udevice_id apple_pmgr_ids[] = {
+ { .compatible = "apple,t8103-pmgr-pwrstate" },
{ .compatible = "apple,pmgr-pwrstate" },
{ /* sentinel */ }
};
@@ -138,7 +139,7 @@ static int apple_pmgr_probe(struct udevice *dev)
return 0;
}
-struct power_domain_ops apple_pmgr_ops = {
+static const struct power_domain_ops apple_pmgr_ops = {
.on = apple_pmgr_on,
.of_xlate = apple_pmgr_of_xlate,
};
diff --git a/drivers/power/domain/bcm6328-power-domain.c b/drivers/power/domain/bcm6328-power-domain.c
index 36b5a933748..5b449f4c29d 100644
--- a/drivers/power/domain/bcm6328-power-domain.c
+++ b/drivers/power/domain/bcm6328-power-domain.c
@@ -57,7 +57,7 @@ static const struct udevice_id bcm6328_power_domain_ids[] = {
{ /* sentinel */ }
};
-struct power_domain_ops bcm6328_power_domain_ops = {
+static const struct power_domain_ops bcm6328_power_domain_ops = {
.off = bcm6328_power_domain_off,
.on = bcm6328_power_domain_on,
.request = bcm6328_power_domain_request,
diff --git a/drivers/power/domain/imx8-power-domain-legacy.c b/drivers/power/domain/imx8-power-domain-legacy.c
index 713a51d7807..a646f667039 100644
--- a/drivers/power/domain/imx8-power-domain-legacy.c
+++ b/drivers/power/domain/imx8-power-domain-legacy.c
@@ -347,7 +347,7 @@ static const struct udevice_id imx8_power_domain_ids[] = {
{ }
};
-struct power_domain_ops imx8_power_domain_ops = {
+static const struct power_domain_ops imx8_power_domain_ops = {
.on = imx8_power_domain_on,
.off = imx8_power_domain_off,
.of_xlate = imx8_power_domain_of_xlate,
diff --git a/drivers/power/domain/imx8-power-domain.c b/drivers/power/domain/imx8-power-domain.c
index e8dcc057fee..5740cd686db 100644
--- a/drivers/power/domain/imx8-power-domain.c
+++ b/drivers/power/domain/imx8-power-domain.c
@@ -51,7 +51,7 @@ static const struct udevice_id imx8_power_domain_ids[] = {
{ }
};
-struct power_domain_ops imx8_power_domain_ops_v2 = {
+static const struct power_domain_ops imx8_power_domain_ops_v2 = {
.on = imx8_power_domain_on,
.off = imx8_power_domain_off,
};
diff --git a/drivers/power/domain/imx8m-power-domain.c b/drivers/power/domain/imx8m-power-domain.c
index 1c731b897cc..a3ef49aa67c 100644
--- a/drivers/power/domain/imx8m-power-domain.c
+++ b/drivers/power/domain/imx8m-power-domain.c
@@ -476,7 +476,7 @@ static int imx8m_power_domain_bind(struct udevice *dev)
const char *name;
int ret = 0;
- ofnode_for_each_subnode(subnode, dev_ofnode(dev)) {
+ dev_for_each_subnode(subnode, dev) {
/* Bind the subnode to this driver */
name = ofnode_get_name(subnode);
@@ -531,7 +531,7 @@ static int imx8m_power_domain_of_to_plat(struct udevice *dev)
struct imx_pgc_domain_data *domain_data =
(struct imx_pgc_domain_data *)dev_get_driver_data(dev);
- pdata->resource_id = ofnode_read_u32_default(dev_ofnode(dev), "reg", -1);
+ pdata->resource_id = dev_read_u32_default(dev, "reg", -1);
pdata->domain = &domain_data->domains[pdata->resource_id];
pdata->regs = domain_data->pgc_regs;
pdata->base = dev_read_addr_ptr(dev->parent);
@@ -558,7 +558,7 @@ static const struct udevice_id imx8m_power_domain_ids[] = {
{ }
};
-struct power_domain_ops imx8m_power_domain_ops = {
+static const struct power_domain_ops imx8m_power_domain_ops = {
.on = imx8m_power_domain_on,
.off = imx8m_power_domain_off,
.of_xlate = imx8m_power_domain_of_xlate,
diff --git a/drivers/power/domain/imx8mp-mediamix.c b/drivers/power/domain/imx8mp-mediamix.c
index 504c22f7d36..66ea5d8e60c 100644
--- a/drivers/power/domain/imx8mp-mediamix.c
+++ b/drivers/power/domain/imx8mp-mediamix.c
@@ -194,7 +194,7 @@ static const struct udevice_id imx8mp_mediamix_ids[] = {
{ }
};
-struct power_domain_ops imx8mp_mediamix_ops = {
+static const struct power_domain_ops imx8mp_mediamix_ops = {
.on = imx8mp_mediamix_on,
.off = imx8mp_mediamix_off,
.of_xlate = imx8mp_mediamix_of_xlate,
diff --git a/drivers/power/domain/meson-ee-pwrc.c b/drivers/power/domain/meson-ee-pwrc.c
index 4d9f3bba644..882238f2937 100644
--- a/drivers/power/domain/meson-ee-pwrc.c
+++ b/drivers/power/domain/meson-ee-pwrc.c
@@ -386,7 +386,7 @@ static int meson_ee_pwrc_of_xlate(struct power_domain *power_domain,
return 0;
}
-struct power_domain_ops meson_ee_pwrc_ops = {
+static const struct power_domain_ops meson_ee_pwrc_ops = {
.off = meson_ee_pwrc_off,
.on = meson_ee_pwrc_on,
.of_xlate = meson_ee_pwrc_of_xlate,
@@ -435,8 +435,7 @@ static const struct udevice_id meson_ee_pwrc_ids[] = {
static int meson_ee_pwrc_probe(struct udevice *dev)
{
struct meson_ee_pwrc_priv *priv = dev_get_priv(dev);
- u32 ao_phandle;
- ofnode ao_node;
+ struct ofnode_phandle_args args;
int ret;
priv->data = (void *)dev_get_driver_data(dev);
@@ -447,16 +446,12 @@ static int meson_ee_pwrc_probe(struct udevice *dev)
if (IS_ERR(priv->regmap_hhi))
return PTR_ERR(priv->regmap_hhi);
- ret = ofnode_read_u32(dev_ofnode(dev), "amlogic,ao-sysctrl",
- &ao_phandle);
+ ret = dev_read_phandle_with_args(dev, "amlogic,ao-sysctrl", NULL, 0, 0,
+ &args);
if (ret)
return ret;
- ao_node = ofnode_get_by_phandle(ao_phandle);
- if (!ofnode_valid(ao_node))
- return -EINVAL;
-
- priv->regmap_ao = syscon_node_to_regmap(ao_node);
+ priv->regmap_ao = syscon_node_to_regmap(args.node);
if (IS_ERR(priv->regmap_ao))
return PTR_ERR(priv->regmap_ao);
diff --git a/drivers/power/domain/meson-gx-pwrc-vpu.c b/drivers/power/domain/meson-gx-pwrc-vpu.c
index 1c56e8508c3..e08c0fac49a 100644
--- a/drivers/power/domain/meson-gx-pwrc-vpu.c
+++ b/drivers/power/domain/meson-gx-pwrc-vpu.c
@@ -262,7 +262,7 @@ static int meson_pwrc_vpu_of_xlate(struct power_domain *power_domain,
return 0;
}
-struct power_domain_ops meson_gx_pwrc_vpu_ops = {
+static const struct power_domain_ops meson_gx_pwrc_vpu_ops = {
.off = meson_pwrc_vpu_off,
.on = meson_pwrc_vpu_on,
.of_xlate = meson_pwrc_vpu_of_xlate,
@@ -283,24 +283,19 @@ static const struct udevice_id meson_gx_pwrc_vpu_ids[] = {
static int meson_gx_pwrc_vpu_probe(struct udevice *dev)
{
struct meson_gx_pwrc_vpu_priv *priv = dev_get_priv(dev);
- u32 hhi_phandle;
- ofnode hhi_node;
+ struct ofnode_phandle_args args;
int ret;
priv->regmap_ao = syscon_node_to_regmap(dev_ofnode(dev_get_parent(dev)));
if (IS_ERR(priv->regmap_ao))
return PTR_ERR(priv->regmap_ao);
- ret = ofnode_read_u32(dev_ofnode(dev), "amlogic,hhi-sysctrl",
- &hhi_phandle);
+ ret = dev_read_phandle_with_args(dev, "amlogic,hhi-sysctrl", NULL, 0, 0,
+ &args);
if (ret)
return ret;
- hhi_node = ofnode_get_by_phandle(hhi_phandle);
- if (!ofnode_valid(hhi_node))
- return -EINVAL;
-
- priv->regmap_hhi = syscon_node_to_regmap(hhi_node);
+ priv->regmap_hhi = syscon_node_to_regmap(args.node);
if (IS_ERR(priv->regmap_hhi))
return PTR_ERR(priv->regmap_hhi);
diff --git a/drivers/power/domain/meson-secure-pwrc.c b/drivers/power/domain/meson-secure-pwrc.c
index f70f8e02423..1b82b58f3e5 100644
--- a/drivers/power/domain/meson-secure-pwrc.c
+++ b/drivers/power/domain/meson-secure-pwrc.c
@@ -120,7 +120,7 @@ static struct meson_secure_pwrc_domain_desc a1_pwrc_domains[] = {
SEC_PD(RSA),
};
-struct power_domain_ops meson_secure_pwrc_ops = {
+static const struct power_domain_ops meson_secure_pwrc_ops = {
.on = meson_secure_pwrc_on,
.off = meson_secure_pwrc_off,
.of_xlate = meson_secure_pwrc_of_xlate,
diff --git a/drivers/power/domain/mtk-power-domain.c b/drivers/power/domain/mtk-power-domain.c
index 2d1ba1855a5..24dd540897d 100644
--- a/drivers/power/domain/mtk-power-domain.c
+++ b/drivers/power/domain/mtk-power-domain.c
@@ -392,7 +392,7 @@ static const struct udevice_id mtk_power_domain_ids[] = {
{ /* sentinel */ }
};
-struct power_domain_ops mtk_power_domain_ops = {
+static const struct power_domain_ops mtk_power_domain_ops = {
.off = scpsys_power_off,
.on = scpsys_power_on,
.request = scpsys_power_request,
diff --git a/drivers/power/domain/power-domain-uclass.c b/drivers/power/domain/power-domain-uclass.c
index cea68945cbd..b4cda5f6c16 100644
--- a/drivers/power/domain/power-domain-uclass.c
+++ b/drivers/power/domain/power-domain-uclass.c
@@ -10,6 +10,7 @@
#include <malloc.h>
#include <power-domain.h>
#include <power-domain-uclass.h>
+#include <dm/device_compat.h>
#include <dm/device-internal.h>
struct power_domain_priv {
@@ -187,6 +188,12 @@ static int dev_power_domain_ctrl(struct udevice *dev, bool on)
"#power-domain-cells", 0);
for (i = 0; i < count; i++) {
ret = power_domain_get_by_index(dev, &pd, i);
+
+ if (ret == -ENODEV) {
+ dev_warn(dev, "power-domain driver not found\n");
+ return 0;
+ }
+
if (ret)
return ret;
if (on)
diff --git a/drivers/power/domain/qcom-rpmhpd.c b/drivers/power/domain/qcom-rpmhpd.c
new file mode 100644
index 00000000000..f51bc9a4bbb
--- /dev/null
+++ b/drivers/power/domain/qcom-rpmhpd.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2018, The Linux Foundation. All rights reserved.
+// Copyright (c) 2025 Qualcomm Innovation Center, Inc. All rights reserved.
+
+#include <dm.h>
+#include <dm/lists.h>
+#include <power-domain.h>
+#include <asm/io.h>
+#include <linux/errno.h>
+
+#include <power-domain-uclass.h>
+#include <soc/qcom/cmd-db.h>
+#include <soc/qcom/rpmh.h>
+#include <dt-bindings/power/qcom-rpmpd.h>
+#include <dm/device_compat.h>
+
+#define RPMH_ARC_MAX_LEVELS 16
+
+/**
+ * struct rpmhpd - top level RPMh power domain resource data structure
+ * @dev: rpmh power domain controller device
+ * @pd: generic_pm_domain corresponding to the power domain
+ * @parent: generic_pm_domain corresponding to the parent's power domain
+ * @enable_corner: lowest non-zero corner
+ * @level: An array of level (vlvl) to corner (hlvl) mappings
+ * derived from cmd-db
+ * @level_count: Number of levels supported by the power domain. max
+ * being 16 (0 - 15)
+ * @enabled: true if the power domain is enabled
+ * @res_name: Resource name used for cmd-db lookup
+ * @addr: Resource address as looped up using resource name from
+ * @skip_retention_level: Indicate that retention level should not be used for the power domain
+ */
+struct rpmhpd {
+ struct udevice *dev;
+ struct power_domain pd;
+ struct power_domain *parent;
+ unsigned int enable_corner;
+ u32 level[RPMH_ARC_MAX_LEVELS];
+ size_t level_count;
+ bool enabled;
+ const char *res_name;
+ u32 addr;
+ bool skip_retention_level;
+};
+
+struct rpmhpd_desc {
+ struct rpmhpd **rpmhpds;
+ size_t num_pds;
+};
+
+/* RPMH powerdomains */
+static struct rpmhpd mmcx_ao;
+static struct rpmhpd mmcx = {
+ .res_name = "mmcx.lvl",
+};
+
+static struct rpmhpd mmcx_ao = {
+ .res_name = "mmcx.lvl",
+};
+
+/* SA8775P RPMH power domains */
+static struct rpmhpd *sa8775p_rpmhpds[] = {
+ [SA8775P_MMCX] = &mmcx,
+ [SA8775P_MMCX_AO] = &mmcx_ao,
+};
+
+static const struct rpmhpd_desc sa8775p_desc = {
+ .rpmhpds = sa8775p_rpmhpds,
+ .num_pds = ARRAY_SIZE(sa8775p_rpmhpds),
+};
+
+/* stub RPMH power domains mapped for unsupported platforms */
+static struct rpmhpd *stub_rpmhpds[] = {};
+
+static const struct rpmhpd_desc stub_desc = {
+ .rpmhpds = stub_rpmhpds,
+ .num_pds = ARRAY_SIZE(stub_rpmhpds),
+};
+
+static const struct udevice_id rpmhpd_match_table[] = {
+ { .compatible = "qcom,sa8775p-rpmhpd", .data = (ulong)&sa8775p_desc },
+ { .compatible = "qcom,qcs615-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,qcs8300-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,qdu1000-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sa8155p-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sa8540p-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sar2130p-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sc7180-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sc7280-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sc8180x-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sc8280xp-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sdm670-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sdm845-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sdx55-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sdx65-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sdx75-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sm4450-rpmhpd", .data = (ulong)&stub_desc},
+ { .compatible = "qcom,sm6350-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm7150-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8150-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8250-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8350-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8450-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8550-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8650-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,sm8750-rpmhpd", .data = (ulong)&stub_desc },
+ { .compatible = "qcom,x1e80100-rpmhpd", .data = (ulong)&stub_desc },
+ { }
+};
+
+static int rpmhpd_send_corner(struct rpmhpd *pd, int state,
+ unsigned int corner, bool sync)
+{
+ struct tcs_cmd cmd = {
+ .addr = pd->addr,
+ .data = corner,
+ };
+
+ return rpmh_write(pd->dev, state, &cmd, 1);
+}
+
+static int rpmhpd_power_on(struct power_domain *pd)
+{
+ int ret;
+ unsigned int corner;
+ struct rpmhpd **rpmhpds;
+ const struct rpmhpd_desc *desc;
+ struct rpmhpd *curr_rpmhpd;
+
+ desc = (const struct rpmhpd_desc *)dev_get_driver_data(pd->dev);
+ if (!desc)
+ return -EINVAL;
+
+ rpmhpds = desc->rpmhpds;
+ curr_rpmhpd = rpmhpds[pd->id];
+
+ /* Do nothing for undefined power domains */
+ if (!curr_rpmhpd) {
+ log_warning("Power domain id (%ld) not supported\n",
+ pd->id);
+ return 0;
+ }
+
+ corner = curr_rpmhpd->enable_corner;
+
+ ret = rpmhpd_send_corner(curr_rpmhpd, RPMH_ACTIVE_ONLY_STATE, corner,
+ false);
+ if (!ret)
+ curr_rpmhpd->enabled = true;
+
+ return ret;
+}
+
+static int rpmhpd_power_off(struct power_domain *pd)
+{
+ int ret;
+ unsigned int corner;
+ struct rpmhpd **rpmhpds;
+ const struct rpmhpd_desc *desc;
+ struct rpmhpd *curr_rpmhpd;
+
+ desc = (const struct rpmhpd_desc *)dev_get_driver_data(pd->dev);
+ if (!desc)
+ return -EINVAL;
+
+ rpmhpds = desc->rpmhpds;
+ curr_rpmhpd = rpmhpds[pd->id];
+
+ /* Do nothing for undefined power domains */
+ if (!curr_rpmhpd) {
+ log_warning("Power domain id (%ld) not supported\n",
+ pd->id);
+ return 0;
+ }
+
+ corner = 0;
+
+ ret = rpmhpd_send_corner(curr_rpmhpd, RPMH_ACTIVE_ONLY_STATE, corner,
+ false);
+ if (!ret)
+ curr_rpmhpd->enabled = false;
+
+ return ret;
+}
+
+static int rpmhpd_update_level_mapping(struct rpmhpd *rpmhpd)
+{
+ int i;
+ const u16 *buf;
+
+ buf = cmd_db_read_aux_data(rpmhpd->res_name, &rpmhpd->level_count);
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ /* 2 bytes used for each command DB aux data entry */
+ rpmhpd->level_count >>= 1;
+
+ if (rpmhpd->level_count > RPMH_ARC_MAX_LEVELS)
+ return -EINVAL;
+
+ for (i = 0; i < rpmhpd->level_count; i++) {
+ if (rpmhpd->skip_retention_level && buf[i] == RPMH_REGULATOR_LEVEL_RETENTION)
+ continue;
+
+ rpmhpd->level[i] = buf[i];
+
+ /* Remember the first corner with non-zero level */
+ if (!rpmhpd->level[rpmhpd->enable_corner] && rpmhpd->level[i])
+ rpmhpd->enable_corner = i;
+
+ /*
+ * The AUX data may be zero padded. These 0 valued entries at
+ * the end of the map must be ignored.
+ */
+ if (i > 0 && rpmhpd->level[i] == 0) {
+ rpmhpd->level_count = i;
+ break;
+ }
+ debug("%s: ARC hlvl=%2d --> vlvl=%4u\n", rpmhpd->res_name, i,
+ rpmhpd->level[i]);
+ }
+
+ return 0;
+}
+
+static int rpmhpd_probe(struct udevice *dev)
+{
+ int i, ret = 0;
+ struct rpmhpd **rpmhpds;
+ struct rpmhpd *priv;
+ const struct rpmhpd_desc *desc;
+
+ desc = (const struct rpmhpd_desc *)dev_get_driver_data(dev);
+ if (!desc)
+ return -EINVAL;
+
+ rpmhpds = desc->rpmhpds;
+
+ for (i = 0; i < desc->num_pds; i++) {
+ if (!rpmhpds[i])
+ continue;
+
+ priv = rpmhpds[i];
+ priv->dev = dev;
+ priv->addr = cmd_db_read_addr(priv->res_name);
+ if (!priv->addr) {
+ dev_err(dev, "Could not find RPMh address for resource %s\n",
+ priv->res_name);
+ return -ENODEV;
+ }
+
+ ret = cmd_db_read_slave_id(priv->res_name);
+ if (ret != CMD_DB_HW_ARC) {
+ dev_err(dev, "RPMh slave ID mismatch\n");
+ return -EINVAL;
+ }
+
+ ret = rpmhpd_update_level_mapping(priv);
+ if (ret)
+ return ret;
+ }
+
+ return ret;
+}
+
+static const struct power_domain_ops qcom_rpmhpd_power_ops = {
+ .on = rpmhpd_power_on,
+ .off = rpmhpd_power_off,
+};
+
+U_BOOT_DRIVER(qcom_rpmhpd_drv) = {
+ .name = "qcom_rpmhpd_drv",
+ .id = UCLASS_POWER_DOMAIN,
+ .of_match = rpmhpd_match_table,
+ .probe = rpmhpd_probe,
+ .ops = &qcom_rpmhpd_power_ops,
+};
diff --git a/drivers/power/domain/renesas-r8a78000-power-domain.c b/drivers/power/domain/renesas-r8a78000-power-domain.c
new file mode 100644
index 00000000000..57b3b56b2d9
--- /dev/null
+++ b/drivers/power/domain/renesas-r8a78000-power-domain.c
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas R-Car Gen5 MDLC driver
+ *
+ * Copyright (C) 2026 Marek Vasut <[email protected]>
+ */
+
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/bitfield.h>
+#include <power-domain-uclass.h>
+#include <reset-uclass.h>
+
+#include <scmi_agent.h>
+#include <scmi_agent-uclass.h>
+#include <scmi_protocols.h>
+
+#include <dt-bindings/power/r8a78000-power-scmi.h>
+#include <dt-bindings/reset/r8a78000-reset-scmi.h>
+
+#define PKC_PROT_LOCK 0xa5a5a500
+#define PKC_PROT_UNLOCK 0xa5a5a501
+
+#define MDLC_MSRESS_STANDBY 0
+#define MDLC_MSRESS_RESET 1
+#define MDLC_MSRESS_STOP 2
+#define MDLC_MSRESS_RUN 3
+
+#define MDLC_MSRES00 0x900
+#define MDLC_MSRESS00 0x960
+#define MDLC_PKCPROT1 0xcf4
+
+struct gen5_mdlc_priv {
+#if IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN)
+ struct udevice *pd;
+#endif
+#if IS_ENABLED(CONFIG_RESET_SCMI)
+ struct udevice *rst;
+#endif
+#if IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN) || IS_ENABLED(CONFIG_RESET_SCMI)
+ u32 basever;
+#endif
+#if !IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN) && !IS_ENABLED(CONFIG_RESET_SCMI)
+ void __iomem *base;
+#endif
+};
+
+static int gen5_pd_of_xlate(struct power_domain *power_domain,
+ struct ofnode_phandle_args *args)
+{
+ /* Perform direct remap until the bindings stabilize. */
+ power_domain->id = args->args[0];
+
+ return 0;
+}
+
+#if IS_ENABLED(CONFIG_SCMI_POWER_DOMAIN)
+static int gen5_pd_on(struct power_domain *power_domain)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(power_domain->dev->parent);
+ struct power_domain_ops *ops = (struct power_domain_ops *)priv->pd->driver->ops;
+ struct power_domain scmi = {
+ .dev = priv->pd,
+ .id = power_domain->id
+ };
+
+ return ops->on(&scmi);
+}
+
+static int gen5_pd_off(struct power_domain *power_domain)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(power_domain->dev->parent);
+ struct power_domain_ops *ops = (struct power_domain_ops *)priv->pd->driver->ops;
+ struct power_domain scmi = {
+ .dev = priv->pd,
+ .id = power_domain->id
+ };
+
+ return ops->off(&scmi);
+}
+
+static const struct power_domain_ops pd_gen5_ops = {
+ .on = gen5_pd_on,
+ .off = gen5_pd_off,
+ .of_xlate = gen5_pd_of_xlate,
+};
+
+static int gen5_pd_probe(struct udevice *dev)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(dev->parent);
+ struct udevice *agent;
+ int ret;
+
+ if (!priv->basever) {
+ ret = uclass_get_device(UCLASS_SCMI_AGENT, 0, &agent);
+ if (ret)
+ return ret;
+
+ if (!agent)
+ return -ENODEV;
+
+ priv->basever = scmi_impl_version(agent);
+ }
+
+ return uclass_get_device_by_driver(UCLASS_POWER_DOMAIN,
+ DM_DRIVER_GET(scmi_power_domain),
+ &priv->pd);
+}
+
+U_BOOT_DRIVER(pd_gen5) = {
+ .name = "pd_gen5",
+ .id = UCLASS_POWER_DOMAIN,
+ .ops = &pd_gen5_ops,
+ .probe = gen5_pd_probe,
+ .flags = DM_FLAG_OS_PREPARE | DM_FLAG_VITAL,
+};
+#else
+static const struct power_domain_ops pd_gen5_ops = {
+ .of_xlate = gen5_pd_of_xlate,
+};
+
+U_BOOT_DRIVER(pd_gen5) = {
+ .name = "pd_gen5",
+ .id = UCLASS_POWER_DOMAIN,
+ .ops = &pd_gen5_ops,
+ .flags = DM_FLAG_OS_PREPARE | DM_FLAG_VITAL,
+};
+#endif
+
+#if IS_ENABLED(CONFIG_RESET_SCMI)
+static int gen5_reset_assert(struct reset_ctl *reset_ctl)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ struct reset_ops *ops = (struct reset_ops *)priv->rst->driver->ops;
+ struct reset_ctl scmi = {
+ .dev = priv->rst,
+ .id = reset_ctl->id
+ };
+
+ return ops->rst_assert(&scmi);
+}
+
+static int gen5_reset_deassert(struct reset_ctl *reset_ctl)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ struct reset_ops *ops = (struct reset_ops *)priv->rst->driver->ops;
+ struct reset_ctl scmi = {
+ .dev = priv->rst,
+ .id = reset_ctl->id
+ };
+
+ return ops->rst_deassert(&scmi);
+}
+
+struct rst_map_in {
+ u16 dt_id; /* DT binding clock ID */
+ u16 fw_id; /* SCMI firmware clock ID */
+};
+
+#define GEN5_SCMI_SDK_4_28 0x010a0000
+#define GEN5_SCMI_SDK_4_29 0x010b0000
+#define GEN5_SCMI_SDK_4_30 0x010c0000
+#define GEN5_SCMI_SDK_4_31 0x010d0000
+#define GEN5_SCMI_SDK_4_32 0x010e0000
+
+static const struct rst_map_in gen5_rst_map_dt_sdk_4_28[] = {
+ { SCP_RESET_DOMAIN_ID_UFS0, 202 },
+ { SCP_RESET_DOMAIN_ID_UFS1, 203 },
+ { SCP_RESET_DOMAIN_ID_XPCS0, 316 },
+ { SCP_RESET_DOMAIN_ID_XPCS1, 317 },
+ { SCP_RESET_DOMAIN_ID_XPCS2, 318 },
+ { SCP_RESET_DOMAIN_ID_XPCS3, 319 },
+ { SCP_RESET_DOMAIN_ID_XPCS4, 320 },
+ { SCP_RESET_DOMAIN_ID_XPCS5, 321 },
+ { SCP_RESET_DOMAIN_ID_XPCS6, 322 },
+ { SCP_RESET_DOMAIN_ID_XPCS7, 323 },
+ { SCP_RESET_DOMAIN_ID_MPPHY01, 344 },
+ { SCP_RESET_DOMAIN_ID_MPPHY11, 345 },
+ { SCP_RESET_DOMAIN_ID_MPPHY21, 346 },
+ { SCP_RESET_DOMAIN_ID_MPPHY31, 347 },
+ { SCP_RESET_DOMAIN_ID_MPPHY02, 348 },
+};
+
+static const struct rst_map_in gen5_rst_map_dt_sdk_4_31[] = {
+ { SCP_RESET_DOMAIN_ID_UFS0, 198 },
+ { SCP_RESET_DOMAIN_ID_UFS1, 199 },
+ { SCP_RESET_DOMAIN_ID_XPCS0, 312 },
+ { SCP_RESET_DOMAIN_ID_XPCS1, 313 },
+ { SCP_RESET_DOMAIN_ID_XPCS2, 314 },
+ { SCP_RESET_DOMAIN_ID_XPCS3, 315 },
+ { SCP_RESET_DOMAIN_ID_XPCS4, 316 },
+ { SCP_RESET_DOMAIN_ID_XPCS5, 317 },
+ { SCP_RESET_DOMAIN_ID_XPCS6, 318 },
+ { SCP_RESET_DOMAIN_ID_XPCS7, 319 },
+ { SCP_RESET_DOMAIN_ID_MPPHY01, 340 },
+ { SCP_RESET_DOMAIN_ID_MPPHY11, 341 },
+ { SCP_RESET_DOMAIN_ID_MPPHY21, 342 },
+ { SCP_RESET_DOMAIN_ID_MPPHY31, 343 },
+ { SCP_RESET_DOMAIN_ID_MPPHY02, 344 },
+};
+
+static int gen5_reset_of_xlate(struct reset_ctl *reset_ctl,
+ struct ofnode_phandle_args *args)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ const struct rst_map_in *map;
+ unsigned int map_size;
+ int i;
+
+ if (args->args_count != 1) {
+ debug("Invalid args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ if (priv->basever == GEN5_SCMI_SDK_4_28) {
+ map = gen5_rst_map_dt_sdk_4_28;
+ map_size = ARRAY_SIZE(gen5_rst_map_dt_sdk_4_28);
+ } else if (priv->basever == GEN5_SCMI_SDK_4_31 ||
+ priv->basever == GEN5_SCMI_SDK_4_32) {
+ map = gen5_rst_map_dt_sdk_4_31;
+ map_size = ARRAY_SIZE(gen5_rst_map_dt_sdk_4_31);
+ } else {
+ printf("Unsupported SCMI base protocol version %x\n", priv->basever);
+ return -EINVAL;
+ }
+
+ reset_ctl->id = -1;
+ for (i = 0; i < map_size; i++) {
+ if (map[i].dt_id != args->args[0])
+ continue;
+ reset_ctl->id = map[i].fw_id;
+ break;
+ }
+
+ return 0;
+}
+
+static const struct reset_ops rst_gen5_ops = {
+ .rst_assert = gen5_reset_assert,
+ .rst_deassert = gen5_reset_deassert,
+ .of_xlate = gen5_reset_of_xlate,
+};
+
+static int gen5_rst_probe(struct udevice *dev)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(dev->parent);
+ struct udevice *agent;
+ int ret = 0;
+
+ if (!priv->basever) {
+ ret = uclass_get_device(UCLASS_SCMI_AGENT, 0, &agent);
+ if (ret)
+ return ret;
+
+ if (!agent)
+ return -ENODEV;
+
+ priv->basever = scmi_impl_version(agent);
+ }
+
+ return uclass_get_device_by_driver(UCLASS_RESET,
+ DM_DRIVER_GET(scmi_reset_domain),
+ &priv->rst);
+}
+#else
+static int mdlc_wait_for_reset(struct reset_ctl *reset_ctl)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ const u32 offset = (reset_ctl->id / 16) * 4;
+ void __iomem *res = priv->base + MDLC_MSRES00 + offset;
+ void __iomem *stat = priv->base + MDLC_MSRESS00 + offset;
+ u32 val;
+ int ret;
+
+ /* Wait 100ms for reset controller to synchronize. */
+ ret = readl_poll_timeout(res, val, val == readl(stat), 100000);
+ if (ret < 0)
+ dev_err(reset_ctl->dev, "Reset controller out of sync!\n");
+
+ return ret;
+}
+
+static void mdlc_rmw_msres(struct reset_ctl *reset_ctl, const int val)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ const u32 offset = (reset_ctl->id / 16) * 4;
+ const u32 mask = 3 << ((reset_ctl->id % 16) * 2);
+ void __iomem *prot = priv->base + MDLC_PKCPROT1;
+ void __iomem *res = priv->base + MDLC_MSRES00 + offset;
+ u32 reg;
+
+ reg = readl(res);
+ reg &= ~mask;
+ reg |= field_prep(mask, val);
+
+ writel(PKC_PROT_UNLOCK, prot);
+ writel(reg, res);
+ writel(PKC_PROT_LOCK, prot);
+}
+
+static int gen5_reset_toggle(struct reset_ctl *reset_ctl, const u8 step1,
+ const u8 step2, const u8 step3, const u8 step4)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(reset_ctl->dev->parent);
+ const u32 offset = (reset_ctl->id / 16) * 4;
+ const u32 mask = 3 << ((reset_ctl->id % 16) * 2);
+ void __iomem *stat = priv->base + MDLC_MSRESS00 + offset;
+ u32 status;
+ int ret;
+
+ ret = mdlc_wait_for_reset(reset_ctl);
+ if (ret)
+ return ret;
+
+ status = field_get(mask, readl(stat));
+ if (status == step1) {
+ mdlc_rmw_msres(reset_ctl, step2);
+ ret = mdlc_wait_for_reset(reset_ctl);
+ if (ret)
+ return ret;
+ status = field_get(mask, readl(stat));
+ }
+
+ if (status == step2 || status == step3) {
+ mdlc_rmw_msres(reset_ctl, step4);
+ ret = mdlc_wait_for_reset(reset_ctl);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int gen5_reset_assert(struct reset_ctl *reset_ctl)
+{
+ return gen5_reset_toggle(reset_ctl,
+ MDLC_MSRESS_STOP, MDLC_MSRESS_STANDBY,
+ MDLC_MSRESS_RUN, MDLC_MSRESS_RESET);
+}
+
+static int gen5_reset_deassert(struct reset_ctl *reset_ctl)
+{
+ return gen5_reset_toggle(reset_ctl,
+ MDLC_MSRESS_STANDBY, MDLC_MSRESS_RESET,
+ MDLC_MSRESS_STOP, MDLC_MSRESS_RUN);
+}
+
+static int gen5_reset_of_xlate(struct reset_ctl *reset_ctl,
+ struct ofnode_phandle_args *args)
+{
+ /* Perform direct remap until the bindings stabilize. */
+ reset_ctl->id = args->args[0];
+
+ return 0;
+}
+
+static const struct reset_ops rst_gen5_ops = {
+ .rst_assert = gen5_reset_assert,
+ .rst_deassert = gen5_reset_deassert,
+ .of_xlate = gen5_reset_of_xlate,
+};
+
+static int gen5_rst_probe(struct udevice *dev)
+{
+ struct gen5_mdlc_priv *priv = dev_get_priv(dev->parent);
+
+ priv->base = dev_read_addr_ptr(dev);
+ if (!priv->base)
+ return -EINVAL;
+
+ return 0;
+}
+#endif
+
+U_BOOT_DRIVER(rst_gen5) = {
+ .name = "rst_gen5",
+ .id = UCLASS_RESET,
+ .ops = &rst_gen5_ops,
+ .probe = gen5_rst_probe,
+ .flags = DM_FLAG_OS_PREPARE | DM_FLAG_VITAL,
+};
+
+int gen5_mdlc_bind(struct udevice *parent)
+{
+ struct udevice *pdev, *rdev;
+ struct driver *pdrv, *rdrv;
+ int ret;
+
+ pdrv = lists_driver_lookup_name("pd_gen5");
+ if (!pdrv)
+ return -ENOENT;
+
+ rdrv = lists_driver_lookup_name("rst_gen5");
+ if (!rdrv)
+ return -ENOENT;
+
+ ret = device_bind_with_driver_data(parent, pdrv, "pd_gen5", 0,
+ dev_ofnode(parent), &pdev);
+ if (ret)
+ return ret;
+
+ ret = device_bind_with_driver_data(parent, rdrv, "rst_gen5", (ulong)pdev,
+ dev_ofnode(parent), &rdev);
+ if (ret)
+ device_unbind(pdev);
+
+ return ret;
+}
+
+static const struct udevice_id r8a78000_mdlc_ids[] = {
+ { .compatible = "renesas,r8a78000-mdlc", },
+ { }
+};
+
+U_BOOT_DRIVER(mdlc_gen5) = {
+ .name = "mdlc_gen5",
+ .id = UCLASS_NOP,
+ .of_match = r8a78000_mdlc_ids,
+ .bind = gen5_mdlc_bind,
+ .priv_auto = sizeof(struct gen5_mdlc_priv),
+};
diff --git a/drivers/power/domain/sandbox-power-domain.c b/drivers/power/domain/sandbox-power-domain.c
index a8031657638..a30826a3b4d 100644
--- a/drivers/power/domain/sandbox-power-domain.c
+++ b/drivers/power/domain/sandbox-power-domain.c
@@ -78,7 +78,7 @@ static const struct udevice_id sandbox_power_domain_ids[] = {
{ }
};
-struct power_domain_ops sandbox_power_domain_ops = {
+static const struct power_domain_ops sandbox_power_domain_ops = {
.request = sandbox_power_domain_request,
.rfree = sandbox_power_domain_free,
.on = sandbox_power_domain_on,
diff --git a/drivers/power/domain/scmi-power-domain.c b/drivers/power/domain/scmi-power-domain.c
index e8c0ba8878e..a369fe52f2f 100644
--- a/drivers/power/domain/scmi-power-domain.c
+++ b/drivers/power/domain/scmi-power-domain.c
@@ -165,21 +165,15 @@ static int scmi_power_domain_probe(struct udevice *dev)
for (i = 0; i < priv->num_pwdoms; i++) {
ret = scmi_pwd_attrs(dev, i, &priv->prop[i].attributes,
&priv->prop[i].name);
- if (ret) {
+ if (ret)
dev_err(dev, "failed to get attributes pwd:%d (%d)\n",
i, ret);
- for (i--; i >= 0; i--)
- free(priv->prop[i].name);
- free(priv->prop);
-
- return ret;
- }
}
return 0;
}
-struct power_domain_ops scmi_power_domain_ops = {
+static const struct power_domain_ops scmi_power_domain_ops = {
.on = scmi_power_domain_on,
.off = scmi_power_domain_off,
};
diff --git a/drivers/power/domain/tegra186-power-domain.c b/drivers/power/domain/tegra186-power-domain.c
index 334c460c805..3865cd4cf47 100644
--- a/drivers/power/domain/tegra186-power-domain.c
+++ b/drivers/power/domain/tegra186-power-domain.c
@@ -55,7 +55,7 @@ static int tegra186_power_domain_off(struct power_domain *power_domain)
return tegra186_power_domain_common(power_domain, false);
}
-struct power_domain_ops tegra186_power_domain_ops = {
+static const struct power_domain_ops tegra186_power_domain_ops = {
.on = tegra186_power_domain_on,
.off = tegra186_power_domain_off,
};
diff --git a/drivers/power/domain/zynqmp-power-domain.c b/drivers/power/domain/zynqmp-power-domain.c
index a54de5c1439..0acfc54e787 100644
--- a/drivers/power/domain/zynqmp-power-domain.c
+++ b/drivers/power/domain/zynqmp-power-domain.c
@@ -57,7 +57,7 @@ static int zynqmp_power_domain_off(struct power_domain *power_domain)
return 0;
}
-struct power_domain_ops zynqmp_power_domain_ops = {
+static const struct power_domain_ops zynqmp_power_domain_ops = {
.request = zynqmp_power_domain_request,
.rfree = zynqmp_power_domain_free,
.on = zynqmp_power_domain_on,