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