[PATCH] PM: cpu: Add CPU_LAST_PM_ENTER and CPU_FIRST_PM_EXIT support

From: Shawn Guo
Date: Sat Feb 19 2022 - 00:35:47 EST


It becomes a common situation on some platforms that certain hardware
setup needs to be done on the last standing cpu, and rpmh-rsc[1] is such
an existing example. As figuring out the last standing cpu is really
something generic, it adds CPU_LAST_PM_ENTER (and CPU_FIRST_PM_EXIT)
event support to cpu_pm helper, so that individual driver can be
notified when the last standing cpu is about to enter low power state.

[1] https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/soc/qcom/rpmh-rsc.c?id=v5.16#n773

Signed-off-by: Shawn Guo <shawn.guo@xxxxxxxxxx>
---
include/linux/cpu_pm.h | 15 +++++++++++++++
kernel/cpu_pm.c | 33 +++++++++++++++++++++++++++++++--
2 files changed, 46 insertions(+), 2 deletions(-)

diff --git a/include/linux/cpu_pm.h b/include/linux/cpu_pm.h
index 552b8f9ea05e..153344307b7c 100644
--- a/include/linux/cpu_pm.h
+++ b/include/linux/cpu_pm.h
@@ -55,6 +55,21 @@ enum cpu_pm_event {

/* A cpu power domain is exiting a low power state */
CPU_CLUSTER_PM_EXIT,
+
+ /*
+ * A cpu is entering a low power state after all other cpus
+ * in the system have entered the lower power state.
+ */
+ CPU_LAST_PM_ENTER,
+
+ /* The last cpu failed to enter a low power state */
+ CPU_LAST_PM_ENTER_FAILED,
+
+ /*
+ * A cpu is exiting a low power state before any other cpus
+ * in the system exits the low power state.
+ */
+ CPU_FIRST_PM_EXIT,
};

#ifdef CONFIG_CPU_PM
diff --git a/kernel/cpu_pm.c b/kernel/cpu_pm.c
index 246efc74e3f3..7c104446e1e9 100644
--- a/kernel/cpu_pm.c
+++ b/kernel/cpu_pm.c
@@ -26,6 +26,8 @@ static struct {
.lock = __RAW_SPIN_LOCK_UNLOCKED(cpu_pm_notifier.lock),
};

+static atomic_t cpus_in_pm;
+
static int cpu_pm_notify(enum cpu_pm_event event)
{
int ret;
@@ -116,7 +118,20 @@ EXPORT_SYMBOL_GPL(cpu_pm_unregister_notifier);
*/
int cpu_pm_enter(void)
{
- return cpu_pm_notify_robust(CPU_PM_ENTER, CPU_PM_ENTER_FAILED);
+ int ret;
+
+ ret = cpu_pm_notify_robust(CPU_PM_ENTER, CPU_PM_ENTER_FAILED);
+ if (ret)
+ return ret;
+
+ if (atomic_inc_return(&cpus_in_pm) == num_online_cpus()) {
+ ret = cpu_pm_notify_robust(CPU_LAST_PM_ENTER,
+ CPU_LAST_PM_ENTER_FAILED);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
}
EXPORT_SYMBOL_GPL(cpu_pm_enter);

@@ -134,7 +149,21 @@ EXPORT_SYMBOL_GPL(cpu_pm_enter);
*/
int cpu_pm_exit(void)
{
- return cpu_pm_notify(CPU_PM_EXIT);
+ int ret;
+
+ ret = cpu_pm_notify(CPU_PM_EXIT);
+ if (ret)
+ return ret;
+
+ if (atomic_read(&cpus_in_pm) == num_online_cpus()) {
+ ret = cpu_pm_notify(CPU_FIRST_PM_EXIT);
+ if (ret)
+ return ret;
+ }
+
+ atomic_dec(&cpus_in_pm);
+
+ return 0;
}
EXPORT_SYMBOL_GPL(cpu_pm_exit);

--
2.17.1