Added support for Qualcomm RPMH power domain driver, responsible
for managing power domains on Qualcomm SoCs. This is a port of
the Linux RPMHPD driver [1] and sa8775p related changes. The
power domain driver currently has support to power on and off
MMCX power domain of sa8775p; support for other soc entries power
domains are stubbed, in future, the required soc support can be
added.
[1]:
https://web.git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/pmdomain/qcom/rpmhpd.c?id=3d25d46a255a83f94d7d4d4216f38aafc8e116b
Signed-off-by: Balaji Selvanathan <[email protected]>
Signed-off-by: Aswin Murugan <[email protected]>
---
v4:
- Added all SoC entries from the Linux driver and stubbed it
- Removed "qcom_rpmhpd" driver
- Removed unused members in struct rpmhpd
v3:
- No changes to this patch in v3
v2:
- Added ARCH_SNAPDRAGON dependency to QCOM_POWER_DOMAIN Kconfig
- In qcom-rpmhpd driver, the un-supported power domains are handled with warning
---
drivers/power/domain/Kconfig | 8 +
drivers/power/domain/Makefile | 1 +
drivers/power/domain/qcom-rpmhpd.c | 278 +++++++++++++++++++++++++++++
3 files changed, 287 insertions(+)
create mode 100644 drivers/power/domain/qcom-rpmhpd.c
diff --git a/drivers/power/domain/Kconfig b/drivers/power/domain/Kconfig
index 5f5218bd8b5..1456df96cd1 100644
--- a/drivers/power/domain/Kconfig
+++ b/drivers/power/domain/Kconfig
@@ -82,6 +82,14 @@ config MESON_SECURE_POWER_DOMAIN
Enable support for manipulating Amlogic Meson Secure power domains.
Support for Amlogic A1 series.
+config QCOM_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 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 4d20c97d26c..950f83972dc 100644
--- a/drivers/power/domain/Makefile
+++ b/drivers/power/domain/Makefile
@@ -21,3 +21,4 @@ obj-$(CONFIG_TEGRA186_POWER_DOMAIN) += tegra186-power-domain.o
obj-$(CONFIG_TI_SCI_POWER_DOMAIN) += ti-sci-power-domain.o
obj-$(CONFIG_TI_POWER_DOMAIN) += ti-power-domain.o
obj-$(CONFIG_ZYNQMP_POWER_DOMAIN) += zynqmp-power-domain.o
+obj-$(CONFIG_QCOM_POWER_DOMAIN) += qcom-rpmhpd.o
diff --git a/drivers/power/domain/qcom-rpmhpd.c
b/drivers/power/domain/qcom-rpmhpd.c
new file mode 100644
index 00000000000..906af2aa0ba
--- /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 = &stub_desc },
+ { .compatible = "qcom,qcs8300-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,qdu1000-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sa8155p-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sa8540p-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sar2130p-rpmhpd", .data = &stub_desc},
+ { .compatible = "qcom,sc7180-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sc7280-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sc8180x-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sc8280xp-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sdm670-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sdm845-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sdx55-rpmhpd", .data = &stub_desc},
+ { .compatible = "qcom,sdx65-rpmhpd", .data = &stub_desc},
+ { .compatible = "qcom,sdx75-rpmhpd", .data = &stub_desc},
+ { .compatible = "qcom,sm4450-rpmhpd", .data = &stub_desc},
+ { .compatible = "qcom,sm6350-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sm7150-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sm8150-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sm8250-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sm8350-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sm8450-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sm8550-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sm8650-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,sm8750-rpmhpd", .data = &stub_desc },
+ { .compatible = "qcom,x1e80100-rpmhpd", .data = &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->parent, state, &cmd, 1);