[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