* [RFC PATCH 1/3] pmdomain: core: Allow a non-CPU device in a CPU PM domain to do power on
2026-08-19 15:14 [RFC PATCH 0/3] pmdomain/cpuidle-psci: Fix behaviours for CPU PM domains Ulf Hansson
@ 2026-08-19 15:14 ` Ulf Hansson
2026-08-19 17:51 ` Abel Vesa
2026-08-19 15:14 ` [RFC PATCH 2/3] pmdomain: core: Add a genpd config to support unknown initial status Ulf Hansson
2026-08-19 15:14 ` [RFC PATCH 3/3] cpuidle: psci: Initialize the PM domains in powered off state for OSI Ulf Hansson
2 siblings, 1 reply; 10+ messages in thread
From: Ulf Hansson @ 2026-08-19 15:14 UTC (permalink / raw)
To: Sudeep Holla, Rafael J . Wysocki, Daniel Lezcano, linux-pm
Cc: Lorenzo Pieralisi, Christian Loehle, Maulik Shah, Yuanfang Zhang,
Suzuki K Poulose, Ulf Hansson, linux-arm-kernel, linux-kernel,
Ulf Hansson
From: Ulf Hansson <ulfh@kernel.org>
A driver for a non-CPU device that is attached to a CPU PM domain (the
genpd has the GENPD_FLAG_CPU_DOMAIN configuration set), is currently not
able to power on the PM domain. More precisely, to power on a CPU PM domain
one of its corresponding CPUs needs to be woken up if they are idle.
The current support for a non-CPU device is that its driver can only
prevent an already powered on CPU PM domain from being powered off. This
leads to problems for a driver while probing its device or when it needs to
call pm_runtime_get_sync() to turn on the power for it. From the driver
point of view it looks like it all works fine, but when accessing the
device it may end up with various errors as the device may not be fully
powered on.
To fix the behavior for these types of devices, let's adjust the behaviour
in genpd_power_on() to wake up an idle CPU that belongs to it, in cases
when it's needed.
Link: https://lore.kernel.org/all/CAPx+jO-sCierYj8jnoKQHckJG16dOBxnNrsZVYO=38R2cLV8nw@mail.gmail.com/
Signed-off-by: Ulf Hansson <ulfh@kernel.org>
---
drivers/pmdomain/core.c | 80 ++++++++++++++++++++++++++++++++++++++---
1 file changed, 75 insertions(+), 5 deletions(-)
diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
index 842c4169e290..7345c06f1d55 100644
--- a/drivers/pmdomain/core.c
+++ b/drivers/pmdomain/core.c
@@ -10,6 +10,7 @@
#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/io.h>
+#include <linux/iopoll.h>
#include <linux/platform_device.h>
#include <linux/pm_opp.h>
#include <linux/pm_runtime.h>
@@ -19,11 +20,14 @@
#include <linux/slab.h>
#include <linux/err.h>
#include <linux/sched.h>
+#include <linux/smp.h>
#include <linux/suspend.h>
#include <linux/export.h>
#include <linux/cpu.h>
#include <linux/debugfs.h>
+#include <trace/events/ipi.h>
+
/* Provides a unique ID for each genpd device */
static DEFINE_IDA(genpd_ida);
@@ -32,7 +36,9 @@ static const struct bus_type genpd_provider_bus_type = {
.name = "genpd_provider",
};
-#define GENPD_RETRY_MAX_MS 250 /* Approximate */
+#define GENPD_RETRY_MAX_MS 250 /* Approximate */
+#define GENPD_CPU_ON_POLL_PERIOD_US 100 /* 100us */
+#define GENPD_CPU_ON_TIMEOUT_US 5000000 /* 5s */
#define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \
({ \
@@ -1027,15 +1033,75 @@ static void genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
}
}
+static bool genpd_check_status_on(struct generic_pm_domain *genpd)
+{
+ bool is_on;
+
+ genpd_lock(genpd);
+ is_on = genpd_status_on(genpd);
+ genpd_unlock(genpd);
+
+ return is_on;
+}
+
+static int genpd_wakeup_cpu(struct generic_pm_domain *genpd)
+{
+ unsigned int cpu;
+ bool is_on;
+ int ret;
+
+ /* Find the first online CPU in the genpd's cpumask. */
+ cpu = cpumask_first_and(genpd->cpus, cpu_online_mask);
+ if (cpu >= nr_cpu_ids)
+ return -EAGAIN;
+
+ genpd_unlock(genpd);
+
+ /* Send a IPI to wakeup the selected CPU. */
+ smp_send_reschedule(cpu);
+
+ /* Poll to wait for it to complete the power on sequence. */
+ ret = readx_poll_timeout(genpd_check_status_on, genpd, is_on, is_on,
+ GENPD_CPU_ON_POLL_PERIOD_US,
+ GENPD_CPU_ON_TIMEOUT_US);
+
+ genpd_lock(genpd);
+
+ /* Re-check the status as we have released the lock in between. */
+ if (ret || !genpd_status_on(genpd))
+ return -EAGAIN;
+
+ return 0;
+}
+
+static bool genpd_need_alive_cpu(struct generic_pm_domain *genpd,
+ struct device *dev)
+{
+ if (!genpd_is_cpu_domain(genpd))
+ return false;
+
+ /* This is not for CPU devices as those are managed differently. */
+ if (to_gpd_data(dev->power.subsys_data->domain_data)->cpu >= 0)
+ return false;
+
+ /*
+ * If the current CPU doesn't belong to the genpd's cpumask, we need to
+ * wake up one of those idle CPUs to power on the CPU domain correctly.
+ */
+ return !cpumask_test_cpu(smp_processor_id(), genpd->cpus);
+}
+
/**
* genpd_power_on - Restore power to a given PM domain and its parents.
* @genpd: PM domain to power up.
+ * @dev: The device that needs the PM domain to power on.
* @depth: nesting count for lockdep.
*
* Restore power to @genpd and all of its parents so that it is possible to
* resume a device belonging to it.
*/
-static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
+static int genpd_power_on(struct generic_pm_domain *genpd, struct device *dev,
+ unsigned int depth)
{
struct gpd_link *link;
int ret = 0;
@@ -1043,6 +1109,10 @@ static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
if (genpd_status_on(genpd))
return 0;
+ /* Special case for a device attached to a CPU domain. */
+ if (genpd_need_alive_cpu(genpd, dev))
+ return genpd_wakeup_cpu(genpd);
+
/* Reflect over the entered idle-states residency for debugfs. */
genpd_reflect_residency(genpd);
@@ -1057,7 +1127,7 @@ static int genpd_power_on(struct generic_pm_domain *genpd, unsigned int depth)
genpd_sd_counter_inc(parent);
genpd_lock_nested(parent, depth + 1);
- ret = genpd_power_on(parent, depth + 1);
+ ret = genpd_power_on(parent, dev, depth + 1);
genpd_unlock(parent);
if (ret) {
@@ -1307,7 +1377,7 @@ static int genpd_runtime_resume(struct device *dev)
genpd_lock(genpd);
genpd_restore_performance_state(dev, gpd_data->rpm_pstate);
- ret = genpd_power_on(genpd, 0);
+ ret = genpd_power_on(genpd, dev, 0);
genpd_unlock(genpd);
if (ret)
@@ -3410,7 +3480,7 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev,
if (power_on) {
genpd_lock(pd);
- ret = genpd_power_on(pd, 0);
+ ret = genpd_power_on(pd, dev, 0);
genpd_unlock(pd);
}
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [RFC PATCH 1/3] pmdomain: core: Allow a non-CPU device in a CPU PM domain to do power on
2026-08-19 15:14 ` [RFC PATCH 1/3] pmdomain: core: Allow a non-CPU device in a CPU PM domain to do power on Ulf Hansson
@ 2026-08-19 17:51 ` Abel Vesa
2026-08-20 9:59 ` Ulf Hansson
0 siblings, 1 reply; 10+ messages in thread
From: Abel Vesa @ 2026-08-19 17:51 UTC (permalink / raw)
To: Ulf Hansson
Cc: Sudeep Holla, Rafael J . Wysocki, Daniel Lezcano, linux-pm,
Lorenzo Pieralisi, Christian Loehle, Maulik Shah, Yuanfang Zhang,
Suzuki K Poulose, linux-arm-kernel, linux-kernel, Ulf Hansson
On 26-08-19 17:14:51, Ulf Hansson wrote:
> From: Ulf Hansson <ulfh@kernel.org>
>
> A driver for a non-CPU device that is attached to a CPU PM domain (the
> genpd has the GENPD_FLAG_CPU_DOMAIN configuration set), is currently not
> able to power on the PM domain. More precisely, to power on a CPU PM domain
> one of its corresponding CPUs needs to be woken up if they are idle.
>
> The current support for a non-CPU device is that its driver can only
> prevent an already powered on CPU PM domain from being powered off. This
> leads to problems for a driver while probing its device or when it needs to
> call pm_runtime_get_sync() to turn on the power for it. From the driver
> point of view it looks like it all works fine, but when accessing the
> device it may end up with various errors as the device may not be fully
> powered on.
>
> To fix the behavior for these types of devices, let's adjust the behaviour
> in genpd_power_on() to wake up an idle CPU that belongs to it, in cases
> when it's needed.
>
> Link: https://lore.kernel.org/all/CAPx+jO-sCierYj8jnoKQHckJG16dOBxnNrsZVYO=38R2cLV8nw@mail.gmail.com/
> Signed-off-by: Ulf Hansson <ulfh@kernel.org>
> ---
> drivers/pmdomain/core.c | 80 ++++++++++++++++++++++++++++++++++++++---
> 1 file changed, 75 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
> index 842c4169e290..7345c06f1d55 100644
> --- a/drivers/pmdomain/core.c
> +++ b/drivers/pmdomain/core.c
> @@ -10,6 +10,7 @@
> #include <linux/idr.h>
> #include <linux/kernel.h>
> #include <linux/io.h>
> +#include <linux/iopoll.h>
> #include <linux/platform_device.h>
> #include <linux/pm_opp.h>
> #include <linux/pm_runtime.h>
> @@ -19,11 +20,14 @@
> #include <linux/slab.h>
> #include <linux/err.h>
> #include <linux/sched.h>
> +#include <linux/smp.h>
> #include <linux/suspend.h>
> #include <linux/export.h>
> #include <linux/cpu.h>
> #include <linux/debugfs.h>
>
> +#include <trace/events/ipi.h>
> +
> /* Provides a unique ID for each genpd device */
> static DEFINE_IDA(genpd_ida);
>
> @@ -32,7 +36,9 @@ static const struct bus_type genpd_provider_bus_type = {
> .name = "genpd_provider",
> };
>
> -#define GENPD_RETRY_MAX_MS 250 /* Approximate */
> +#define GENPD_RETRY_MAX_MS 250 /* Approximate */
> +#define GENPD_CPU_ON_POLL_PERIOD_US 100 /* 100us */
> +#define GENPD_CPU_ON_TIMEOUT_US 5000000 /* 5s */
>
> #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \
> ({ \
> @@ -1027,15 +1033,75 @@ static void genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
> }
> }
>
> +static bool genpd_check_status_on(struct generic_pm_domain *genpd)
> +{
> + bool is_on;
> +
> + genpd_lock(genpd);
> + is_on = genpd_status_on(genpd);
> + genpd_unlock(genpd);
> +
> + return is_on;
> +}
I'd change the genpd_status_on() to genpd_is_on_unlocked().
And then this one would be just genpd_is_on().
The rest looks good, so:
Reviewed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [RFC PATCH 1/3] pmdomain: core: Allow a non-CPU device in a CPU PM domain to do power on
2026-08-19 17:51 ` Abel Vesa
@ 2026-08-20 9:59 ` Ulf Hansson
0 siblings, 0 replies; 10+ messages in thread
From: Ulf Hansson @ 2026-08-20 9:59 UTC (permalink / raw)
To: Abel Vesa
Cc: Sudeep Holla, Rafael J . Wysocki, Daniel Lezcano, linux-pm,
Lorenzo Pieralisi, Christian Loehle, Maulik Shah, Yuanfang Zhang,
Suzuki K Poulose, linux-arm-kernel, linux-kernel, Ulf Hansson
On Wed, Aug 19, 2026 at 7:52 PM Abel Vesa <abel.vesa@oss.qualcomm.com> wrote:
>
> On 26-08-19 17:14:51, Ulf Hansson wrote:
> > From: Ulf Hansson <ulfh@kernel.org>
> >
> > A driver for a non-CPU device that is attached to a CPU PM domain (the
> > genpd has the GENPD_FLAG_CPU_DOMAIN configuration set), is currently not
> > able to power on the PM domain. More precisely, to power on a CPU PM domain
> > one of its corresponding CPUs needs to be woken up if they are idle.
> >
> > The current support for a non-CPU device is that its driver can only
> > prevent an already powered on CPU PM domain from being powered off. This
> > leads to problems for a driver while probing its device or when it needs to
> > call pm_runtime_get_sync() to turn on the power for it. From the driver
> > point of view it looks like it all works fine, but when accessing the
> > device it may end up with various errors as the device may not be fully
> > powered on.
> >
> > To fix the behavior for these types of devices, let's adjust the behaviour
> > in genpd_power_on() to wake up an idle CPU that belongs to it, in cases
> > when it's needed.
> >
> > Link: https://lore.kernel.org/all/CAPx+jO-sCierYj8jnoKQHckJG16dOBxnNrsZVYO=38R2cLV8nw@mail.gmail.com/
> > Signed-off-by: Ulf Hansson <ulfh@kernel.org>
> > ---
> > drivers/pmdomain/core.c | 80 ++++++++++++++++++++++++++++++++++++++---
> > 1 file changed, 75 insertions(+), 5 deletions(-)
> >
> > diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
> > index 842c4169e290..7345c06f1d55 100644
> > --- a/drivers/pmdomain/core.c
> > +++ b/drivers/pmdomain/core.c
> > @@ -10,6 +10,7 @@
> > #include <linux/idr.h>
> > #include <linux/kernel.h>
> > #include <linux/io.h>
> > +#include <linux/iopoll.h>
> > #include <linux/platform_device.h>
> > #include <linux/pm_opp.h>
> > #include <linux/pm_runtime.h>
> > @@ -19,11 +20,14 @@
> > #include <linux/slab.h>
> > #include <linux/err.h>
> > #include <linux/sched.h>
> > +#include <linux/smp.h>
> > #include <linux/suspend.h>
> > #include <linux/export.h>
> > #include <linux/cpu.h>
> > #include <linux/debugfs.h>
> >
> > +#include <trace/events/ipi.h>
> > +
> > /* Provides a unique ID for each genpd device */
> > static DEFINE_IDA(genpd_ida);
> >
> > @@ -32,7 +36,9 @@ static const struct bus_type genpd_provider_bus_type = {
> > .name = "genpd_provider",
> > };
> >
> > -#define GENPD_RETRY_MAX_MS 250 /* Approximate */
> > +#define GENPD_RETRY_MAX_MS 250 /* Approximate */
> > +#define GENPD_CPU_ON_POLL_PERIOD_US 100 /* 100us */
> > +#define GENPD_CPU_ON_TIMEOUT_US 5000000 /* 5s */
> >
> > #define GENPD_DEV_CALLBACK(genpd, type, callback, dev) \
> > ({ \
> > @@ -1027,15 +1033,75 @@ static void genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on,
> > }
> > }
> >
> > +static bool genpd_check_status_on(struct generic_pm_domain *genpd)
> > +{
> > + bool is_on;
> > +
> > + genpd_lock(genpd);
> > + is_on = genpd_status_on(genpd);
> > + genpd_unlock(genpd);
> > +
> > + return is_on;
> > +}
>
> I'd change the genpd_status_on() to genpd_is_on_unlocked().
>
> And then this one would be just genpd_is_on().
Yep, that sounds like an improvement, thanks for the suggestion!
>
> The rest looks good, so:
>
> Reviewed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
Thanks!
Kind regards
Uffe
^ permalink raw reply [flat|nested] 10+ messages in thread
* [RFC PATCH 2/3] pmdomain: core: Add a genpd config to support unknown initial status
2026-08-19 15:14 [RFC PATCH 0/3] pmdomain/cpuidle-psci: Fix behaviours for CPU PM domains Ulf Hansson
2026-08-19 15:14 ` [RFC PATCH 1/3] pmdomain: core: Allow a non-CPU device in a CPU PM domain to do power on Ulf Hansson
@ 2026-08-19 15:14 ` Ulf Hansson
2026-08-19 17:40 ` Abel Vesa
2026-08-19 15:14 ` [RFC PATCH 3/3] cpuidle: psci: Initialize the PM domains in powered off state for OSI Ulf Hansson
2 siblings, 1 reply; 10+ messages in thread
From: Ulf Hansson @ 2026-08-19 15:14 UTC (permalink / raw)
To: Sudeep Holla, Rafael J . Wysocki, Daniel Lezcano, linux-pm
Cc: Lorenzo Pieralisi, Christian Loehle, Maulik Shah, Yuanfang Zhang,
Suzuki K Poulose, Ulf Hansson, linux-arm-kernel, linux-kernel,
Ulf Hansson
From: Ulf Hansson <ulfh@kernel.org>
It's not always possible for a genpd provider to know the initial status
for its corresponding PM domain(s). To register the PM domain in a safe
state, the genpd provider driver may therefore have to initialize the
genpd's status to be powered off, as a way to prevent the PM domain from
being used when it actually could be powered off.
In these cases we may end up to power on a PM domain through the genpd
subsystem, while from the HW point of view it may already be powered on.
Under these circumstances, it may also be required to keep the PM domain
powered on until all the consumer devices have been probed. In other words,
until the ->sync_state() callback for the genpd provider in question have
been called.
To support this behaviour for a genpd provider, let's introduce a new genpd
configuration, GENPD_FLAG_OFF_STAY_ON.
Reported-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
Link: https://lore.kernel.org/all/20260811-domain_off_ss3-v1-0-6a0a0fc023f5@oss.qualcomm.com/
Signed-off-by: Ulf Hansson <ulfh@kernel.org>
---
drivers/pmdomain/core.c | 6 +++++-
include/linux/pm_domain.h | 9 +++++++++
2 files changed, 14 insertions(+), 1 deletion(-)
diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
index 7345c06f1d55..be4fff2f4664 100644
--- a/drivers/pmdomain/core.c
+++ b/drivers/pmdomain/core.c
@@ -189,6 +189,7 @@ static const struct genpd_lock_ops genpd_raw_spin_ops = {
#define genpd_is_dev_name_fw(genpd) (genpd->flags & GENPD_FLAG_DEV_NAME_FW)
#define genpd_is_no_sync_state(genpd) (genpd->flags & GENPD_FLAG_NO_SYNC_STATE)
#define genpd_is_no_stay_on(genpd) (genpd->flags & GENPD_FLAG_NO_STAY_ON)
+#define genpd_is_off_stay_on(genpd) (genpd->flags & GENPD_FLAG_OFF_STAY_ON)
static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,
const struct generic_pm_domain *genpd)
@@ -2445,7 +2446,10 @@ static void genpd_lock_init(struct generic_pm_domain *genpd)
#ifdef CONFIG_PM_GENERIC_DOMAINS_OF
static void genpd_set_stay_on(struct generic_pm_domain *genpd, bool is_off)
{
- genpd->stay_on = !genpd_is_no_stay_on(genpd) && !is_off;
+ if (genpd_is_off_stay_on(genpd))
+ genpd->stay_on = is_off;
+ else
+ genpd->stay_on = !genpd_is_no_stay_on(genpd) && !is_off;
}
#else
static void genpd_set_stay_on(struct generic_pm_domain *genpd, bool is_off)
diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
index f925614aebdb..9929347e1d7f 100644
--- a/include/linux/pm_domain.h
+++ b/include/linux/pm_domain.h
@@ -121,6 +121,14 @@ struct dev_pm_domain_list {
* powered-off until the ->sync_state() callback is
* invoked. This flag informs genpd to allow a
* power-off without waiting for ->sync_state().
+ *
+ * GENPD_FLAG_OFF_STAY_ON: Use this flag to inform genpd that its initial
+ * status for the PM domain is set to powered off,
+ * which may not correctly reflect the state of the
+ * HW, as it's unknown. If the PM domain becomes
+ * powered on during boot, genpd will prevent it
+ * from being powered off until the ->sync_state
+ * callback is invoked for it.
*/
#define GENPD_FLAG_PM_CLK (1U << 0)
#define GENPD_FLAG_IRQ_SAFE (1U << 1)
@@ -133,6 +141,7 @@ struct dev_pm_domain_list {
#define GENPD_FLAG_DEV_NAME_FW (1U << 8)
#define GENPD_FLAG_NO_SYNC_STATE (1U << 9)
#define GENPD_FLAG_NO_STAY_ON (1U << 10)
+#define GENPD_FLAG_OFF_STAY_ON (1U << 11)
enum gpd_status {
GENPD_STATE_ON = 0, /* PM domain is on */
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [RFC PATCH 2/3] pmdomain: core: Add a genpd config to support unknown initial status
2026-08-19 15:14 ` [RFC PATCH 2/3] pmdomain: core: Add a genpd config to support unknown initial status Ulf Hansson
@ 2026-08-19 17:40 ` Abel Vesa
2026-08-20 9:56 ` Ulf Hansson
0 siblings, 1 reply; 10+ messages in thread
From: Abel Vesa @ 2026-08-19 17:40 UTC (permalink / raw)
To: Ulf Hansson
Cc: Sudeep Holla, Rafael J . Wysocki, Daniel Lezcano, linux-pm,
Lorenzo Pieralisi, Christian Loehle, Maulik Shah, Yuanfang Zhang,
Suzuki K Poulose, linux-arm-kernel, linux-kernel, Ulf Hansson
On 26-08-19 17:14:52, Ulf Hansson wrote:
> From: Ulf Hansson <ulfh@kernel.org>
>
> It's not always possible for a genpd provider to know the initial status
> for its corresponding PM domain(s). To register the PM domain in a safe
> state, the genpd provider driver may therefore have to initialize the
> genpd's status to be powered off, as a way to prevent the PM domain from
> being used when it actually could be powered off.
>
> In these cases we may end up to power on a PM domain through the genpd
> subsystem, while from the HW point of view it may already be powered on.
> Under these circumstances, it may also be required to keep the PM domain
> powered on until all the consumer devices have been probed. In other words,
> until the ->sync_state() callback for the genpd provider in question have
> been called.
>
> To support this behaviour for a genpd provider, let's introduce a new genpd
> configuration, GENPD_FLAG_OFF_STAY_ON.
>
> Reported-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
> Link: https://lore.kernel.org/all/20260811-domain_off_ss3-v1-0-6a0a0fc023f5@oss.qualcomm.com/
> Signed-off-by: Ulf Hansson <ulfh@kernel.org>
> ---
> drivers/pmdomain/core.c | 6 +++++-
> include/linux/pm_domain.h | 9 +++++++++
> 2 files changed, 14 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
> index 7345c06f1d55..be4fff2f4664 100644
> --- a/drivers/pmdomain/core.c
> +++ b/drivers/pmdomain/core.c
> @@ -189,6 +189,7 @@ static const struct genpd_lock_ops genpd_raw_spin_ops = {
> #define genpd_is_dev_name_fw(genpd) (genpd->flags & GENPD_FLAG_DEV_NAME_FW)
> #define genpd_is_no_sync_state(genpd) (genpd->flags & GENPD_FLAG_NO_SYNC_STATE)
> #define genpd_is_no_stay_on(genpd) (genpd->flags & GENPD_FLAG_NO_STAY_ON)
> +#define genpd_is_off_stay_on(genpd) (genpd->flags & GENPD_FLAG_OFF_STAY_ON)
Maybe genpd_is_state_unknown ?
>
> static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,
> const struct generic_pm_domain *genpd)
> @@ -2445,7 +2446,10 @@ static void genpd_lock_init(struct generic_pm_domain *genpd)
> #ifdef CONFIG_PM_GENERIC_DOMAINS_OF
> static void genpd_set_stay_on(struct generic_pm_domain *genpd, bool is_off)
> {
> - genpd->stay_on = !genpd_is_no_stay_on(genpd) && !is_off;
> + if (genpd_is_off_stay_on(genpd))
> + genpd->stay_on = is_off;
> + else
> + genpd->stay_on = !genpd_is_no_stay_on(genpd) && !is_off;
> }
> #else
> static void genpd_set_stay_on(struct generic_pm_domain *genpd, bool is_off)
> diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
> index f925614aebdb..9929347e1d7f 100644
> --- a/include/linux/pm_domain.h
> +++ b/include/linux/pm_domain.h
> @@ -121,6 +121,14 @@ struct dev_pm_domain_list {
> * powered-off until the ->sync_state() callback is
> * invoked. This flag informs genpd to allow a
> * power-off without waiting for ->sync_state().
> + *
> + * GENPD_FLAG_OFF_STAY_ON: Use this flag to inform genpd that its initial
> + * status for the PM domain is set to powered off,
> + * which may not correctly reflect the state of the
> + * HW, as it's unknown. If the PM domain becomes
> + * powered on during boot, genpd will prevent it
> + * from being powered off until the ->sync_state
> + * callback is invoked for it.
Maybe GENPD_FLAG_STATE_UNKNOWN ?
Or probably it would be better to just add a new state to the gpd_status
enum, like GENPD_STATE_UNKNOWN.
> */
> #define GENPD_FLAG_PM_CLK (1U << 0)
> #define GENPD_FLAG_IRQ_SAFE (1U << 1)
> @@ -133,6 +141,7 @@ struct dev_pm_domain_list {
> #define GENPD_FLAG_DEV_NAME_FW (1U << 8)
> #define GENPD_FLAG_NO_SYNC_STATE (1U << 9)
> #define GENPD_FLAG_NO_STAY_ON (1U << 10)
> +#define GENPD_FLAG_OFF_STAY_ON (1U << 11)
>
> enum gpd_status {
> GENPD_STATE_ON = 0, /* PM domain is on */
> --
> 2.43.0
>
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [RFC PATCH 2/3] pmdomain: core: Add a genpd config to support unknown initial status
2026-08-19 17:40 ` Abel Vesa
@ 2026-08-20 9:56 ` Ulf Hansson
2026-08-20 20:31 ` Abel Vesa
0 siblings, 1 reply; 10+ messages in thread
From: Ulf Hansson @ 2026-08-20 9:56 UTC (permalink / raw)
To: Abel Vesa
Cc: Sudeep Holla, Rafael J . Wysocki, Daniel Lezcano, linux-pm,
Lorenzo Pieralisi, Christian Loehle, Maulik Shah, Yuanfang Zhang,
Suzuki K Poulose, linux-arm-kernel, linux-kernel, Ulf Hansson
On Wed, Aug 19, 2026 at 7:40 PM Abel Vesa <abel.vesa@oss.qualcomm.com> wrote:
>
> On 26-08-19 17:14:52, Ulf Hansson wrote:
> > From: Ulf Hansson <ulfh@kernel.org>
> >
> > It's not always possible for a genpd provider to know the initial status
> > for its corresponding PM domain(s). To register the PM domain in a safe
> > state, the genpd provider driver may therefore have to initialize the
> > genpd's status to be powered off, as a way to prevent the PM domain from
> > being used when it actually could be powered off.
> >
> > In these cases we may end up to power on a PM domain through the genpd
> > subsystem, while from the HW point of view it may already be powered on.
> > Under these circumstances, it may also be required to keep the PM domain
> > powered on until all the consumer devices have been probed. In other words,
> > until the ->sync_state() callback for the genpd provider in question have
> > been called.
> >
> > To support this behaviour for a genpd provider, let's introduce a new genpd
> > configuration, GENPD_FLAG_OFF_STAY_ON.
> >
> > Reported-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
> > Link: https://lore.kernel.org/all/20260811-domain_off_ss3-v1-0-6a0a0fc023f5@oss.qualcomm.com/
> > Signed-off-by: Ulf Hansson <ulfh@kernel.org>
> > ---
> > drivers/pmdomain/core.c | 6 +++++-
> > include/linux/pm_domain.h | 9 +++++++++
> > 2 files changed, 14 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
> > index 7345c06f1d55..be4fff2f4664 100644
> > --- a/drivers/pmdomain/core.c
> > +++ b/drivers/pmdomain/core.c
> > @@ -189,6 +189,7 @@ static const struct genpd_lock_ops genpd_raw_spin_ops = {
> > #define genpd_is_dev_name_fw(genpd) (genpd->flags & GENPD_FLAG_DEV_NAME_FW)
> > #define genpd_is_no_sync_state(genpd) (genpd->flags & GENPD_FLAG_NO_SYNC_STATE)
> > #define genpd_is_no_stay_on(genpd) (genpd->flags & GENPD_FLAG_NO_STAY_ON)
> > +#define genpd_is_off_stay_on(genpd) (genpd->flags & GENPD_FLAG_OFF_STAY_ON)
>
> Maybe genpd_is_state_unknown ?
>
> >
> > static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,
> > const struct generic_pm_domain *genpd)
> > @@ -2445,7 +2446,10 @@ static void genpd_lock_init(struct generic_pm_domain *genpd)
> > #ifdef CONFIG_PM_GENERIC_DOMAINS_OF
> > static void genpd_set_stay_on(struct generic_pm_domain *genpd, bool is_off)
> > {
> > - genpd->stay_on = !genpd_is_no_stay_on(genpd) && !is_off;
> > + if (genpd_is_off_stay_on(genpd))
> > + genpd->stay_on = is_off;
> > + else
> > + genpd->stay_on = !genpd_is_no_stay_on(genpd) && !is_off;
> > }
> > #else
> > static void genpd_set_stay_on(struct generic_pm_domain *genpd, bool is_off)
> > diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
> > index f925614aebdb..9929347e1d7f 100644
> > --- a/include/linux/pm_domain.h
> > +++ b/include/linux/pm_domain.h
> > @@ -121,6 +121,14 @@ struct dev_pm_domain_list {
> > * powered-off until the ->sync_state() callback is
> > * invoked. This flag informs genpd to allow a
> > * power-off without waiting for ->sync_state().
> > + *
> > + * GENPD_FLAG_OFF_STAY_ON: Use this flag to inform genpd that its initial
> > + * status for the PM domain is set to powered off,
> > + * which may not correctly reflect the state of the
> > + * HW, as it's unknown. If the PM domain becomes
> > + * powered on during boot, genpd will prevent it
> > + * from being powered off until the ->sync_state
> > + * callback is invoked for it.
>
> Maybe GENPD_FLAG_STATE_UNKNOWN ?
Hmm, I understand your idea to clarify this and it sounds reasonable to me.
However, "STATE" sounds a bit vague, what do you think of
"GENPD_FLAG_POWER_UNKNOWN"?
>
> Or probably it would be better to just add a new state to the gpd_status
> enum, like GENPD_STATE_UNKNOWN.
Well, the problem with that is how it should be treated by genpd
internals, when there are child/parent domains. Having an in-between
status sounds unnecessarily complicated to me, at least for the
purpose of $subject patch.
>
> > */
> > #define GENPD_FLAG_PM_CLK (1U << 0)
> > #define GENPD_FLAG_IRQ_SAFE (1U << 1)
> > @@ -133,6 +141,7 @@ struct dev_pm_domain_list {
> > #define GENPD_FLAG_DEV_NAME_FW (1U << 8)
> > #define GENPD_FLAG_NO_SYNC_STATE (1U << 9)
> > #define GENPD_FLAG_NO_STAY_ON (1U << 10)
> > +#define GENPD_FLAG_OFF_STAY_ON (1U << 11)
> >
> > enum gpd_status {
> > GENPD_STATE_ON = 0, /* PM domain is on */
> > --
> > 2.43.0
> >
Kind regards
Uffe
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [RFC PATCH 2/3] pmdomain: core: Add a genpd config to support unknown initial status
2026-08-20 9:56 ` Ulf Hansson
@ 2026-08-20 20:31 ` Abel Vesa
0 siblings, 0 replies; 10+ messages in thread
From: Abel Vesa @ 2026-08-20 20:31 UTC (permalink / raw)
To: Ulf Hansson
Cc: Sudeep Holla, Rafael J . Wysocki, Daniel Lezcano, linux-pm,
Lorenzo Pieralisi, Christian Loehle, Maulik Shah, Yuanfang Zhang,
Suzuki K Poulose, linux-arm-kernel, linux-kernel, Ulf Hansson
On 26-08-20 11:56:40, Ulf Hansson wrote:
> On Wed, Aug 19, 2026 at 7:40 PM Abel Vesa <abel.vesa@oss.qualcomm.com> wrote:
> >
> > On 26-08-19 17:14:52, Ulf Hansson wrote:
> > > From: Ulf Hansson <ulfh@kernel.org>
> > >
> > > It's not always possible for a genpd provider to know the initial status
> > > for its corresponding PM domain(s). To register the PM domain in a safe
> > > state, the genpd provider driver may therefore have to initialize the
> > > genpd's status to be powered off, as a way to prevent the PM domain from
> > > being used when it actually could be powered off.
> > >
> > > In these cases we may end up to power on a PM domain through the genpd
> > > subsystem, while from the HW point of view it may already be powered on.
> > > Under these circumstances, it may also be required to keep the PM domain
> > > powered on until all the consumer devices have been probed. In other words,
> > > until the ->sync_state() callback for the genpd provider in question have
> > > been called.
> > >
> > > To support this behaviour for a genpd provider, let's introduce a new genpd
> > > configuration, GENPD_FLAG_OFF_STAY_ON.
> > >
> > > Reported-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
> > > Link: https://lore.kernel.org/all/20260811-domain_off_ss3-v1-0-6a0a0fc023f5@oss.qualcomm.com/
> > > Signed-off-by: Ulf Hansson <ulfh@kernel.org>
> > > ---
> > > drivers/pmdomain/core.c | 6 +++++-
> > > include/linux/pm_domain.h | 9 +++++++++
> > > 2 files changed, 14 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/pmdomain/core.c b/drivers/pmdomain/core.c
> > > index 7345c06f1d55..be4fff2f4664 100644
> > > --- a/drivers/pmdomain/core.c
> > > +++ b/drivers/pmdomain/core.c
> > > @@ -189,6 +189,7 @@ static const struct genpd_lock_ops genpd_raw_spin_ops = {
> > > #define genpd_is_dev_name_fw(genpd) (genpd->flags & GENPD_FLAG_DEV_NAME_FW)
> > > #define genpd_is_no_sync_state(genpd) (genpd->flags & GENPD_FLAG_NO_SYNC_STATE)
> > > #define genpd_is_no_stay_on(genpd) (genpd->flags & GENPD_FLAG_NO_STAY_ON)
> > > +#define genpd_is_off_stay_on(genpd) (genpd->flags & GENPD_FLAG_OFF_STAY_ON)
> >
> > Maybe genpd_is_state_unknown ?
> >
> > >
> > > static inline bool irq_safe_dev_in_sleep_domain(struct device *dev,
> > > const struct generic_pm_domain *genpd)
> > > @@ -2445,7 +2446,10 @@ static void genpd_lock_init(struct generic_pm_domain *genpd)
> > > #ifdef CONFIG_PM_GENERIC_DOMAINS_OF
> > > static void genpd_set_stay_on(struct generic_pm_domain *genpd, bool is_off)
> > > {
> > > - genpd->stay_on = !genpd_is_no_stay_on(genpd) && !is_off;
> > > + if (genpd_is_off_stay_on(genpd))
> > > + genpd->stay_on = is_off;
> > > + else
> > > + genpd->stay_on = !genpd_is_no_stay_on(genpd) && !is_off;
> > > }
> > > #else
> > > static void genpd_set_stay_on(struct generic_pm_domain *genpd, bool is_off)
> > > diff --git a/include/linux/pm_domain.h b/include/linux/pm_domain.h
> > > index f925614aebdb..9929347e1d7f 100644
> > > --- a/include/linux/pm_domain.h
> > > +++ b/include/linux/pm_domain.h
> > > @@ -121,6 +121,14 @@ struct dev_pm_domain_list {
> > > * powered-off until the ->sync_state() callback is
> > > * invoked. This flag informs genpd to allow a
> > > * power-off without waiting for ->sync_state().
> > > + *
> > > + * GENPD_FLAG_OFF_STAY_ON: Use this flag to inform genpd that its initial
> > > + * status for the PM domain is set to powered off,
> > > + * which may not correctly reflect the state of the
> > > + * HW, as it's unknown. If the PM domain becomes
> > > + * powered on during boot, genpd will prevent it
> > > + * from being powered off until the ->sync_state
> > > + * callback is invoked for it.
> >
> > Maybe GENPD_FLAG_STATE_UNKNOWN ?
>
> Hmm, I understand your idea to clarify this and it sounds reasonable to me.
>
> However, "STATE" sounds a bit vague, what do you think of
> "GENPD_FLAG_POWER_UNKNOWN"?
I guess that could work too. I was suggesting "STATE" because the
gpd_status holds STATE_ON and STATE_OFF. So it would be the flag that
says the state is unknown.
>
> >
> > Or probably it would be better to just add a new state to the gpd_status
> > enum, like GENPD_STATE_UNKNOWN.
>
> Well, the problem with that is how it should be treated by genpd
> internals, when there are child/parent domains. Having an in-between
> status sounds unnecessarily complicated to me, at least for the
> purpose of $subject patch.
As for it being unnecesarily complicated, it would actually be the
true description of such PD. Otherwise we might end up with cases
where it is inconsistent with the actual hardware state.
But I agree, it isn't something that should be part of this patch.
Either way, I think the patch looks good:
Reviewed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 10+ messages in thread
* [RFC PATCH 3/3] cpuidle: psci: Initialize the PM domains in powered off state for OSI
2026-08-19 15:14 [RFC PATCH 0/3] pmdomain/cpuidle-psci: Fix behaviours for CPU PM domains Ulf Hansson
2026-08-19 15:14 ` [RFC PATCH 1/3] pmdomain: core: Allow a non-CPU device in a CPU PM domain to do power on Ulf Hansson
2026-08-19 15:14 ` [RFC PATCH 2/3] pmdomain: core: Add a genpd config to support unknown initial status Ulf Hansson
@ 2026-08-19 15:14 ` Ulf Hansson
2026-08-21 3:24 ` Abel Vesa
2 siblings, 1 reply; 10+ messages in thread
From: Ulf Hansson @ 2026-08-19 15:14 UTC (permalink / raw)
To: Sudeep Holla, Rafael J . Wysocki, Daniel Lezcano, linux-pm
Cc: Lorenzo Pieralisi, Christian Loehle, Maulik Shah, Yuanfang Zhang,
Suzuki K Poulose, Ulf Hansson, linux-arm-kernel, linux-kernel,
Ulf Hansson
From: Ulf Hansson <ulfh@kernel.org>
At the point when the PM domain and the topology are registered through the
genpd subsystem, it's not really known whether corresponding CPUs are
online and thus if the PM domain should be initialized as powered on or
not. Instead this information becomes available when the CPU devices gets
attached to their respective PM domain through dt_idle_attach_cpu().
This is a problem when using PSCI OS-initiated mode, as we may end up with
a PM domain that has the genpd's status indicating it to be powered on,
while it in fact may not be the case. In the less severe scenario, this
leads to selecting a shallower domain idle state for the PM domain than
necessary. A more critical problem is when a non-CPU device shares the PM
domain, leading to their corresponding drivers not being able to trust the
status of it.
Let's fix these problems by initializing the state for the genpd's to be
powered off and in the deepest possible domain idle state, when using
OS-initiated mode. The support for ->sync_state() is maintained by setting
the GENPD_FLAG_OFF_STAY_ON for the genpds in question.
Reported-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
Link: https://lore.kernel.org/all/20260811-domain_off_ss3-v1-0-6a0a0fc023f5@oss.qualcomm.com/
Signed-off-by: Ulf Hansson <ulfh@kernel.org>
---
drivers/cpuidle/cpuidle-psci-domain.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/cpuidle/cpuidle-psci-domain.c b/drivers/cpuidle/cpuidle-psci-domain.c
index b9e4ad7d43a3..29caf3114185 100644
--- a/drivers/cpuidle/cpuidle-psci-domain.c
+++ b/drivers/cpuidle/cpuidle-psci-domain.c
@@ -68,7 +68,8 @@ static int psci_pd_init(struct device_node *np, bool use_osi)
*/
if (use_osi) {
pd->power_off = psci_pd_power_off;
- pd->flags |= GENPD_FLAG_ACTIVE_WAKEUP;
+ pd->flags |= GENPD_FLAG_ACTIVE_WAKEUP | GENPD_FLAG_OFF_STAY_ON;
+ pd->state_idx = pd->state_count ? pd->state_count - 1 : 0;
if (IS_ENABLED(CONFIG_PREEMPT_RT))
pd->flags |= GENPD_FLAG_RPM_ALWAYS_ON;
} else {
@@ -78,7 +79,7 @@ static int psci_pd_init(struct device_node *np, bool use_osi)
/* Use governor for CPU PM domains if it has some states to manage. */
pd_gov = pd->states ? &pm_domain_cpu_gov : NULL;
- ret = pm_genpd_init(pd, pd_gov, false);
+ ret = pm_genpd_init(pd, pd_gov, !use_osi);
if (ret)
goto free_pd_prov;
--
2.43.0
^ permalink raw reply related [flat|nested] 10+ messages in thread* Re: [RFC PATCH 3/3] cpuidle: psci: Initialize the PM domains in powered off state for OSI
2026-08-19 15:14 ` [RFC PATCH 3/3] cpuidle: psci: Initialize the PM domains in powered off state for OSI Ulf Hansson
@ 2026-08-21 3:24 ` Abel Vesa
0 siblings, 0 replies; 10+ messages in thread
From: Abel Vesa @ 2026-08-21 3:24 UTC (permalink / raw)
To: Ulf Hansson
Cc: Sudeep Holla, Rafael J . Wysocki, Daniel Lezcano, linux-pm,
Lorenzo Pieralisi, Christian Loehle, Maulik Shah, Yuanfang Zhang,
Suzuki K Poulose, linux-arm-kernel, linux-kernel, Ulf Hansson
On 26-08-19 17:14:53, Ulf Hansson wrote:
> From: Ulf Hansson <ulfh@kernel.org>
>
> At the point when the PM domain and the topology are registered through the
> genpd subsystem, it's not really known whether corresponding CPUs are
> online and thus if the PM domain should be initialized as powered on or
> not. Instead this information becomes available when the CPU devices gets
> attached to their respective PM domain through dt_idle_attach_cpu().
>
> This is a problem when using PSCI OS-initiated mode, as we may end up with
> a PM domain that has the genpd's status indicating it to be powered on,
> while it in fact may not be the case. In the less severe scenario, this
> leads to selecting a shallower domain idle state for the PM domain than
> necessary. A more critical problem is when a non-CPU device shares the PM
> domain, leading to their corresponding drivers not being able to trust the
> status of it.
Exactly why a third state like GENPD_STATE_UNKNOWN would be more
fitting, IMO. Maybe something we can do at some point.
This solution works for now as well.
>
> Let's fix these problems by initializing the state for the genpd's to be
> powered off and in the deepest possible domain idle state, when using
> OS-initiated mode. The support for ->sync_state() is maintained by setting
> the GENPD_FLAG_OFF_STAY_ON for the genpds in question.
>
> Reported-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
> Link: https://lore.kernel.org/all/20260811-domain_off_ss3-v1-0-6a0a0fc023f5@oss.qualcomm.com/
> Signed-off-by: Ulf Hansson <ulfh@kernel.org>
Reviewed-by: Abel Vesa <abel.vesa@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 10+ messages in thread