[PATCH 2/7] pmdomain: st: add STM32MP power domain
From: Patrick Delaunay
Date: Wed Nov 04 2026 - 09:03:07 EST
Add a generic PM domain for the STM32MP D1 domain, using the PM
framework to manage attached devices.
Make D1 a subdomain of the PSCI cluster domain, so active devices keep
the Cortex-A35 cluster in Run1. Do not set GENPD_FLAG_ACTIVE_WAKEUP on
D1: system wakeups are handled by EXTI1 through the RET_PD PSCI domain.
Signed-off-by: Patrick Delaunay <patrick.delaunay@xxxxxxxxxxx>
---
drivers/pmdomain/st/Kconfig | 14 +++
drivers/pmdomain/st/Makefile | 1 +
drivers/pmdomain/st/stm32mp-pm-domain.c | 128 ++++++++++++++++++++++++
3 files changed, 143 insertions(+)
create mode 100644 drivers/pmdomain/st/stm32mp-pm-domain.c
diff --git a/drivers/pmdomain/st/Kconfig b/drivers/pmdomain/st/Kconfig
index a77a70211f61..da8a54d44158 100644
--- a/drivers/pmdomain/st/Kconfig
+++ b/drivers/pmdomain/st/Kconfig
@@ -1,4 +1,18 @@
# SPDX-License-Identifier: GPL-2.0-only
+
+config STM32MP_PM_DOMAIN
+ tristate "STM32MP PM domain"
+ depends on ARCH_STM32 || COMPILE_TEST
+ select PM_GENERIC_DOMAINS
+ default y
+ help
+ Enable support for the STM32MP Power Management (PM) domain driver.
+ This driver implements a generic PM domain for STM32MP SoCs, allowing
+ the kernel to manage power domains for various hardware blocks using
+ the generic power management framework. It is typically used to
+ control the power state of devices and peripherals on STM32MP
+ platforms. If unsure, say Y.
+
config UX500_PM_DOMAIN
bool "ST-Ericsson ux500 Power Domain"
depends on ARCH_U8500 || COMPILE_TEST
diff --git a/drivers/pmdomain/st/Makefile b/drivers/pmdomain/st/Makefile
index 6d8b617eb834..0e8131b7fe9d 100644
--- a/drivers/pmdomain/st/Makefile
+++ b/drivers/pmdomain/st/Makefile
@@ -1,2 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
+obj-$(CONFIG_STM32MP_PM_DOMAIN) += stm32mp-pm-domain.o
obj-$(CONFIG_UX500_PM_DOMAIN) += ste-ux500-pm-domain.o
diff --git a/drivers/pmdomain/st/stm32mp-pm-domain.c b/drivers/pmdomain/st/stm32mp-pm-domain.c
new file mode 100644
index 000000000000..1a6393840f56
--- /dev/null
+++ b/drivers/pmdomain/st/stm32mp-pm-domain.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Implements PM domain using the generic PM domain for STM32MP SoC.
+ *
+ * Copyright (C) STMicroelectronics 2026
+ * Author(s): Patrick Delaunay <patrick.delaunay@xxxxxxxxxxx>
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/pm_runtime.h>
+
+struct stm32mp_pm_subdomain_link {
+ struct generic_pm_domain *domain;
+ struct generic_pm_domain *subdomain;
+};
+
+static void stm32mp_pm_domain_genpd_remove(void *data)
+{
+ struct generic_pm_domain *genpd = data;
+
+ pm_genpd_remove(genpd);
+}
+
+static void stm32mp_pm_domain_release_subdomain(void *data)
+{
+ struct stm32mp_pm_subdomain_link *link = data;
+
+ pm_genpd_remove_subdomain(link->domain, link->subdomain);
+}
+
+static void stm32mp_pm_domain_release_genpd_provider(void *data)
+{
+ struct device_node *np = data;
+
+ of_genpd_del_provider(np);
+}
+
+static int stm32mp_pm_domain_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct generic_pm_domain *pd;
+ struct stm32mp_pm_subdomain_link *link;
+ int ret;
+
+ if (!np)
+ return -ENODEV;
+
+ /* The OF node must specify us as a power-domain provider. */
+ if (!of_property_present(np, "#power-domain-cells")) {
+ dev_warn(dev, "%pOF lacks #power-domain-cells\n", np);
+ return 0;
+ }
+
+ pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
+ if (!pd)
+ return -ENOMEM;
+
+ pd->name = dev_name(dev);
+
+ /*
+ * Domain based only on PM clk framework, no GENPD_FLAG_ACTIVE_WAKEUP
+ * support, the wake-up path is handled by interrupt driver with its
+ * power domain. GENPD_FLAG_NO_STAY_ON as domain no required sync state,
+ * domain allow low power modes even if devices are not probed
+ */
+ pd->flags = GENPD_FLAG_PM_CLK | GENPD_FLAG_IRQ_SAFE | GENPD_FLAG_NO_STAY_ON;
+
+ ret = pm_genpd_init(pd, NULL, false);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to init power domain\n");
+
+ ret = devm_add_action_or_reset(dev, stm32mp_pm_domain_genpd_remove, pd);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add action\n");
+
+ link = devm_kzalloc(dev, sizeof(*link), GFP_KERNEL);
+ if (!link)
+ return -ENOMEM;
+ link->domain = pd_to_genpd(dev->pm_domain);
+ link->subdomain = pd;
+
+ ret = pm_genpd_add_subdomain(link->domain, link->subdomain);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add subdomain\n");
+
+ ret = devm_add_action_or_reset(dev, stm32mp_pm_domain_release_subdomain, link);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add action\n");
+
+ ret = of_genpd_add_provider_simple(np, pd);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add power domain provider\n");
+
+ ret = devm_add_action_or_reset(dev, stm32mp_pm_domain_release_genpd_provider, np);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to add action\n");
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "failed to enable pm-runtime\n");
+
+ return 0;
+}
+
+static const struct of_device_id stm32mp_pm_domain_matches[] = {
+ { .compatible = "st,stm32mp-pm-domain", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, stm32mp_pm_domain_matches);
+
+static struct platform_driver stm32mp_pm_domain_driver = {
+ .driver = {
+ .name = "stm32mp_pm_domain",
+ .of_match_table = stm32mp_pm_domain_matches,
+ },
+ .probe = stm32mp_pm_domain_probe,
+};
+
+module_platform_driver(stm32mp_pm_domain_driver);
+
+MODULE_AUTHOR("Patrick Delaunay <patrick.delaunay@xxxxxxxxxxx>");
+MODULE_DESCRIPTION("ST STM32MP power domain driver");
+MODULE_LICENSE("GPL");
--
2.43.0