| From 621a88dbfe9006c318a0cafbd12e677ccfe006e7 Mon Sep 17 00:00:00 2001 |
| From: Daniel Lezcano <daniel.lezcano@linaro.org> |
| Date: Wed, 9 Jul 2025 17:47:28 +0200 |
| Subject: cpuidle: psci: Fix cpuhotplug routine with PREEMPT_RT=y |
| |
| From: Daniel Lezcano <daniel.lezcano@linaro.org> |
| |
| commit 621a88dbfe9006c318a0cafbd12e677ccfe006e7 upstream. |
| |
| Currently cpu hotplug with the PREEMPT_RT option set in the kernel is |
| not supported because the underlying generic power domain functions |
| used in the cpu hotplug callbacks are incompatible from a lock point |
| of view. This situation prevents the suspend to idle to reach the |
| deepest idle state for the "cluster" as identified in the |
| undermentioned commit. |
| |
| Use the compatible ones when PREEMPT_RT is enabled and remove the |
| boolean disabling the hotplug callbacks with this option. |
| |
| With this change the platform can reach the deepest idle state |
| allowing at suspend time to consume less power. |
| |
| Tested-on Lenovo T14s with the following script: |
| |
| echo 0 > /sys/devices/system/cpu/cpu3/online |
| BEFORE=$(cat /sys/kernel/debug/pm_genpd/power-domain-cpu-cluster0/idle_states | grep S0 | awk '{ print $3 }') ; |
| rtcwake -s 1 -m mem; |
| AFTER=$(cat /sys/kernel/debug/pm_genpd/power-domain-cpu-cluster0/idle_states | grep S0 | awk '{ print $3 }'); |
| if [ $BEFORE -lt $AFTER ]; then |
| echo "Test successful" |
| else |
| echo "Test failed" |
| fi |
| echo 1 > /sys/devices/system/cpu/cpu3/online |
| |
| Fixes: 1c4b2932bd62 ("cpuidle: psci: Enable the hierarchical topology for s2idle on PREEMPT_RT") |
| Cc: Raghavendra Kakarla <quic_rkakarla@quicinc.com> |
| Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org> |
| Reviewed-by: Sudeep Holla <sudeep.holla@arm.com> |
| Cc: stable@vger.kernel.org |
| Link: https://lore.kernel.org/r/20250709154728.733920-1-daniel.lezcano@linaro.org |
| Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| drivers/cpuidle/cpuidle-psci.c | 23 ++++++++++++----------- |
| 1 file changed, 12 insertions(+), 11 deletions(-) |
| |
| --- a/drivers/cpuidle/cpuidle-psci.c |
| +++ b/drivers/cpuidle/cpuidle-psci.c |
| @@ -39,7 +39,6 @@ struct psci_cpuidle_data { |
| static DEFINE_PER_CPU_READ_MOSTLY(struct psci_cpuidle_data, psci_cpuidle_data); |
| static DEFINE_PER_CPU(u32, domain_state); |
| static bool psci_cpuidle_use_syscore; |
| -static bool psci_cpuidle_use_cpuhp; |
| |
| void psci_set_domain_state(u32 state) |
| { |
| @@ -108,8 +107,12 @@ static int psci_idle_cpuhp_up(unsigned i |
| { |
| struct device *pd_dev = __this_cpu_read(psci_cpuidle_data.dev); |
| |
| - if (pd_dev) |
| - pm_runtime_get_sync(pd_dev); |
| + if (pd_dev) { |
| + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) |
| + pm_runtime_get_sync(pd_dev); |
| + else |
| + dev_pm_genpd_resume(pd_dev); |
| + } |
| |
| return 0; |
| } |
| @@ -119,7 +122,11 @@ static int psci_idle_cpuhp_down(unsigned |
| struct device *pd_dev = __this_cpu_read(psci_cpuidle_data.dev); |
| |
| if (pd_dev) { |
| - pm_runtime_put_sync(pd_dev); |
| + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) |
| + pm_runtime_put_sync(pd_dev); |
| + else |
| + dev_pm_genpd_suspend(pd_dev); |
| + |
| /* Clear domain state to start fresh at next online. */ |
| psci_set_domain_state(0); |
| } |
| @@ -180,9 +187,6 @@ static void psci_idle_init_cpuhp(void) |
| { |
| int err; |
| |
| - if (!psci_cpuidle_use_cpuhp) |
| - return; |
| - |
| err = cpuhp_setup_state_nocalls(CPUHP_AP_CPU_PM_STARTING, |
| "cpuidle/psci:online", |
| psci_idle_cpuhp_up, |
| @@ -243,10 +247,8 @@ static int psci_dt_cpu_init_topology(str |
| * s2ram and s2idle. |
| */ |
| drv->states[state_count - 1].enter_s2idle = psci_enter_s2idle_domain_idle_state; |
| - if (!IS_ENABLED(CONFIG_PREEMPT_RT)) { |
| + if (!IS_ENABLED(CONFIG_PREEMPT_RT)) |
| drv->states[state_count - 1].enter = psci_enter_domain_idle_state; |
| - psci_cpuidle_use_cpuhp = true; |
| - } |
| |
| return 0; |
| } |
| @@ -323,7 +325,6 @@ static void psci_cpu_deinit_idle(int cpu |
| |
| dt_idle_detach_cpu(data->dev); |
| psci_cpuidle_use_syscore = false; |
| - psci_cpuidle_use_cpuhp = false; |
| } |
| |
| static int psci_idle_init_cpu(struct device *dev, int cpu) |