]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
pmdomain: Respect the CPU system wakeup QoS limit for s2idle
authorUlf Hansson <ulf.hansson@linaro.org>
Tue, 25 Nov 2025 11:26:43 +0000 (12:26 +0100)
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>
Tue, 25 Nov 2025 18:01:29 +0000 (19:01 +0100)
A CPU system wakeup QoS limit may have been requested by user space. To
avoid breaking this constraint when entering a low power state during
s2idle through genpd, let's extend the corresponding genpd governor for
CPUs. More precisely, during s2idle let the genpd governor select a
suitable domain idle state, by taking into account the QoS limit.

Reviewed-by: Dhruva Gole <d-gole@ti.com>
Reviewed-by: Kevin Hilman (TI) <khilman@baylibre.com>
Tested-by: Kevin Hilman (TI) <khilman@baylibre.com>
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
Link: https://patch.msgid.link/20251125112650.329269-3-ulf.hansson@linaro.org
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
drivers/pmdomain/core.c
drivers/pmdomain/governor.c
include/linux/pm_domain.h

index 61c2277c9ce39fcd2f7e77df549626e49a4d5310..4fd546ef044815a13977bc3630eadd8641784ce7 100644 (file)
@@ -1425,8 +1425,14 @@ static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock,
                        return;
        }
 
-       /* Choose the deepest state when suspending */
-       genpd->state_idx = genpd->state_count - 1;
+       if (genpd->gov && genpd->gov->system_power_down_ok) {
+               if (!genpd->gov->system_power_down_ok(&genpd->domain))
+                       return;
+       } else {
+               /* Default to the deepest state. */
+               genpd->state_idx = genpd->state_count - 1;
+       }
+
        if (_genpd_power_off(genpd, false)) {
                genpd->states[genpd->state_idx].rejected++;
                return;
index 39359811a93047b36443a1b9583962726f24b88b..bd1b9d66d4a5629386831560b83b0d310121c69b 100644 (file)
@@ -415,9 +415,36 @@ static bool cpu_power_down_ok(struct dev_pm_domain *pd)
        return false;
 }
 
+static bool cpu_system_power_down_ok(struct dev_pm_domain *pd)
+{
+       s64 constraint_ns = cpu_wakeup_latency_qos_limit() * NSEC_PER_USEC;
+       struct generic_pm_domain *genpd = pd_to_genpd(pd);
+       int state_idx = genpd->state_count - 1;
+
+       if (!(genpd->flags & GENPD_FLAG_CPU_DOMAIN)) {
+               genpd->state_idx = state_idx;
+               return true;
+       }
+
+       /* Find the deepest state for the latency constraint. */
+       while (state_idx >= 0) {
+               s64 latency_ns = genpd->states[state_idx].power_off_latency_ns +
+                                genpd->states[state_idx].power_on_latency_ns;
+
+               if (latency_ns <= constraint_ns) {
+                       genpd->state_idx = state_idx;
+                       return true;
+               }
+               state_idx--;
+       }
+
+       return false;
+}
+
 struct dev_power_governor pm_domain_cpu_gov = {
        .suspend_ok = default_suspend_ok,
        .power_down_ok = cpu_power_down_ok,
+       .system_power_down_ok = cpu_system_power_down_ok,
 };
 #endif
 
index f67a2cb7d7814859fd0657d44144a30d595bd6ed..93ba0143ca476afc8c801cd5c911084394718f10 100644 (file)
@@ -153,6 +153,7 @@ enum genpd_sync_state {
 };
 
 struct dev_power_governor {
+       bool (*system_power_down_ok)(struct dev_pm_domain *domain);
        bool (*power_down_ok)(struct dev_pm_domain *domain);
        bool (*suspend_ok)(struct device *dev);
 };