[PATCH v3 12/18] pmdomain: st: ux500: Control DB8500 EPODs

Ulf Hansson ulf.hansson at oss.qualcomm.com
Thu Sep 24 06:44:57 PDT 2026


On Mon, Sep 21, 2026 at 12:49 AM Linus Walleij <linusw at kernel.org> 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: LLM
> Signed-off-by: Linus Walleij <linusw at kernel.org>
> ---
>  arch/arm/mach-ux500/Kconfig               |   2 +-
>  drivers/pmdomain/st/ste-ux500-pm-domain.c | 355 +++++++++++++++++++++---------
>  drivers/regulator/Kconfig                 |   1 +
>  3 files changed, 254 insertions(+), 104 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 d90a563cd6f3..b31a36a4acd1 100644
> --- a/drivers/pmdomain/st/ste-ux500-pm-domain.c
> +++ b/drivers/pmdomain/st/ste-ux500-pm-domain.c
> @@ -6,174 +6,292 @@
>   *
>   * Implements PM domains using the generic PM domain for ux500.
>   */
> +#include <linux/cleanup.h>
>  #include <linux/device.h>
> -#include <linux/kernel.h>
> -#include <linux/platform_device.h>
> -#include <linux/printk.h>
> -#include <linux/slab.h>
>  #include <linux/err.h>
> +#include <linux/kernel.h>
> +#include <linux/mfd/db8500-prcmu.h>
> +#include <linux/mutex.h>
>  #include <linux/of.h>
> +#include <linux/platform_device.h>
>  #include <linux/pm_domain.h>
> +#include <linux/slab.h>
>
>  #include <dt-bindings/arm/ux500_pm_domains.h>
>
>  #define NR_DOMAINS             (DOMAIN_ESRAM_34 + 1)
> +#define UX500_EPOD_NONE                NUM_EPOD_ID
>
> -static int pd_power_off(struct generic_pm_domain *domain)
> +/**
> + * 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)
> + */
> +struct dbx500_powerdomain_info {
> +       struct generic_pm_domain genpd;
> +       u16 epod_id;
> +       bool is_ramret;

This one is unused for the defined power domains. Is the plan to add
support for this on top, later on?

> +};
> +
> +static DEFINE_MUTEX(ux500_pd_lock);
> +static bool epod_on[NUM_EPOD_ID];
> +static bool epod_ramret[NUM_EPOD_ID];
> +
> +static int enable_epod(u16 epod_id, bool ramret)
>  {
> -       /*
> -        * 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.
> -        */
> +       int ret;
> +
> +       if (ramret) {
> +               if (!epod_on[epod_id]) {
> +                       ret = db8500_prcmu_set_epod(epod_id, EPOD_STATE_RAMRET);
> +                       if (ret < 0)
> +                               return ret;
> +               }
> +               epod_ramret[epod_id] = true;
> +       } else {
> +               ret = db8500_prcmu_set_epod(epod_id, EPOD_STATE_ON);
> +               if (ret < 0)
> +                       return ret;
> +               epod_on[epod_id] = true;
> +       }
> +
>         return 0;
>  }
>
> -static int pd_power_on(struct generic_pm_domain *domain)
> +static int disable_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 = db8500_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 = db8500_prcmu_set_epod(epod_id, EPOD_STATE_RAMRET);
> +                       if (ret < 0)
> +                               return ret;
> +               } else {
> +                       ret = db8500_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);
> +
> +       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);
> +
> +       return ret;
> +}
> +
>  /*
>   * Apart from these voltage domains there is also VSAFE which is always
>   * on. Vape_esram0_pwr for eSRAM0 is connected to VSAFE.
>   */
> -static struct generic_pm_domain ux500_pm_domain_vape = {
> +static struct dbx500_powerdomain_info ux500_pm_domain_vape = {
>         /* Vape_pwr */
> -       .name = "VAPE",  /* 0.95 .. 1.20 V */
> -       .power_off = pd_power_off,
> -       .power_on = pd_power_on,
> +       .genpd = {
> +               .name = "VAPE",  /* 0.95 .. 1.20 V */
> +               .power_off = pd_power_off,
> +               .power_on = pd_power_on,
> +       },
> +       .epod_id = UX500_EPOD_NONE,

Several power domains below share this epod_id. What does that really
mean? Or is this only temporary?

>  };
>
> -static struct generic_pm_domain ux500_pm_domain_varm = {
> -       .name = "VARM",
> -       .power_off = pd_power_off,
> -       .power_on = pd_power_on,
> +static struct dbx500_powerdomain_info ux500_pm_domain_varm = {
> +       .genpd = {
> +               .name = "VARM",
> +               .power_off = pd_power_off,
> +               .power_on = pd_power_on,
> +       },
> +       .epod_id = UX500_EPOD_NONE,
>  };
>
> -static struct generic_pm_domain ux500_pm_domain_vmodem = {
> -       .name = "VMODEM",
> -       .power_off = pd_power_off,
> -       .power_on = pd_power_on,
> +static struct dbx500_powerdomain_info ux500_pm_domain_vmodem = {
> +       .genpd = {
> +               .name = "VMODEM",
> +               .power_off = pd_power_off,
> +               .power_on = pd_power_on,
> +       },
> +       .epod_id = UX500_EPOD_NONE,
>  };
>
> -static struct generic_pm_domain ux500_pm_domain_vpll = {
> -       .name = "VPLL", /* 1.8 V */
> -       .power_off = pd_power_off,
> -       .power_on = pd_power_on,
> +static struct dbx500_powerdomain_info ux500_pm_domain_vpll = {
> +       .genpd = {
> +               .name = "VPLL", /* 1.8 V */
> +               .power_off = pd_power_off,
> +               .power_on = pd_power_on,
> +       },
> +       .epod_id = UX500_EPOD_NONE,
>  };
>
>  /*
>   * CHECKME: as these are used directly by peripherals as regulators,
>   * perhaps they should stay in the regulator subsystem?
>   */
> -static struct generic_pm_domain ux500_pm_domain_vsmps1 = {
> -       .name = "VSMPS1", /* Also called VIO (1.2V) */
> -       .power_off = pd_power_off,
> -       .power_on = pd_power_on,
> +static struct dbx500_powerdomain_info ux500_pm_domain_vsmps1 = {
> +       .genpd = {
> +               .name = "VSMPS1", /* Also called VIO (1.2V) */
> +               .power_off = pd_power_off,
> +               .power_on = pd_power_on,
> +       },
> +       .epod_id = UX500_EPOD_NONE,
>  };
>
> -static struct generic_pm_domain ux500_pm_domain_vsmps2 = {
> -       .name = "VSMPS2", /* Also called VIO (1.8V) */
> -       .power_off = pd_power_off,
> -       .power_on = pd_power_on,
> +static struct dbx500_powerdomain_info ux500_pm_domain_vsmps2 = {
> +       .genpd = {
> +               .name = "VSMPS2", /* Also called VIO (1.8V) */
> +               .power_off = pd_power_off,
> +               .power_on = pd_power_on,
> +       },
> +       .epod_id = UX500_EPOD_NONE,
>  };
>
> -static struct generic_pm_domain ux500_pm_domain_vsmps3 = {
> -       .name = "VSMPS3",
> -       .power_off = pd_power_off,
> -       .power_on = pd_power_on,
> +static struct dbx500_powerdomain_info ux500_pm_domain_vsmps3 = {
> +       .genpd = {
> +               .name = "VSMPS3",
> +               .power_off = pd_power_off,
> +               .power_on = pd_power_on,
> +       },
> +       .epod_id = UX500_EPOD_NONE,
>  };
>
> -static struct generic_pm_domain ux500_pm_domain_vrf1 = {
> -       .name = "VRF1",
> -       .power_off = pd_power_off,
> -       .power_on = pd_power_on,
> +static struct dbx500_powerdomain_info ux500_pm_domain_vrf1 = {
> +       .genpd = {
> +               .name = "VRF1",
> +               .power_off = pd_power_off,
> +               .power_on = pd_power_on,
> +       },
> +       .epod_id = UX500_EPOD_NONE,
>  };

[...]

Kind regards
Uffe



More information about the linux-arm-kernel mailing list