On Thu, Jun 18, 2026 at 7:01 AM Linus Walleij <[email protected]> wrote:
>
> Move the DB8500 EPOD state handling into the Ux500 power-domain driver.
>
> Keep the old regulator driver mutually exclusive with the pmdomain driver.
>
> Assisted-by: Codex:gpt-5-5
> Signed-off-by: Linus Walleij <[email protected]>
> ---
> arch/arm/mach-ux500/Kconfig | 2 +-
> drivers/pmdomain/st/ste-ux500-pm-domain.c | 380
> ++++++++++++++++++++++--------
> drivers/regulator/Kconfig | 1 +
> 3 files changed, 282 insertions(+), 101 deletions(-)
>
> diff --git a/arch/arm/mach-ux500/Kconfig b/arch/arm/mach-ux500/Kconfig
> index c18def269137..56636c993f49 100644
> --- a/arch/arm/mach-ux500/Kconfig
> +++ b/arch/arm/mach-ux500/Kconfig
> @@ -26,7 +26,7 @@ menuconfig ARCH_U8500
> select PL310_ERRATA_753970 if CACHE_L2X0
> select PM_GENERIC_DOMAINS if PM
> select REGULATOR
> - select REGULATOR_DB8500_PRCMU
> + select UX500_PM_DOMAIN
> select REGULATOR_FIXED_VOLTAGE
> select SOC_BUS
> select RESET_CONTROLLER
> diff --git a/drivers/pmdomain/st/ste-ux500-pm-domain.c
> b/drivers/pmdomain/st/ste-ux500-pm-domain.c
> index 723001004690..1cd5b4985db0 100644
> --- a/drivers/pmdomain/st/ste-ux500-pm-domain.c
> +++ b/drivers/pmdomain/st/ste-ux500-pm-domain.c
> @@ -6,172 +6,315 @@
> *
> * Implements PM domains using the generic PM domain for ux500.
> */
> +#include <linux/cleanup.h>
> #include <linux/device.h>
> +#include <linux/err.h>
> #include <linux/kernel.h>
> +#include <linux/mfd/dbx500-prcmu.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> #include <linux/platform_device.h>
> +#include <linux/pm_domain.h>
> #include <linux/printk.h>
> #include <linux/slab.h>
> -#include <linux/err.h>
> -#include <linux/of.h>
> -#include <linux/pm_domain.h>
>
> #include <dt-bindings/arm/ux500_pm_domains.h>
>
> -static int pd_power_off(struct generic_pm_domain *domain)
> +#define UX500_EPOD_NONE NUM_EPOD_ID
> +
> +/**
> + * struct dbx500_powerdomain_info - dbx500 power domain information
> + * @genpd: generic power domain
> + * @epod_id: id for EPOD (power domain)
> + * @is_ramret: RAM retention switch for EPOD (power domain)
> + * @exclude_from_power_state: exclude domain from power state count
> + */
> +struct dbx500_powerdomain_info {
> + struct generic_pm_domain genpd;
> + u16 epod_id;
> + bool is_ramret;
> + bool exclude_from_power_state;
> +};
> +
> +static DEFINE_MUTEX(ux500_pd_lock);
> +static int power_state_active_cnt;
> +static bool epod_on[NUM_EPOD_ID];
> +static bool epod_ramret[NUM_EPOD_ID];
> +
> +static void power_state_active_enable(void)
> +{
> + power_state_active_cnt++;
> +}
> +
> +static int power_state_active_disable(void)
> {
> - /*
> - * Handle the gating of the PM domain regulator here.
> - *
> - * Drivers/subsystems handling devices in the PM domain needs to
> perform
> - * register context save/restore from their respective runtime PM
> - * callbacks, to be able to enable PM domain gating/ungating.
> - */
> + if (power_state_active_cnt <= 0) {
> + pr_err("power state: unbalanced enable/disable calls\n");
> + return -EINVAL;
> + }
> +
> + power_state_active_cnt--;
The whole power_state_active_cnt thing seems to be a leftover from
debug exercise, no?
At least, I can't see that it actually adds much - or maybe following
patches makes use of it somehow?
> return 0;
> }
>
> -static int pd_power_on(struct generic_pm_domain *domain)
> +static int enable_epod(u16 epod_id, bool ramret)
> {
> - /*
> - * Handle the ungating of the PM domain regulator here.
> - *
> - * Drivers/subsystems handling devices in the PM domain needs to
> perform
> - * register context save/restore from their respective runtime PM
> - * callbacks, to be able to enable PM domain gating/ungating.
> - */
> + int ret;
> +
> + if (ramret) {
> + if (!epod_on[epod_id]) {
> + ret = prcmu_set_epod(epod_id, EPOD_STATE_RAMRET);
> + if (ret < 0)
> + return ret;
> + }
> + epod_ramret[epod_id] = true;
> + } else {
> + ret = prcmu_set_epod(epod_id, EPOD_STATE_ON);
> + if (ret < 0)
> + return ret;
> + epod_on[epod_id] = true;
> + }
> +
> + return 0;
> +}
> +
> +static int disable_epod(u16 epod_id, bool ramret)
> +{
> + int ret;
> +
> + if (ramret) {
> + if (!epod_on[epod_id]) {
> + ret = prcmu_set_epod(epod_id, EPOD_STATE_OFF);
> + if (ret < 0)
> + return ret;
> + }
> + epod_ramret[epod_id] = false;
> + } else {
> + if (epod_ramret[epod_id]) {
> + ret = prcmu_set_epod(epod_id, EPOD_STATE_RAMRET);
> + if (ret < 0)
> + return ret;
> + } else {
> + ret = prcmu_set_epod(epod_id, EPOD_STATE_OFF);
> + if (ret < 0)
> + return ret;
> + }
> + epod_on[epod_id] = false;
> + }
> +
> return 0;
> }
>
> +static int pd_power_off(struct generic_pm_domain *domain)
> +{
> + struct dbx500_powerdomain_info *info =
> + container_of(domain, struct dbx500_powerdomain_info, genpd);
> + int ret = 0;
> +
> + guard(mutex)(&ux500_pd_lock);
> + if (info->epod_id < NUM_EPOD_ID)
> + ret = disable_epod(info->epod_id, info->is_ramret);
> + else if (!info->exclude_from_power_state)
> + ret = power_state_active_disable();
> +
> + return ret;
> +}
> +
> +static int pd_power_on(struct generic_pm_domain *domain)
> +{
> + struct dbx500_powerdomain_info *info =
> + container_of(domain, struct dbx500_powerdomain_info, genpd);
> + int ret = 0;
> +
> + guard(mutex)(&ux500_pd_lock);
> + if (info->epod_id < NUM_EPOD_ID)
> + ret = enable_epod(info->epod_id, info->is_ramret);
> + else if (!info->exclude_from_power_state)
> + power_state_active_enable();
> +
> + return ret;
> +}
[...]
>
> +static int ux500_pm_domain_add_subdomain(struct generic_pm_domain *domain)
> +{
> + return pm_genpd_add_subdomain(&ux500_pm_domain_vape.genpd, domain);
> +}
> +
> +static int ux500_pm_domains_add_subdomains(void)
> +{
> + int ret;
> +
> + ret = ux500_pm_domain_add_subdomain(&ux500_pm_domain_sva_mmdsp.genpd);
> + if (ret)
> + return ret;
> +
> + ret = ux500_pm_domain_add_subdomain(&ux500_pm_domain_sva_pipe.genpd);
> + if (ret)
> + return ret;
> +
> + ret = ux500_pm_domain_add_subdomain(&ux500_pm_domain_sia_mmdsp.genpd);
> + if (ret)
> + return ret;
> +
> + ret = ux500_pm_domain_add_subdomain(&ux500_pm_domain_sia_pipe.genpd);
> + if (ret)
> + return ret;
> +
> + ret = ux500_pm_domain_add_subdomain(&ux500_pm_domain_sga.genpd);
> + if (ret)
> + return ret;
> +
> + return
> ux500_pm_domain_add_subdomain(&ux500_pm_domain_b2r2_mcde.genpd);
> +}
We recently added a generic "power-domains-child-ids" DT property,
that allows us to describe child power-domains for these kinds of
cases.
Moreover, we have of_genpd_add|remove_child_ids() to easily hook them
up when probing. I suggest we use that here as well.
> +
> static int ux500_pm_domains_probe(struct platform_device *pdev)
> {
> struct device_node *np = pdev->dev.of_node;
> struct genpd_onecell_data *genpd_data;
> int i;
> + int ret;
>
> if (!np)
> return -ENODEV;
> @@ -196,7 +372,11 @@ static int ux500_pm_domains_probe(struct platform_device
> *pdev)
> genpd_data->num_domains = ARRAY_SIZE(ux500_pm_domains);
>
> for (i = 0; i < ARRAY_SIZE(ux500_pm_domains); ++i)
> - pm_genpd_init(ux500_pm_domains[i], NULL, false);
> + pm_genpd_init(ux500_pm_domains[i], NULL, true);
> +
> + ret = ux500_pm_domains_add_subdomains();
> + if (ret)
> + return ret;
>
> of_genpd_add_provider_onecell(np, genpd_data);
> return 0;
> diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
> index 87554ab92801..35d1b191462c 100644
> --- a/drivers/regulator/Kconfig
> +++ b/drivers/regulator/Kconfig
> @@ -414,6 +414,7 @@ config REGULATOR_DBX500_PRCMU
> config REGULATOR_DB8500_PRCMU
> bool "ST-Ericsson DB8500 Voltage Domain Regulators"
> depends on MFD_DB8500_PRCMU
> + depends on !UX500_PM_DOMAIN
> select REGULATOR_DBX500_PRCMU
> help
> This driver supports the voltage domain regulators controlled by the
>
> --
> 2.54.0
>
Kind regards
Uffe